Skip to contentSkip to conversation

counter: add GPIO-based quadrature encoder driver

PATCH Superseded Wadim Mueller 2 participants 6 messages Apr 16 – Apr 19

Subsystems (3)

v1 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

cover counter: add GPIO-based quadrature encoder driver

This series adds a new counter subsystem driver that implements quadrature encoder position tracking using plain GPIO pins with edge-triggered interrupts.

The driver is intended for low to medium speed rotary encoders where hardware counter peripherals (eQEP, FTM, etc.) are unavailable or already in use. It targets the same use-cases as interrupt-cnt.c but provides full quadrature decoding instead of simple pulse counting.

Features:
- X1, X2, X4 quadrature decoding and pulse-direction mode
- Optional index signal for zero-reset
- Configurable ceiling (position clamping)
- Standard counter subsystem sysfs + chrdev interface
- Enable/disable via sysfs with IRQ gating

Tested on TI AM64x (Cortex-A53) with a motor-driven rotary encoder at up to 2 kHz quadrature edge rate.

Wadim Mueller (3):
dt-bindings: counter: add gpio-quadrature-encoder binding
counter: add GPIO-based quadrature encoder driver
MAINTAINERS: add entry for GPIO quadrature encoder counter driver

 .../counter/gpio-quadrature-encoder.yaml      |  69 ++
 MAINTAINERS                                   |   7 +
 drivers/counter/Kconfig                       |  15 +
 drivers/counter/Makefile                      |   1 +
 drivers/counter/gpio-quadrature-encoder.c     | 710 ++++++++++++++++++
 5 files changed, 802 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
 create mode 100644 drivers/counter/gpio-quadrature-encoder.c
Signature · 1 line
2.52.0
#1 lore raw

PATCH 1/3 dt-bindings: counter: add gpio-quadrature-encoder binding

Add devicetree binding documentation for the GPIO-based quadrature encoder counter driver. The driver reads A/B quadrature signals and an optional index pulse via edge-triggered GPIO interrupts, supporting X1, X2, X4 quadrature decoding and pulse-direction mode.

This is useful on SoCs that lack a dedicated hardware quadrature decoder or where the encoder is wired to generic GPIO pins.

---
 .../counter/gpio-quadrature-encoder.yaml      | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +69 -0
new file mode 100644
index 000000000..a52deaab6
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/counter/gpio-quadrature-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO-based Quadrature Encoder
+
+maintainers:
+  - Wadim Mueller <[email protected]>
+
+description: |
+  A generic GPIO-based quadrature encoder counter.  Reads A/B quadrature
+  signals and an optional index pulse via edge-triggered GPIO interrupts.
+  Supports X1, X2, X4 quadrature decoding and pulse-direction mode.
+
+  This driver is useful on SoCs that lack a dedicated hardware quadrature
+  decoder (eQEP, QEI, etc.) or where the encoder is wired to generic GPIO
+  pins rather than to a dedicated peripheral.
+
+properties:
+  compatible:
+    const: gpio-quadrature-encoder
+
+  encoder-a-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's A (phase A) output.
+
+  encoder-b-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's B (phase B) output.
+
+  encoder-index-gpios:
+    maxItems: 1
+    description:
+      Optional GPIO connected to the encoder's index (Z) output.
+      When the index input is enabled via sysfs, the count resets
+      to zero on each index pulse.
+
+required:
+  - compatible
+  - encoder-a-gpios
+  - encoder-b-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    quadrature-encoder-0 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+    };
+
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    quadrature-encoder-1 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
+        encoder-index-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+    };
+
+...
Signature · 1 line
2.52.0
#2 lore raw

Re: [PATCH 1/3] dt-bindings: counter: add gpio-quadrature-encoder binding

Quoted 30 lines · Wadim Mueller

Add devicetree binding documentation for the GPIO-based quadrature encoder counter driver. The driver reads A/B quadrature signals and an optional index pulse via edge-triggered GPIO interrupts, supporting X1, X2, X4 quadrature decoding and pulse-direction mode.

This is useful on SoCs that lack a dedicated hardware quadrature decoder or where the encoder is wired to generic GPIO pins.

---
 .../counter/gpio-quadrature-encoder.yaml      | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +10 -0
new file mode 100644
index 000000000..a52deaab6
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/counter/gpio-quadrature-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO-based Quadrature Encoder
+
+maintainers:
+  - Wadim Mueller <[email protected]>

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 19

Quoted 10 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +9 -0
+
+description: |
+  A generic GPIO-based quadrature encoder counter.  Reads A/B quadrature
+  signals and an optional index pulse via edge-triggered GPIO interrupts.
+  Supports X1, X2, X4 quadrature decoding and pulse-direction mode.
+
 
+  This driver is useful on SoCs that lack a dedicated hardware quadrature
+  decoder (eQEP, QEI, etc.) or where the encoder is wired to generic GPIO
+  pins rather than to a dedicated peripheral.

Idea seems okay to me. Please rephrase this section to avoid talking about drivers...

Quoted 11 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +11 -0
+
+properties:
+  compatible:
+    const: gpio-quadrature-encoder
+
+  encoder-a-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's A (phase A) output.
+
+  encoder-b-gpios:

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 40

Quoted 10 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +10 -0
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's B (phase B) output.
+
+  encoder-index-gpios:
+    maxItems: 1
+    description:
+      Optional GPIO connected to the encoder's index (Z) output.
+      When the index input is enabled via sysfs, the count resets
+      to zero on each index pulse.

...and this to stop talking about sysfs and driver behaviour though. Bindings are about hardware.
pw-bot: changes-requested

Quoted 17 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +17 -0
+
+required:
+  - compatible
+  - encoder-a-gpios
+  - encoder-b-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    quadrature-encoder-0 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+    };

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 67

Quoted 10 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +10 -0
+
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    quadrature-encoder-1 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
+        encoder-index-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+    };

I think this example alone is sufficient btw.

Cheers,
Conor.

Quoted 5 lines

+
+...

Signature · 1 line
2.52.0
#3 lore raw

Re: [PATCH 1/3] dt-bindings: counter: add gpio-quadrature-encoder binding

On 2026-04-17 17:13, Conor Dooley wrote:

Thanks for the review

Quoted 38 lines · Wadim Mueller
Quoted 36 lines

Add devicetree binding documentation for the GPIO-based quadrature encoder counter driver. The driver reads A/B quadrature signals and an optional index pulse via edge-triggered GPIO interrupts, supporting X1, X2, X4 quadrature decoding and pulse-direction mode.

This is useful on SoCs that lack a dedicated hardware quadrature decoder or where the encoder is wired to generic GPIO pins.

---
 .../counter/gpio-quadrature-encoder.yaml      | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +16 -0
new file mode 100644
index 000000000..a52deaab6
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/counter/gpio-quadrature-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO-based Quadrature Encoder
+
+maintainers:
+  - Wadim Mueller <[email protected]>
+
+description: |
+  A generic GPIO-based quadrature encoder counter.  Reads A/B quadrature
+  signals and an optional index pulse via edge-triggered GPIO interrupts.
+  Supports X1, X2, X4 quadrature decoding and pulse-direction mode.
+
Quoted 6 lines

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 19

Quoted 3 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +3 -0
+  This driver is useful on SoCs that lack a dedicated hardware quadrature
+  decoder (eQEP, QEI, etc.) or where the encoder is wired to generic GPIO
+  pins rather than to a dedicated peripheral.

Idea seems okay to me. Please rephrase this section to avoid talking about drivers...

Thanks, will fix that in v2.

Quoted 18 lines
Quoted 18 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +17 -0
+
+properties:
+  compatible:
+    const: gpio-quadrature-encoder
+
+  encoder-a-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's A (phase A) output.
+
+  encoder-b-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the encoder's B (phase B) output.
+
+  encoder-index-gpios:
+    maxItems: 1
Quoted 9 lines

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 40

Quoted 4 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +4 -0
+    description:
+      Optional GPIO connected to the encoder's index (Z) output.
+      When the index input is enabled via sysfs, the count resets
+      to zero on each index pulse.

...and this to stop talking about sysfs and driver behaviour though. Bindings are about hardware.
pw-bot: changes-requested

Agreed, will rephrase to mention hardware signals only

Quoted 21 lines
Quoted 21 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +21 -0
+
+required:
+  - compatible
+  - encoder-a-gpios
+  - encoder-b-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    quadrature-encoder-0 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+    };
+
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
Quoted 9 lines

Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml line 67

Quoted 6 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +6 -0
+    quadrature-encoder-1 {
+        compatible = "gpio-quadrature-encoder";
+        encoder-a-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+        encoder-b-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
+        encoder-index-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+    };

I think this example alone is sufficient btw.

Ack, will drop the first example

Quoted 7 lines

Cheers,
Conor.

Quoted 5 lines
Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml new file +2 -0
+
+...
Signature · 1 line
2.52.0
#4 lore raw

PATCH 2/3 counter: add GPIO-based quadrature encoder driver

In reply to Wadim Mueller

Add a platform driver that turns ordinary GPIOs into a quadrature encoder counter device. The driver requests edge-triggered interrupts on the A and B (and optional Index) GPIOs and decodes the quadrature signal in software using a classic state-table approach.

Supported counting modes:
- Quadrature X1 (count on A rising edge only)
- Quadrature X2 (count on both A edges)
- Quadrature X4 (count on every A and B edge)
- Pulse-direction (A = pulse, B = direction)

An optional index signal resets the count to zero on its rising edge when enabled through sysfs. A configurable ceiling clamps the count to [0, ceiling].

---
 drivers/counter/Kconfig                   |  15 +
 drivers/counter/Makefile                  |   1 +
 drivers/counter/gpio-quadrature-encoder.c | 710 ++++++++++++++++++++++
 3 files changed, 726 insertions(+)
 create mode 100644 drivers/counter/gpio-quadrature-encoder.c
drivers/counter/Kconfig +15 -0
index d30d22dfe..72c5c8159 100644
@@ -68,6 +68,21 @@ config INTEL_QEP
 	  To compile this driver as a module, choose M here: the module
 	  will be called intel-qep.
 
+config GPIO_QUADRATURE_ENCODER
+	tristate "GPIO-based quadrature encoder counter driver"
+	depends on GPIOLIB
+	help
+	  Select this option to enable the GPIO-based quadrature encoder
+	  counter driver.  It reads A/B quadrature signals and an optional
+	  index pulse via edge-triggered GPIO interrupts, supporting X1, X2,
+	  X4 quadrature decoding and pulse-direction mode.
+
+	  This is useful on SoCs that lack a dedicated hardware quadrature
+	  decoder or where the encoder is wired to generic GPIO pins.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio-quadrature-encoder.
+
 config INTERRUPT_CNT
 	tristate "Interrupt counter driver"
 	depends on GPIOLIB
drivers/counter/Makefile +1 -0
index fa3c1d08f..2bef64d10 100644
@@ -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_QUADRATURE_ENCODER)	+= gpio-quadrature-encoder.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-quadrature-encoder.c new file +710 -0

710 lines not on this page · raw message

Signature · 1 line
2.52.0
#5 lore raw

PATCH 3/3 MAINTAINERS: add entry for GPIO quadrature encoder counter driver

In reply to Wadim Mueller

Add myself as maintainer for the new gpio-quadrature-encoder counter driver and its devicetree binding.

---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index 06a8c7457..fca62baa7 100644
@@ -11018,6 +11018,13 @@ F:	Documentation/dev-tools/gpio-sloppy-logic-analyzer.rst
 F:	drivers/gpio/gpio-sloppy-logic-analyzer.c
 F:	tools/gpio/gpio-sloppy-logic-analyzer.sh
 
+GPIO QUADRATURE ENCODER COUNTER DRIVER
+M:	Wadim Mueller <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/counter/gpio-quadrature-encoder.yaml
+F:	drivers/counter/gpio-quadrature-encoder.c
+
 GPIO SUBSYSTEM
 M:	Linus Walleij <[email protected]>
 M:	Bartosz Golaszewski <[email protected]>
Signature · 1 line
2.52.0