counter: add GPIO-based counter driver
Subsystems (3)
v7 cover counter: add GPIO-based counter driver
This series adds a counter subsystem driver that does quadrature encoder position tracking with plain GPIO pins and edge interrupts. Compared to interrupt-cnt.c (pulse-only) it provides full A/B/Index decoding and exposes the counter sysfs ABI. Target hardware is low- to medium-speed rotary encoders on SoCs without a free eQEP / FTM / etc. Benchmark rig: github.com/wafgo/qenc-bench.
v7 addresses Williams review comments on v6 [1]. Main change is the reworked index GPIO handling, rest is smaller stuff.
Tested in quadrature x4 mode (both directions) on a AM642 board with a real encoder on GPIOs.
== Changes in v7 ==
Driver:
- check the enabled state in the ISRs before reading the GPIOs. This also moves the reads under the spinlock, so concurrent A/B ISRs cant evaluate the state out of order anymore.
- dropped the spinlock from the simple single-field read callbacks
- smaller cleanups from the review (b_irq_users increment split from the zero check, current_state variable gone, ternary in index_pulse, GPIO_COUNTER_NUM_COUNTS_MAX rename, GPIO_COUNTER_MAX_INDEX removed)
- index GPIOs now live per Count (idx_gpio/idx_irq/has_index in gpio_counter_count_priv), the n_idx based arrays are gone
- enable_write: dont resample prev_a/prev_b when Count 2 gets enabled, that would corrupt the quadrature state of an already running Count 1. Also sync the direction from the current B level on enable in pulse-direction mode, otherwise the counter steps in the wrong direction until the next B edge if B moved while disabled (both reported by the Sashiko review on v6).
Regarding the IRQ enable ordering question from the review: I kept setting cp->enabled = true before enable_irq(). The prev sampling and the enabled flag are done atomic under the lock, so the first edge after enable_irq() sees consistent state. With the other order there is a window where an edge fires and gets droped.
Binding:
- index-gpios (array, position selects the Count) is replaced by two seperate properties index1-gpios and index2-gpios, matching the signal-a-gpios/signal-b-gpios naming. So Count 2 can have an index line without a dummy entry for Count 1.
- dropped Conors Ack [2] because of the binding change, a fresh one would be appreciated
The 2D synapse array preparation for a future pulse-direction mode on Count 2 is not part of this revision, that was flagged as follow-up material.
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/20260629-trekker-estranged-754e9bfada16@spud/
Wadim Mueller (3):
dt-bindings: counter: add gpio-counter binding
counter: add GPIO-based counter driver
MAINTAINERS: add entry for GPIO counter driver
.../bindings/counter/gpio-counter.yaml | 60 + MAINTAINERS | 7 + drivers/counter/Kconfig | 17 + drivers/counter/Makefile | 1 + drivers/counter/gpio-counter.c | 1011 +++++++++++++++++ 5 files changed, 1096 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/gpio-counter.yaml create mode 100644 drivers/counter/gpio-counter.c
base-commit: 3cd8b194bf3428dfa53120fee47e827a7c495815
Signature · 1 line
2.43.0
PATCH v7 1/3 dt-bindings: counter: add gpio-counter binding
Add a binding for a generic GPIO-based counter. Two GPIOs (signal-a, signal-b) drive two Counts; optional index GPIOs load a preset. The counter function (quadrature, pulse-direction, increase/decrease) is selected at runtime via the counter sysfs interface.
---
v7:
- index-gpios split into seperate index1-gpios/index2-gpios, so Count 2 can have an index line without a dummy entry for Count 1
- dropped Conors Ack since the binding changed
--- .../bindings/counter/gpio-counter.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/gpio-counter.yaml
Documentation/devicetree/bindings/counter/gpio-counter.yaml new file +60 -0
@@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/gpio-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO-based Counter + +maintainers: + - Wadim Mueller <[email protected]> + +description: | + GPIO-based software counter with two Counts: + * Count 1: A + B. Quadrature X1/X2/X4, pulse-direction, inc/dec. + * Count 2: B. Inc / dec. + The function is selected at runtime via the counter sysfs ABI. Each + Count has an optional Index (Z) that loads its preset on rising edge. + +properties: + compatible: + const: gpio-counter + + signal-a-gpios: + maxItems: 1 + description: Signal A (phase A; pulse input in pulse-direction/inc/dec). + + signal-b-gpios: + maxItems: 1 + description: + Signal B (phase B; direction in pulse-direction; pulse input for + Count 2). + + index1-gpios: + maxItems: 1 + description: Optional Index (Z) input for Count 1. + + index2-gpios: + maxItems: 1 + description: Optional Index (Z) input for Count 2. + +required: + - compatible + - signal-a-gpios + - signal-b-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + counter { + compatible = "gpio-counter"; + signal-a-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; + signal-b-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; + index1-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + index2-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + }; + +...
Signature · 1 line
2.43.0
Rob Herring
maintainerRe: [PATCH v7 1/3] dt-bindings: counter: add gpio-counter binding
Quoted 28 lines · Wadim Mueller
Add a binding for a generic GPIO-based counter. Two GPIOs (signal-a, signal-b) drive two Counts; optional index GPIOs load a preset. The counter function (quadrature, pulse-direction, increase/decrease) is selected at runtime via the counter sysfs interface.
Signed-off-by Wadim Mueller <[email protected]>---
v7:
- index-gpios split into seperate index1-gpios/index2-gpios, so Count 2 can have an index line without a dummy entry for Count 1
- dropped Conors Ack since the binding changed--- .../bindings/counter/gpio-counter.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/gpio-counter.yamlDocumentation/devicetree/bindings/counter/gpio-counter.yaml new file +6
-0@@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/gpio-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# +
Documentation/devicetree/bindings/counter/gpio-counter.yaml line 16
Quoted 10 lines
Documentation/devicetree/bindings/counter/gpio-counter.yaml new file +10
-0+title: GPIO-based Counter + +maintainers: + - Wadim Mueller <[email protected]> + +description: | + GPIO-based software counter with two Counts: + * Count 1: A + B. Quadrature X1/X2/X4, pulse-direction, inc/dec. + * Count 2: B. Inc / dec. + The function is selected at runtime via the counter sysfs ABI. Each
sysfs is not relevant to bindings.
Documentation/devicetree/bindings/counter/gpio-counter.yaml line 17
Quoted 1 line
Documentation/devicetree/bindings/counter/gpio-counter.yaml new file +1
-0+ Count has an optional Index (Z) that loads its preset on rising edge.
I don't have any clue what this h/w is based on *this* patch. Rotary encoder was mentioned elsewhere, but this patch needs to stand on its own. We already have rotary encoder and GPIO decoder bindings. How is this h/w different? The patch needs to answer that.
Rob
PATCH v7 2/3 counter: add GPIO-based counter driver
In reply to Wadim MuellerAdd a platform driver that turns plain GPIOs into a counter device. Edge interrupts on signal-a, signal-b (and optional index) are decoded in software. Quadrature steps are validated with a 2-bit Gray-code parity check; direction comes from the same identity.
The driver exposes two Counts on the same wires:
"Count 1" - Synapses on A, B and optional Index 1. Supports
QUADRATURE_X1_{A,B} / X2_{A,B} / X4, PULSE_DIRECTION, INCREASE and DECREASE.
"Count 2" - Synapse on B and optional Index 2. Supports INCREASE
and DECREASE only.
Per-Count state (value, ceiling, preset, enable, function, direction) lives in struct gpio_counter_count_priv so new per-Count features (e.g. floor) can be added without touching parallel arrays.
Sleepable GPIO providers (I2C/SPI expanders) are rejected at probe since the ISRs run in hardirq context.
---
v7:
- enabled check before the GPIO reads in the ISRs (this also moves the reads under the lock)
- no spinlock in the simple single-field read callbacks anymore
- b_irq_users increment/decrement split from the zero check, current_state variable removed, ternary in index_pulse, GPIO_COUNTER_NUM_COUNTS_MAX rename, GPIO_COUNTER_MAX_INDEX gone
- index GPIOs tracked per Count now (idx_gpio/idx_irq/has_index), matches the index1-gpios/index2-gpios split in patch 1
- enable_write: dont resample prev_a/prev_b on Count 2 enable and sync the direction from B on enable in pulse-direction mode
--- drivers/counter/Kconfig | 17 + drivers/counter/Makefile | 1 + drivers/counter/gpio-counter.c | 1011 ++++++++++++++++++++++++++++++++ 3 files changed, 1029 insertions(+) create mode 100644 drivers/counter/gpio-counter.c
drivers/counter/Kconfig +17 -0
@@ -68,6 +68,23 @@ config INTEL_QEP To compile this driver as a module, choose M here: the module will be called intel-qep. +config GPIO_COUNTER + tristate "GPIO-based counter driver" + depends on GPIOLIB + help + Select this option to enable the GPIO-based counter driver. It + reads A/B and an optional index signal via edge-triggered GPIO + interrupts and decodes them according to the selected mode: + Quadrature X1/X2/X4 (rotary or linear encoders), pulse-direction, + and pure increase / decrease pulse counters. + + This is useful on SoCs that lack a dedicated hardware quadrature + decoder or pulse counter, or where the signals are wired to + generic GPIO pins rather than to a dedicated peripheral. + + To compile this driver as a module, choose M here: the + module will be called gpio-counter. + config INTERRUPT_CNT tristate "Interrupt counter driver" depends on GPIOLIB
drivers/counter/Makefile +1 -0
@@ -14,6 +14,7 @@ obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o +obj-$(CONFIG_GPIO_COUNTER) += gpio-counter.o obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o obj-$(CONFIG_INTEL_QEP) += intel-qep.o obj-$(CONFIG_TI_ECAP_CAPTURE) += ti-ecap-capture.o
drivers/counter/gpio-counter.c new file +1011 -0
1,011 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] Sparse initialization of the `signals` array causes a NULL pointer dereference and missed…
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Sparse initialization of the signals array causes a NULL pointer dereference and missed registrations if index1 is omitted but index2 is present.
- [High] Counter values incorrectly saturate at 0 and ceiling instead of wrapping around, permanently breaking position tracking for quadrature encoders.
- [Medium] Torn reads of 64-bit state variables (value, preset, ceiling) on 32-bit architectures due to lockless access.
- [Medium] Missing cp->enabled check in index pulse ISR allows disabled counters to be modified and generate events.
--
--- Patch [2]: [PATCH v7 2/3] counter: add GPIO-based counter driver --- commit 71f827659851f1023e187d6b98a7113441670997
Author: Wadim Mueller <[email protected]>
counter: add GPIO-based counter driver
Add a platform driver that turns plain GPIOs into a counter device supporting quadrature, pulse-direction, and increase/decrease modes.
Quoted 5 lines
drivers/counter/gpio-counter.c new file +0
-0
[ ... ]
drivers/counter/gpio-counter.c line 101
Quoted 11 lines
drivers/counter/gpio-counter.c new file +11
-0+/* priv->lock held. */ +static void gpio_counter_value_step (struct gpio_counter_count_priv *cp) +{ +if (cp->direction ==COUNTER_COUNT_DIRECTION_FORWARD ) { +if (cp->value < cp->ceiling) + cp->value++; + }else { +if (cp->value >0 ) + cp->value--; + } +}
[Severity: High]
Will this clamp the counter instead of letting it wrap around?
If the hardware oscillates or moves backward past 0, the counter will stick at 0. Moving forward again will increment from 0, which could destroy the absolute position mapping for quadrature encoders. Should this wrap around modulo the ceiling instead?
[ ... ]
Quoted 4 lines
drivers/counter/gpio-counter.c new file +4
-0+static void gpio_counter_index_pulse (struct counter_device *counter, +enum gpio_counter_count_id count_id) +{ +struct gpio_counter_priv *priv =counter_priv (counter);
drivers/counter/gpio-counter.c line 257
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+struct gpio_counter_count_priv *cp = &priv->count_priv[count_id]; +unsigned long flags; + +spin_lock_irqsave (&priv->lock, flags); +if (cp->preset_enabled) + cp->value = (cp->preset > cp->ceiling) ? cp->ceiling : cp->preset; +spin_unlock_irqrestore (&priv->lock, flags); + +counter_push_event (counter,COUNTER_EVENT_INDEX , count_id); +}
[Severity: Medium]
Is a check for cp->enabled needed here?
When disabling a counter in gpio_counter_enable_write(), cp->enabled is set to false and the spinlock is released before disable_irq() is called. If an index edge fires in this narrow window, it looks like this could update cp->value and push a COUNTER_EVENT_INDEX event despite the driver state being logically disabled.
[ ... ]
drivers/counter/gpio-counter.c line 280
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+static int gpio_counter_count_read (struct counter_device *counter, +struct counter_count *count,u64 *val) +{ +struct gpio_counter_priv *priv =counter_priv (counter); +struct gpio_counter_count_priv *cp = &priv->count_priv[count->id]; + + *val = cp->value; + +return 0 ; +}
[Severity: Medium]
Can this cause torn reads on 32-bit architectures?
Since cp->value is a 64-bit variable, reading it locklessly on a 32-bit architecture requires two 32-bit loads. If the hardware ISR interrupts these loads to increment the value across a 32-bit boundary, the reader might fuse the old and new halves together resulting in a large, spurious jump in the reported value.
This also seems to apply to the lockless reads of cp->ceiling and cp->preset later in this file.
[ ... ]
Quoted 11 lines
drivers/counter/gpio-counter.c new file +11
-0+static int gpio_counter_setup_signals (struct gpio_counter_priv *priv) +{ + priv->signals[GPIO_COUNTER_SIGNAL_A ].id =GPIO_COUNTER_SIGNAL_A ; + priv->signals[GPIO_COUNTER_SIGNAL_A ].name ="Signal A" ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].id =GPIO_COUNTER_SIGNAL_B ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].name ="Signal B" ; + +if (priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].id = +GPIO_COUNTER_SIGNAL_INDEX1 ; + priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].name ="Index 1" ;
drivers/counter/gpio-counter.c line 768
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ } +if (priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].id = +GPIO_COUNTER_SIGNAL_INDEX2 ; + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].name ="Index 2" ; + } + +return 2 + priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index + + priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index; +}
[Severity: High]
Does this setup handle the case where a device tree omits index1 but includes index2?
If index1 is missing but index2 is present, the index 2 signal is mapped to priv->signals[3] (GPIO_COUNTER_SIGNAL_INDEX2), leaving priv->signals[2] zero-initialized.
Since this function returns a compressed count of 3, the counter framework will iterate from index 0 to 2, causing a NULL pointer dereference on the empty name at signals[2] and skipping the valid signal at signals[3].
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Hello,
drivers/counter/gpio-counter.c line 21
Quoted 3 lines · Wadim Mueller
drivers/counter/gpio-counter.c new file +2
-0+#include <linux/mod_devicetable.h> +#include <linux/platform_device.h>
Please don't use <linux/mod_devicetable.h>. of_device_id is also provided by <linux/platform_device.h>, so you can just drop the former include.
drivers/counter/gpio-counter.c line 994
Quoted 3 lines
drivers/counter/gpio-counter.c new file +3
-0+static const struct of_device_id gpio_counter_of_match[] = { + { .compatible ="gpio-counter" }, + {}
{ } please.
drivers/counter/gpio-counter.c line 996
Quoted 2 lines
drivers/counter/gpio-counter.c new file +2
-0+}; +MODULE_DEVICE_TABLE (of, gpio_counter_of_match);
Best regards
Uwe
William Breathitt Gray
maintainerQuoted 13 lines · Wadim Mueller
Add a platform driver that turns plain GPIOs into a counter device. Edge interrupts on signal-a, signal-b (and optional index) are decoded in software. Quadrature steps are validated with a 2-bit Gray-code parity check; direction comes from the same identity.
The driver exposes two Counts on the same wires:
"Count 1" - Synapses on A, B and optional Index 1. Supports
QUADRATURE_X1_{A,B} / X2_{A,B} / X4,PULSE_DIRECTION, INCREASE and DECREASE.
"Count 2" - Synapse on B and optional Index 2. Supports INCREASE
and DECREASE only.
Quoted 8 lines
Per-Count state (value, ceiling, preset, enable, function, direction) lives in struct
gpio_counter_count_privso new per-Count features (e.g. floor) can be added without touching parallel arrays.Sleepable GPIO providers (I2C/SPI expanders) are rejected at probe since the ISRs run in hardirq context.
Signed-off-by Wadim Mueller <[email protected]>
Hi Wadim,
The sashiko-bot picked up an issue I hadn't considered in the previous
review: 64-bit reads may be implemented as two 32-bit read operations on 32-bit systems resulting in a race condition. So it seems that we do need to acquire priv->lock to protect access to u64 count_priv members even when we simply read. As such, please add back the spinlock calls from the previous version for the relevant u64 members (value, ceiling, and preset).
A few more comments below.
Quoted 16 lines
drivers/counter/gpio-counter.c new file +16
-0+static void gpio_counter_index_pulse (struct counter_device *counter, +enum gpio_counter_count_id count_id) +{ +struct gpio_counter_priv *priv =counter_priv (counter); +struct gpio_counter_count_priv *cp = &priv->count_priv[count_id]; +unsigned long flags; + +spin_lock_irqsave (&priv->lock, flags); +if (cp->preset_enabled) + cp->value = (cp->preset > cp->ceiling) ? cp->ceiling : cp->preset; +spin_unlock_irqrestore (&priv->lock, flags); + +counter_push_event (counter,COUNTER_EVENT_INDEX , count_id); +} + +static irqreturn_t gpio_counter_index1_isr (int irq,void *dev_id)
drivers/counter/gpio-counter.c line 269
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+{ +gpio_counter_index_pulse (dev_id,GPIO_COUNTER_COUNT_1 ); +return IRQ_HANDLED ; +} + +static irqreturn_t gpio_counter_index2_isr (int irq,void *dev_id) +{ +gpio_counter_index_pulse (dev_id,GPIO_COUNTER_COUNT_2 ); +return IRQ_HANDLED ; +}
The count_priv array holds the respective IRQ value in the idx_irq member, so you can loop through the array to find the particular count_priv element. We have only two Counts, so a simple if-else will suffice. That allows you to avoid the two separate Index ISR and instead repurpose the code in gpio_counter_index_pulse() as the sole Index ISR. Maybe something like this:
static irqreturn_t gpio_counter_index_isr(int irq, void *private)
{
struct counter_device *counter = private;
struct gpio_counter_priv *priv = counter_priv(counter);
struct gpio_counter_count_priv *cp = &priv->count_priv[0];
unsigned long flags;
if (cp->idx_irq != irq);
cp = &priv->count_priv[1];
spin_lock_irqsave(&priv->lock, flags);
if (!cp->enabled) {
spin_unlock_irqrestore(&priv->lock, flags);
return IRQ_HANDLED;
}
if (cp->preset_enabled)
cp->value = (cp->preset > cp->ceiling) ? cp->ceiling : cp->preset;
spin_unlock_irqrestore(&priv->lock, flags);
counter_push_event(counter, COUNTER_EVENT_INDEX, count_id);
return IRQ_HANDLED;
}Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+static int gpio_counter_setup_signals (struct gpio_counter_priv *priv) +{ + priv->signals[GPIO_COUNTER_SIGNAL_A ].id =GPIO_COUNTER_SIGNAL_A ; + priv->signals[GPIO_COUNTER_SIGNAL_A ].name ="Signal A" ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].id =GPIO_COUNTER_SIGNAL_B ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].name ="Signal B" ; + +if (priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].id = +GPIO_COUNTER_SIGNAL_INDEX1 ;
drivers/counter/gpio-counter.c line 767
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].name ="Index 1" ; + } +if (priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].id = +GPIO_COUNTER_SIGNAL_INDEX2 ; + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].name ="Index 2" ; + } + +return 2 + priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index + + priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index;
As sashiko-bot pointed out, the Counter subsystem doesn't handle gaps in the signals array. What you can try instead is use a local variable num_signals to hold the current signals array offset:
num_signals = 2;
if (priv->count_priv[GPIO_COUNTER_COUNT_1].has_index) {
priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX1;
priv->signals[i].name = "Index 1";
i++;
}
if (priv->count_priv[GPIO_COUNTER_COUNT_2].has_index) {
priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX2;
priv->signals[i].name = "Index 2";
i++;
}
return i;Now, to refer back to the correct signals element later you have three
options: loop through the signals array and check each id, or save the signals offset to a member of count_priv so you can refer to it later.
I think the first option is the simplest because there are only two Index Signals and a single if statement suffices: if priv->signals[2].id is not GPIO_COUNTER_SIGNAL_INDEX1, then it is GPIO_COUNTER_SIGNAL_INDEX2 (and vice versa).
Quoted 13 lines
drivers/counter/gpio-counter.c new file +13
-0+} + +static int gpio_counter_setup_synapses (struct gpio_counter_priv *priv) +{ +int n_c1 =2 , n_c2 =1 ; + + priv->synapses_count1[0 ].actions_list = gpio_counter_synapse_actions; + priv->synapses_count1[0 ].num_actions = +ARRAY_SIZE (gpio_counter_synapse_actions); + priv->synapses_count1[0 ].signal = &priv->signals[GPIO_COUNTER_SIGNAL_A ]; + + priv->synapses_count1[1 ].actions_list = gpio_counter_synapse_actions; + priv->synapses_count1[1].num_actions =
drivers/counter/gpio-counter.c line 790
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ARRAY_SIZE (gpio_counter_synapse_actions); + priv->synapses_count1[1 ].signal = &priv->signals[GPIO_COUNTER_SIGNAL_B ]; + +if (priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index) { + priv->synapses_count1[2 ].actions_list = + gpio_counter_index_synapse_actions; + priv->synapses_count1[2 ].num_actions = +ARRAY_SIZE (gpio_counter_index_synapse_actions); + priv->synapses_count1[2 ].signal = + &priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ];
You can use a ternary directly like this:
priv->synapses_count1[2].signal = (priv->signals[2].id == GPIO_COUNTER_SIGNAL_INDEX1) ? &priv->signals[2] : &priv->signals[3];
Or wrap it in a function to abstract it with a name like gpio_counter_get_index_signal() or similar.
Quoted 5 lines
drivers/counter/gpio-counter.c new file +5
-0+ n_c1 =3 ; + } + + priv->synapses_count2[0 ].actions_list = gpio_counter_synapse_actions; + priv->synapses_count2[0].num_actions =
drivers/counter/gpio-counter.c line 805
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ARRAY_SIZE (gpio_counter_synapse_actions); + priv->synapses_count2[0 ].signal = &priv->signals[GPIO_COUNTER_SIGNAL_B ]; + +if (priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index) { + priv->synapses_count2[1 ].actions_list = + gpio_counter_index_synapse_actions; + priv->synapses_count2[1 ].num_actions = +ARRAY_SIZE (gpio_counter_index_synapse_actions); + priv->synapses_count2[1 ].signal = + &priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ];
Similar comment as above.
William Breathitt Gray
William Breathitt Gray
maintainerQuoted 26 lines · William Breathitt Gray
Quoted 10 lines · Wadim Mueller
drivers/counter/gpio-counter.c new file +10
-0+static int gpio_counter_setup_signals (struct gpio_counter_priv *priv) +{ + priv->signals[GPIO_COUNTER_SIGNAL_A ].id =GPIO_COUNTER_SIGNAL_A ; + priv->signals[GPIO_COUNTER_SIGNAL_A ].name ="Signal A" ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].id =GPIO_COUNTER_SIGNAL_B ; + priv->signals[GPIO_COUNTER_SIGNAL_B ].name ="Signal B" ; + +if (priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].id = +GPIO_COUNTER_SIGNAL_INDEX1 ;drivers/counter/gpio-counter.c line 767
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ].name ="Index 1" ; + } +if (priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index) { + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].id = +GPIO_COUNTER_SIGNAL_INDEX2 ; + priv->signals[GPIO_COUNTER_SIGNAL_INDEX2 ].name ="Index 2" ; + } + +return 2 + priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index + + priv->count_priv[GPIO_COUNTER_COUNT_2 ].has_index;As sashiko-bot pointed out, the Counter subsystem doesn't handle gaps in the signals array. What you can try instead is use a local variable
num_signalsto hold the current signals array offset:
Quoted 13 lines
num_signals = 2;
if (priv->count_priv[GPIO_COUNTER_COUNT_1].has_index) {
priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX1;
priv->signals[i].name = "Index 1";
i++;
}
if (priv->count_priv[GPIO_COUNTER_COUNT_2].has_index) {
priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX2;
priv->signals[i].name = "Index 2";
i++;
}
return i;Oops, I intended for those 'i' to be 'num_signals'. However, if you like you may name it 'i' or whatever makes sense.
Quoted 36 lines
Now, to refer back to the correct signals element later you have three
options: loop through the signals array and check each id, or save the signals offset to a member ofcount_privso you can refer to it later.I think the first option is the simplest because there are only two Index Signals and a single if statement suffices: if priv->signals[2].id is not
GPIO_COUNTER_SIGNAL_INDEX1, then it isGPIO_COUNTER_SIGNAL_INDEX2(and vice versa).Quoted 13 lines
drivers/counter/gpio-counter.c new file +13
-0+} + +static int gpio_counter_setup_synapses (struct gpio_counter_priv *priv) +{ +int n_c1 =2 , n_c2 =1 ; + + priv->synapses_count1[0 ].actions_list = gpio_counter_synapse_actions; + priv->synapses_count1[0 ].num_actions = +ARRAY_SIZE (gpio_counter_synapse_actions); + priv->synapses_count1[0 ].signal = &priv->signals[GPIO_COUNTER_SIGNAL_A ]; + + priv->synapses_count1[1 ].actions_list = gpio_counter_synapse_actions; + priv->synapses_count1[1].num_actions =drivers/counter/gpio-counter.c line 790
Quoted 10 lines
drivers/counter/gpio-counter.c new file +10
-0+ARRAY_SIZE (gpio_counter_synapse_actions); + priv->synapses_count1[1 ].signal = &priv->signals[GPIO_COUNTER_SIGNAL_B ]; + +if (priv->count_priv[GPIO_COUNTER_COUNT_1 ].has_index) { + priv->synapses_count1[2 ].actions_list = + gpio_counter_index_synapse_actions; + priv->synapses_count1[2 ].num_actions = +ARRAY_SIZE (gpio_counter_index_synapse_actions); + priv->synapses_count1[2 ].signal = + &priv->signals[GPIO_COUNTER_SIGNAL_INDEX1 ];You can use a ternary directly like this:
Quoted 2 lines
priv->synapses_count1[2].signal = (priv->signals[2].id == GPIO_COUNTER_SIGNAL_INDEX1) ? &priv->signals[2] : &priv->signals[3];
I suppose index1 would always be at offset 2 if it does exist, so this ternary isn't needed here but rather better for the index2 case in the count2 block.
William Breathitt Gray
PATCH v7 3/3 MAINTAINERS: add entry for GPIO counter driver
In reply to Wadim MuellerCover the gpio-counter driver and its device-tree binding.
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
@@ -10984,6 +10984,13 @@ S: Supported F: Documentation/admin-guide/gpio/gpio-aggregator.rst F: drivers/gpio/gpio-aggregator.c +GPIO COUNTER DRIVER +M: Wadim Mueller <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/counter/gpio-counter.yaml +F: drivers/counter/gpio-counter.c + GPIO IR Transmitter M: Sean Young <[email protected]> L: [email protected]
Signature · 1 line
2.43.0