This is an automated email from the ASF dual-hosted git repository.

linguini pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 1513b3e20c2387732428d529b9d9046d163e608f
Author: raiden00pl <[email protected]>
AuthorDate: Sat Apr 11 12:30:33 2026 +0200

    arch/nrf52|nrf53: add qencoder support
    
    add qencoder support for nrf52 and nrf53
    
    Signed-off-by: raiden00pl <[email protected]>
---
 Documentation/platforms/arm/nrf52/index.rst |   8 +-
 Documentation/platforms/arm/nrf53/index.rst |   8 +-
 arch/arm/src/nrf52/CMakeLists.txt           |   4 +
 arch/arm/src/nrf52/Kconfig                  |  21 ++
 arch/arm/src/nrf52/Make.defs                |   4 +
 arch/arm/src/nrf52/hardware/nrf52_qdec.h    | 242 +++++++++++++++
 arch/arm/src/nrf52/nrf52_qdec.c             | 445 ++++++++++++++++++++++++++
 arch/arm/src/nrf52/nrf52_qdec.h             |  52 ++++
 arch/arm/src/nrf53/CMakeLists.txt           |   8 +
 arch/arm/src/nrf53/Kconfig                  |  28 ++
 arch/arm/src/nrf53/Make.defs                |   4 +
 arch/arm/src/nrf53/hardware/nrf53_qdec.h    | 215 +++++++++++++
 arch/arm/src/nrf53/nrf53_qdec.c             | 464 ++++++++++++++++++++++++++++
 arch/arm/src/nrf53/nrf53_qdec.h             |  52 ++++
 14 files changed, 1553 insertions(+), 2 deletions(-)

diff --git a/Documentation/platforms/arm/nrf52/index.rst 
b/Documentation/platforms/arm/nrf52/index.rst
index 5d4bb0144c6..f2fd75315a6 100644
--- a/Documentation/platforms/arm/nrf52/index.rst
+++ b/Documentation/platforms/arm/nrf52/index.rst
@@ -62,7 +62,7 @@ NFCT        No
 PDM         No
 PPI         Yes
 PWM         Yes
-QDEC        No
+QDEC        Yes     Quadrature encoder
 QSPI        Yes
 RADIO       Yes     BLE, IEEE 802.15.4
 RNG         Yes
@@ -158,6 +158,12 @@ PWM
 PWM is supported via standard driver. This means that more advanced features 
such as
 complex sequences or waveform modes are not yet supported.
 
+QDEC
+----
+
+The QDEC (Quadrature Decoder) peripheral is supported via the standard 
qencoder driver
+interface. The LED output is not supported now.
+
 QSPI
 ----
 
diff --git a/Documentation/platforms/arm/nrf53/index.rst 
b/Documentation/platforms/arm/nrf53/index.rst
index 50ae98fee05..4ea834de622 100644
--- a/Documentation/platforms/arm/nrf53/index.rst
+++ b/Documentation/platforms/arm/nrf53/index.rst
@@ -21,7 +21,7 @@ NFCT        No
 PDM         No
 DPPI        No
 PWM         Yes
-QDEC        No
+QDEC        Yes     Quadrature encoder
 QSPI        Yes
 RADIO       No
 RNG         No
@@ -102,6 +102,12 @@ PWM
 PWM is supported via standard driver. This means that more advanced features 
such as
 complex sequences or waveform modes are not yet supported.
 
+QDEC
+----
+
+The QDEC (Quadrature Decoder) peripheral is supported via the standard 
qencoder driver
+interface. The LED output is not supported now.
+
 QSPI
 ----
 
diff --git a/arch/arm/src/nrf52/CMakeLists.txt 
b/arch/arm/src/nrf52/CMakeLists.txt
index 66858f547ee..4fcf2e6fb81 100644
--- a/arch/arm/src/nrf52/CMakeLists.txt
+++ b/arch/arm/src/nrf52/CMakeLists.txt
@@ -132,6 +132,10 @@ if(CONFIG_NRF52_SAADC)
   list(APPEND SRCS nrf52_adc.c)
 endif()
 
+if(CONFIG_NRF52_QDEC)
+  list(APPEND SRCS nrf52_qdec.c)
+endif()
+
 if(CONFIG_PM)
   list(APPEND SRCS nrf52_pminitialize.c)
 endif()
diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig
index eb2d0847bdb..3c6ac405212 100644
--- a/arch/arm/src/nrf52/Kconfig
+++ b/arch/arm/src/nrf52/Kconfig
@@ -170,6 +170,10 @@ config NRF52_RTC
        bool
        default n
 
+config NRF52_QDEC
+       bool
+       default n
+
 menu "nRF52 Peripheral Selection"
 
 config NRF52_I2C0_MASTER
@@ -803,6 +807,23 @@ menu "USBDEV Configuration"
 
 endmenu # USBDEV Configuration
 
+config NRF52_QDEC0
+       bool "Qencoder (QDEC) support"
+       default n
+       select NRF52_QDEC
+       ---help---
+               Enable Qencoder support.
+
+config NRF52_QENCODER_INDEX
+       bool "Enable QDEC index pin support"
+       default n
+       depends on NRF52_GPIOTE && NRF52_QDEC
+       ---help---
+               Enable index/Z channel support for quadrature encoder.
+               Uses a GPIO interrupt to detect index pulses and reset
+               position to configured index value. Requires one GPIOTE
+               channel for interrupt detection.
+
 menuconfig NRF52_SOFTDEVICE_CONTROLLER
        bool "SoftDevice Controller"
        depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs
index 10118d1753d..a8eb228fdc6 100644
--- a/arch/arm/src/nrf52/Make.defs
+++ b/arch/arm/src/nrf52/Make.defs
@@ -123,6 +123,10 @@ ifeq ($(CONFIG_NRF52_SAADC),y)
 CHIP_CSRCS += nrf52_adc.c
 endif
 
+ifeq ($(CONFIG_NRF52_QDEC),y)
+CHIP_CSRCS += nrf52_qdec.c
+endif
+
 ifeq ($(CONFIG_PM),y)
 CHIP_CSRCS += nrf52_pminitialize.c
 endif
diff --git a/arch/arm/src/nrf52/hardware/nrf52_qdec.h 
b/arch/arm/src/nrf52/hardware/nrf52_qdec.h
new file mode 100644
index 00000000000..2cbe799dc92
--- /dev/null
+++ b/arch/arm/src/nrf52/hardware/nrf52_qdec.h
@@ -0,0 +1,242 @@
+/****************************************************************************
+ * arch/arm/src/nrf52/hardware/nrf52_qdec.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H
+#define __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf52_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NRF52_QDEC_TASKS_START_OFFSET       0x0000  /* Task starting the 
quadrature decoder */
+#define NRF52_QDEC_TASKS_STOP_OFFSET        0x0004  /* Task stopping the 
quadrature decoder */
+#define NRF52_QDEC_TASKS_READCLRACC_OFFSET  0x0008  /* Read and clear ACC and 
ACCDBL */
+#define NRF52_QDEC_TASKS_RDCLRACC_OFFSET    0x000c  /* Read and clear ACC */
+#define NRF52_QDEC_TASKS_RDCLRDBL_OFFSET    0x0010  /* Read and clear ACCDBL */
+
+#define NRF52_QDEC_EVENTS_SAMPLERDY_OFFSET  0x0100  /* Event being generated 
for every new sample value */
+#define NRF52_QDEC_EVENTS_REPORTRDY_OFFSET  0x0104  /* Non-null report ready */
+#define NRF52_QDEC_EVENTS_ACCOF_OFFSET      0x0108  /* ACC or ACCDBL register 
overflow */
+#define NRF52_QDEC_EVENTS_DBLRDY_OFFSET     0x010c  /* Double displacement(s) 
detected */
+#define NRF52_QDEC_EVENTS_STOPPED_OFFSET    0x0110  /* QDEC has been stopped */
+
+#define NRF52_QDEC_SHORTS_OFFSET            0x0200  /* Shortcut register */
+#define NRF52_QDEC_INTENSET_OFFSET          0x0304  /* Enable interrupt */
+#define NRF52_QDEC_INTENCLR_OFFSET          0x0308  /* Disable interrupt */
+#define NRF52_QDEC_ENABLE_OFFSET            0x0500  /* Enable the quadrature 
decoder */
+#define NRF52_QDEC_LEDPOL_OFFSET            0x0504  /* LED output pin polarity 
*/
+#define NRF52_QDEC_SAMPLEPER_OFFSET         0x0508  /* Sample period */
+#define NRF52_QDEC_SAMPLE_OFFSET            0x050c  /* Motion sample value */
+#define NRF52_QDEC_REPORTPER_OFFSET         0x0510  /* Number of samples to be 
taken before REPORTRDY and DBLRDY events */
+#define NRF52_QDEC_ACC_OFFSET               0x0514  /* Register accumulating 
the valid transitions */
+#define NRF52_QDEC_ACCREAD_OFFSET           0x0518  /* Snapshot of the ACC 
register */
+#define NRF52_QDEC_PSEL_LED_OFFSET          0x051c  /* Pin select for LED 
signal */
+#define NRF52_QDEC_PSEL_A_OFFSET            0x0520  /* Pin select for A signal 
*/
+#define NRF52_QDEC_PSEL_B_OFFSET            0x0524  /* Pin select for B signal 
*/
+#define NRF52_QDEC_DBFEN_OFFSET             0x0528  /* Enable input debounce 
filters */
+#define NRF52_QDEC_LEDPRE_OFFSET            0x0540  /* Time period the LED is 
switched ON prior to sampling */
+#define NRF52_QDEC_ACCDBL_OFFSET            0x0544  /* Register accumulating 
the number of detected double transitions */
+#define NRF52_QDEC_ACCDBLREAD_OFFSET        0x0548  /* Snapshot of the ACCDBL 
register */
+
+/* Register addresses *******************************************************/
+
+#define NRF52_QDEC_TASKS_START              (NRF52_QDEC_BASE + 
NRF52_QDEC_TASKS_START_OFFSET)
+#define NRF52_QDEC_TASKS_STOP               (NRF52_QDEC_BASE + 
NRF52_QDEC_TASKS_STOP_OFFSET)
+#define NRF52_QDEC_TASKS_READCLRACC         (NRF52_QDEC_BASE + 
NRF52_QDEC_TASKS_READCLRACC_OFFSET)
+#define NRF52_QDEC_TASKS_RDCLRACC           (NRF52_QDEC_BASE + 
NRF52_QDEC_TASKS_RDCLRACC_OFFSET)
+#define NRF52_QDEC_TASKS_RDCLRDBL           (NRF52_QDEC_BASE + 
NRF52_QDEC_TASKS_RDCLRDBL_OFFSET)
+
+#define NRF52_QDEC_EVENTS_SAMPLERDY         (NRF52_QDEC_BASE + 
NRF52_QDEC_EVENTS_SAMPLERDY_OFFSET)
+#define NRF52_QDEC_EVENTS_REPORTRDY         (NRF52_QDEC_BASE + 
NRF52_QDEC_EVENTS_REPORTRDY_OFFSET)
+#define NRF52_QDEC_EVENTS_ACCOF             (NRF52_QDEC_BASE + 
NRF52_QDEC_EVENTS_ACCOF_OFFSET)
+#define NRF52_QDEC_EVENTS_DBLRDY            (NRF52_QDEC_BASE + 
NRF52_QDEC_EVENTS_DBLRDY_OFFSET)
+#define NRF52_QDEC_EVENTS_STOPPED           (NRF52_QDEC_BASE + 
NRF52_QDEC_EVENTS_STOPPED_OFFSET)
+
+#define NRF52_QDEC_SHORTS                   (NRF52_QDEC_BASE + 
NRF52_QDEC_SHORTS_OFFSET)
+#define NRF52_QDEC_INTENSET                 (NRF52_QDEC_BASE + 
NRF52_QDEC_INTENSET_OFFSET)
+#define NRF52_QDEC_INTENCLR                 (NRF52_QDEC_BASE + 
NRF52_QDEC_INTENCLR_OFFSET)
+#define NRF52_QDEC_ENABLE                   (NRF52_QDEC_BASE + 
NRF52_QDEC_ENABLE_OFFSET)
+#define NRF52_QDEC_LEDPOL                   (NRF52_QDEC_BASE + 
NRF52_QDEC_LEDPOL_OFFSET)
+#define NRF52_QDEC_SAMPLEPER                (NRF52_QDEC_BASE + 
NRF52_QDEC_SAMPLEPER_OFFSET)
+#define NRF52_QDEC_SAMPLE                   (NRF52_QDEC_BASE + 
NRF52_QDEC_SAMPLE_OFFSET)
+#define NRF52_QDEC_REPORTPER                (NRF52_QDEC_BASE + 
NRF52_QDEC_REPORTPER_OFFSET)
+#define NRF52_QDEC_ACC                      (NRF52_QDEC_BASE + 
NRF52_QDEC_ACC_OFFSET)
+#define NRF52_QDEC_ACCREAD                  (NRF52_QDEC_BASE + 
NRF52_QDEC_ACCREAD_OFFSET)
+#define NRF52_QDEC_PSEL_LED                 (NRF52_QDEC_BASE + 
NRF52_QDEC_PSEL_LED_OFFSET)
+#define NRF52_QDEC_PSEL_A                   (NRF52_QDEC_BASE + 
NRF52_QDEC_PSEL_A_OFFSET)
+#define NRF52_QDEC_PSEL_B                   (NRF52_QDEC_BASE + 
NRF52_QDEC_PSEL_B_OFFSET)
+#define NRF52_QDEC_DBFEN                    (NRF52_QDEC_BASE + 
NRF52_QDEC_DBFEN_OFFSET)
+#define NRF52_QDEC_LEDPRE                   (NRF52_QDEC_BASE + 
NRF52_QDEC_LEDPRE_OFFSET)
+#define NRF52_QDEC_ACCDBL                   (NRF52_QDEC_BASE + 
NRF52_QDEC_ACCDBL_OFFSET)
+#define NRF52_QDEC_ACCDBLREAD               (NRF52_QDEC_BASE + 
NRF52_QDEC_ACCDBLREAD_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* TASKS_START Register */
+
+#define QDEC_TASKS_START                    (1 << 0)  /* Bit 0: Start QDEC */
+
+/* TASKS_STOP Register */
+
+#define QDEC_TASKS_STOP                     (1 << 0)  /* Bit 0: Stop QDEC */
+
+/* TASKS_READCLRACC Register */
+
+#define QDEC_TASKS_READCLRACC               (1 << 0)  /* Bit 0: Read and clear 
ACC and ACCDBL */
+
+/* TASKS_RDCLRACC Register */
+
+#define QDEC_TASKS_RDCLRACC                 (1 << 0)  /* Bit 0: Read and clear 
ACC */
+
+/* TASKS_RDCLRDBL Register */
+
+#define QDEC_TASKS_RDCLRDBL                 (1 << 0)  /* Bit 0: Read and clear 
ACCDBL */
+
+/* EVENTS_SAMPLERDY Register */
+
+#define QDEC_EVENTS_SAMPLERDY               (1 << 0)  /* Bit 0: Sample ready 
event */
+
+/* EVENTS_REPORTRDY Register */
+
+#define QDEC_EVENTS_REPORTRDY               (1 << 0)  /* Bit 0: Report ready 
event */
+
+/* EVENTS_ACCOF Register */
+
+#define QDEC_EVENTS_ACCOF                   (1 << 0)  /* Bit 0: Accumulator 
overflow event */
+
+/* EVENTS_DBLRDY Register */
+
+#define QDEC_EVENTS_DBLRDY                  (1 << 0)  /* Bit 0: Double 
displacement ready event */
+
+/* EVENTS_STOPPED Register */
+
+#define QDEC_EVENTS_STOPPED                 (1 << 0)  /* Bit 0: QDEC stopped 
event */
+
+/* SHORTS Register */
+
+#define QDEC_SHORTS_REPORTRDY_READCLRACC    (1 << 0)  /* Bit 0: Shortcut 
between REPORTRDY event and READCLRACC task */
+#define QDEC_SHORTS_SAMPLERDY_STOP          (1 << 1)  /* Bit 1: Shortcut 
between SAMPLERDY event and STOP task */
+#define QDEC_SHORTS_REPORTRDY_RDCLRACC      (1 << 2)  /* Bit 2: Shortcut 
between REPORTRDY event and RDCLRACC task */
+#define QDEC_SHORTS_REPORTRDY_STOP          (1 << 3)  /* Bit 3: Shortcut 
between REPORTRDY event and STOP task */
+#define QDEC_SHORTS_DBLRDY_RDCLRDBL         (1 << 4)  /* Bit 4: Shortcut 
between DBLRDY event and RDCLRDBL task */
+#define QDEC_SHORTS_DBLRDY_STOP             (1 << 5)  /* Bit 5: Shortcut 
between DBLRDY event and STOP task */
+#define QDEC_SHORTS_SAMPLERDY_READCLRACC    (1 << 6)  /* Bit 6: Shortcut 
between SAMPLERDY event and READCLRACC task */
+
+/* INTENSET/INTENCLR Register */
+
+#define QDEC_INT_SAMPLERDY                  (1 << 0)  /* Bit 0: Interrupt for 
SAMPLERDY event */
+#define QDEC_INT_REPORTRDY                  (1 << 1)  /* Bit 1: Interrupt for 
REPORTRDY event */
+#define QDEC_INT_ACCOF                      (1 << 2)  /* Bit 2: Interrupt for 
ACCOF event */
+#define QDEC_INT_DBLRDY                     (1 << 3)  /* Bit 3: Interrupt for 
DBLRDY event */
+#define QDEC_INT_STOPPED                    (1 << 4)  /* Bit 4: Interrupt for 
STOPPED event */
+
+/* ENABLE Register */
+
+#define QDEC_ENABLE_DISABLE                 (0 << 0)  /* Bit 0: Disable QDEC */
+#define QDEC_ENABLE_ENABLE                  (1 << 0)  /* Bit 0: Enable QDEC */
+
+/* LEDPOL Register */
+
+#define QDEC_LEDPOL_ACTIVELOW               (0 << 0)  /* Bit 0: LED active on 
output pin low */
+#define QDEC_LEDPOL_ACTIVEHIGH              (1 << 0)  /* Bit 0: LED active on 
output pin high */
+
+/* SAMPLEPER Register */
+
+#define QDEC_SAMPLEPER_SHIFT                (0)       /* Bits 0-3: Sample 
period */
+#define QDEC_SAMPLEPER_MASK                 (0xf << QDEC_SAMPLEPER_SHIFT)
+#  define QDEC_SAMPLEPER_128US              (0 << QDEC_SAMPLEPER_SHIFT)  /* 
128 us */
+#  define QDEC_SAMPLEPER_256US              (1 << QDEC_SAMPLEPER_SHIFT)  /* 
256 us */
+#  define QDEC_SAMPLEPER_512US              (2 << QDEC_SAMPLEPER_SHIFT)  /* 
512 us */
+#  define QDEC_SAMPLEPER_1024US             (3 << QDEC_SAMPLEPER_SHIFT)  /* 
1024 us */
+#  define QDEC_SAMPLEPER_2048US             (4 << QDEC_SAMPLEPER_SHIFT)  /* 
2048 us */
+#  define QDEC_SAMPLEPER_4096US             (5 << QDEC_SAMPLEPER_SHIFT)  /* 
4096 us */
+#  define QDEC_SAMPLEPER_8192US             (6 << QDEC_SAMPLEPER_SHIFT)  /* 
8192 us */
+#  define QDEC_SAMPLEPER_16384US            (7 << QDEC_SAMPLEPER_SHIFT)  /* 
16384 us */
+#  define QDEC_SAMPLEPER_32MS               (8 << QDEC_SAMPLEPER_SHIFT)  /* 
32768 us */
+#  define QDEC_SAMPLEPER_65MS               (9 << QDEC_SAMPLEPER_SHIFT)  /* 
65536 us */
+#  define QDEC_SAMPLEPER_131MS              (10 << QDEC_SAMPLEPER_SHIFT) /* 
131072 us */
+
+/* SAMPLE Register */
+
+#define QDEC_SAMPLE_MASK                    (0xffffffff)
+
+/* REPORTPER Register */
+
+#define QDEC_REPORTPER_SHIFT                (0)       /* Bits 0-3: Report 
period */
+#define QDEC_REPORTPER_MASK                 (0xf << QDEC_REPORTPER_SHIFT)
+#  define QDEC_REPORTPER_10SMPL             (0 << QDEC_REPORTPER_SHIFT)  /* 10 
samples */
+#  define QDEC_REPORTPER_40SMPL             (1 << QDEC_REPORTPER_SHIFT)  /* 40 
samples */
+#  define QDEC_REPORTPER_80SMPL             (2 << QDEC_REPORTPER_SHIFT)  /* 80 
samples */
+#  define QDEC_REPORTPER_120SMPL            (3 << QDEC_REPORTPER_SHIFT)  /* 
120 samples */
+#  define QDEC_REPORTPER_160SMPL            (4 << QDEC_REPORTPER_SHIFT)  /* 
160 samples */
+#  define QDEC_REPORTPER_200SMPL            (5 << QDEC_REPORTPER_SHIFT)  /* 
200 samples */
+#  define QDEC_REPORTPER_240SMPL            (6 << QDEC_REPORTPER_SHIFT)  /* 
240 samples */
+#  define QDEC_REPORTPER_280SMPL            (7 << QDEC_REPORTPER_SHIFT)  /* 
280 samples */
+#  define QDEC_REPORTPER_DISABLED           (8 << QDEC_REPORTPER_SHIFT)  /* 
Disabled */
+
+/* ACC Register */
+
+#define QDEC_ACC_MASK                       (0xffffffff)
+
+/* ACCREAD Register */
+
+#define QDEC_ACCREAD_MASK                   (0xffffffff)
+
+/* PSEL Registers */
+
+#define QDEC_PSEL_PIN_SHIFT                 (0)       /* Bits 0-4: Pin number 
*/
+#define QDEC_PSEL_PIN_MASK                  (0x1f << QDEC_PSEL_PIN_SHIFT)
+#define QDEC_PSEL_PORT_SHIFT                (5)       /* Bit 5: Port number */
+#define QDEC_PSEL_PORT_MASK                 (0x1 << QDEC_PSEL_PORT_SHIFT)
+#define QDEC_PSEL_CONNECT_SHIFT             (31)      /* Bit 31: Connection */
+#define QDEC_PSEL_CONNECT_MASK              (0x1 << QDEC_PSEL_CONNECT_SHIFT)
+#  define QDEC_PSEL_CONNECTED               (0 << QDEC_PSEL_CONNECT_SHIFT)
+#  define QDEC_PSEL_DISCONNECTED            (1 << QDEC_PSEL_CONNECT_SHIFT)
+
+/* DBFEN Register */
+
+#define QDEC_DBFEN_DISABLE                  (0 << 0)  /* Bit 0: Debounce input 
filters disabled */
+#define QDEC_DBFEN_ENABLE                   (1 << 0)  /* Bit 0: Debounce input 
filters enabled */
+
+/* LEDPRE Register */
+
+#define QDEC_LEDPRE_SHIFT                   (0)       /* Bits 0-8: LED pre 
time */
+#define QDEC_LEDPRE_MASK                    (0x1ff << QDEC_LEDPRE_SHIFT)
+
+/* ACCDBL Register */
+
+#define QDEC_ACCDBL_MASK                    (0xf)
+
+/* ACCDBLREAD Register */
+
+#define QDEC_ACCDBLREAD_MASK                (0xf)
+
+#endif /* __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H */
diff --git a/arch/arm/src/nrf52/nrf52_qdec.c b/arch/arm/src/nrf52/nrf52_qdec.c
new file mode 100644
index 00000000000..128f978f99e
--- /dev/null
+++ b/arch/arm/src/nrf52/nrf52_qdec.c
@@ -0,0 +1,445 @@
+/****************************************************************************
+ * arch/arm/src/nrf52/nrf52_qdec.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/sensors/qencoder.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "nrf52_gpio.h"
+#include "nrf52_qdec.h"
+
+#include "hardware/nrf52_qdec.h"
+#include "hardware/nrf52_utils.h"
+
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+#  include <nuttx/irq.h>
+#  include "nrf52_gpiote.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf52_qdec_s
+{
+  const struct qe_ops_s         *ops;
+  const struct nrf52_qeconfig_s *config;
+  uint32_t                       base;
+  uint32_t                       pin_a;
+  uint32_t                       pin_b;
+  int32_t                        position;
+  uint32_t                       posmax;
+  bool                           inuse;
+  bool                           posmax_enabled;
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  uint16_t                       index_count;
+  uint32_t                       pin_index;
+  uint32_t                       index_pos;
+#endif
+  spinlock_t                     lock;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline void nrf52_qdec_putreg(struct nrf52_qdec_s *priv,
+                                     uint32_t offset, uint32_t value);
+static inline uint32_t nrf52_qdec_getreg(struct nrf52_qdec_s *priv,
+                                         uint32_t offset);
+
+static int nrf52_qdec_setup(struct qe_lowerhalf_s *lower);
+static int nrf52_qdec_shutdown(struct qe_lowerhalf_s *lower);
+static int nrf52_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos);
+static int nrf52_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos);
+static int nrf52_qdec_reset(struct qe_lowerhalf_s *lower);
+static int nrf52_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos);
+static int nrf52_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd,
+                            unsigned long arg);
+
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+static int nrf52_qdec_index_interrupt(int irq, void *context, void *arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct qe_ops_s g_nrf52_qdecops =
+{
+  .setup     = nrf52_qdec_setup,
+  .shutdown  = nrf52_qdec_shutdown,
+  .position  = nrf52_qdec_position,
+  .setposmax = nrf52_qdec_setposmax,
+  .reset     = nrf52_qdec_reset,
+  .setindex  = nrf52_qdec_setindex,
+  .ioctl     = nrf52_qdec_ioctl,
+};
+
+#ifdef CONFIG_NRF52_QDEC0
+struct nrf52_qdec_s g_nrf52_qdec0 =
+{
+  .ops       = &g_nrf52_qdecops,
+  .base      = NRF52_QDEC_BASE,
+  .pin_a     = BOARD_QDEC0_A_PIN,
+  .pin_b     = BOARD_QDEC0_B_PIN,
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  .pin_index = BOARD_QDEC0_INDEX_PIN,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf52_qdec_putreg
+ ****************************************************************************/
+
+static inline void nrf52_qdec_putreg(struct nrf52_qdec_s *priv,
+                                     uint32_t offset, uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_getreg
+ ****************************************************************************/
+
+static inline uint32_t nrf52_qdec_getreg(struct nrf52_qdec_s *priv,
+                                         uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_setup
+ ****************************************************************************/
+
+static int nrf52_qdec_setup(struct qe_lowerhalf_s *lower)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  uint32_t pin;
+  uint32_t port;
+  uint32_t regval;
+  irqstate_t flags;
+  int ret;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  if (priv->inuse)
+    {
+      ret = -EBUSY;
+      goto errout;
+    }
+
+  nrf52_gpio_config(priv->pin_a);
+  nrf52_gpio_config(priv->pin_b);
+
+  /* Configure PSEL A */
+
+  pin  = GPIO_PIN_DECODE(priv->pin_a);
+  port = GPIO_PORT_DECODE(priv->pin_a);
+  regval = (port << QDEC_PSEL_PORT_SHIFT);
+  regval |= (pin << QDEC_PSEL_PIN_SHIFT);
+  nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_A_OFFSET, regval);
+
+  /* Configure PSEL B */
+
+  pin  = GPIO_PIN_DECODE(priv->pin_b);
+  port = GPIO_PORT_DECODE(priv->pin_b);
+  regval = (port << QDEC_PSEL_PORT_SHIFT);
+  regval |= (pin << QDEC_PSEL_PIN_SHIFT);
+  nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_B_OFFSET, regval);
+
+  /* Disable LED */
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_LED_OFFSET,
+                    QDEC_PSEL_DISCONNECTED);
+
+  /* Configure decoder */
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_SAMPLEPER_OFFSET,
+                    priv->config->sample_period & QDEC_SAMPLEPER_MASK);
+  nrf52_qdec_putreg(priv, NRF52_QDEC_REPORTPER_OFFSET,
+                    priv->config->report_period & QDEC_REPORTPER_MASK);
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_DBFEN_OFFSET,
+                    priv->config->enable_debounce ? QDEC_DBFEN_ENABLE :
+                    QDEC_DBFEN_DISABLE);
+
+  /* Start decoder */
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_ENABLE_OFFSET, QDEC_ENABLE_ENABLE);
+  nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_START_OFFSET, QDEC_TASKS_START);
+
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  /* Configure Index pin */
+
+  nrf52_gpio_config(priv->pin_index);
+  ret = nrf52_gpiote_set_event(priv->pin_index, true, false,
+                                nrf52_qdec_index_interrupt, priv);
+  if (ret < 0)
+    {
+      snerr("ERROR: Failed to configure index pin interrupt: %d\n", ret);
+      goto errout;
+    }
+#endif
+
+  priv->inuse          = true;
+  priv->position       = 0;
+  priv->posmax_enabled = false;
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  priv->index_pos      = 0;
+  priv->index_count    = 0;
+#endif
+  ret                  = OK;
+
+errout:
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_shutdown
+ ****************************************************************************/
+
+static int nrf52_qdec_shutdown(struct qe_lowerhalf_s *lower)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_STOP_OFFSET, QDEC_TASKS_STOP);
+  nrf52_qdec_putreg(priv, NRF52_QDEC_ENABLE_OFFSET, QDEC_ENABLE_DISABLE);
+
+  nrf52_gpio_unconfig(priv->pin_a);
+  nrf52_gpio_unconfig(priv->pin_b);
+
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  nrf52_gpiote_set_event(priv->pin_index, false, false, NULL, NULL);
+  nrf52_gpio_unconfig(priv->pin_index);
+#endif
+
+  priv->inuse = false;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_position
+ ****************************************************************************/
+
+static int nrf52_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  irqstate_t flags;
+  int32_t accread;
+
+  DEBUGASSERT(lower && pos);
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_RDCLRACC_OFFSET,
+                    QDEC_TASKS_RDCLRACC);
+
+  accread = (int32_t)nrf52_qdec_getreg(priv, NRF52_QDEC_ACCREAD_OFFSET);
+
+  priv->position += accread;
+
+  if (priv->posmax_enabled && priv->posmax > 0)
+    {
+      priv->position = priv->position % (int32_t)priv->posmax;
+      if (priv->position < 0)
+        {
+          priv->position += (int32_t)priv->posmax;
+        }
+    }
+
+  *pos = priv->position;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_setposmax
+ ****************************************************************************/
+
+static int nrf52_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  priv->posmax = pos;
+  priv->posmax_enabled = (pos > 0);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_reset
+ ****************************************************************************/
+
+static int nrf52_qdec_reset(struct qe_lowerhalf_s *lower)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_READCLRACC_OFFSET,
+                    QDEC_TASKS_READCLRACC);
+
+  priv->position = 0;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf52_qdec_setindex
+ ****************************************************************************/
+
+static int nrf52_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos)
+{
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+  priv->index_pos = pos;
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return OK;
+#else
+  return -ENOTTY;
+#endif
+}
+
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+
+/****************************************************************************
+ * Name: nrf52_qdec_index_interrupt
+ ****************************************************************************/
+
+static int nrf52_qdec_index_interrupt(int irq, void *context, void *arg)
+{
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)arg;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  priv->position = priv->index_pos;
+  priv->index_count++;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return OK;
+}
+
+#endif
+
+/****************************************************************************
+ * Name: nrf52_qdec_ioctl
+ ****************************************************************************/
+
+static int nrf52_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd,
+                            unsigned long arg)
+{
+#ifdef CONFIG_NRF52_QENCODER_INDEX
+  struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower;
+  struct qe_index_s *index;
+
+  if (cmd == QEIOC_GETINDEX)
+    {
+      index = (struct qe_index_s *)arg;
+      index->qenc_pos = priv->position;
+      index->indx_pos = priv->index_pos;
+      index->indx_cnt = priv->index_count;
+      return OK;
+    }
+#endif
+
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf52_qeinitialize
+ ****************************************************************************/
+
+struct qe_lowerhalf_s *
+nrf52_qeinitialize(int qdec, const struct nrf52_qeconfig_s *config)
+{
+  struct nrf52_qdec_s *priv = NULL;
+
+  DEBUGASSERT(config != NULL);
+
+  switch (qdec)
+    {
+#ifdef CONFIG_NRF52_QDEC0
+      case 0:
+        {
+          priv = &g_nrf52_qdec0;
+          break;
+        }
+#endif
+
+      default:
+        {
+          snerr("ERROR: Invalid QDEC instance: %d\n", qdec);
+          return NULL;
+        }
+    }
+
+  priv->config = config;
+
+  return (struct qe_lowerhalf_s *)priv;
+}
diff --git a/arch/arm/src/nrf52/nrf52_qdec.h b/arch/arm/src/nrf52/nrf52_qdec.h
new file mode 100644
index 00000000000..3dce1dabdc7
--- /dev/null
+++ b/arch/arm/src/nrf52/nrf52_qdec.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+ * arch/arm/src/nrf52/nrf52_qdec.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H
+#define __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct nrf52_qeconfig_s
+{
+  uint8_t  sample_period;
+  uint8_t  report_period;
+  bool     enable_debounce;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+struct qe_lowerhalf_s;
+struct qe_lowerhalf_s *
+nrf52_qeinitialize(int qdec, const struct nrf52_qeconfig_s *config);
+
+#endif /* __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H */
diff --git a/arch/arm/src/nrf53/CMakeLists.txt 
b/arch/arm/src/nrf53/CMakeLists.txt
index 900c52430c1..49d99a0548f 100644
--- a/arch/arm/src/nrf53/CMakeLists.txt
+++ b/arch/arm/src/nrf53/CMakeLists.txt
@@ -107,6 +107,14 @@ if(CONFIG_NRF53_QSPI)
   list(APPEND SRCS nrf53_qspi.c)
 endif()
 
+if(CONFIG_NRF53_QDEC)
+  list(APPEND SRCS nrf53_qdec.c)
+endif()
+
+if(CONFIG_SENSORS_QENCODER)
+  list(APPEND SRCS nrf53_qdec.c)
+endif()
+
 if(CONFIG_NRF53_SOFTDEVICE_CONTROLLER)
 
   set(NRFXLIB_VER "2.3.0")
diff --git a/arch/arm/src/nrf53/Kconfig b/arch/arm/src/nrf53/Kconfig
index a629b2d5a23..b951cd9bde1 100644
--- a/arch/arm/src/nrf53/Kconfig
+++ b/arch/arm/src/nrf53/Kconfig
@@ -182,6 +182,10 @@ config NRF53_RTC
        bool
        default n
 
+config NRF53_QDEC
+       bool
+       default n
+
 menu "nRF53 Peripheral Selection"
 
 config NRF53_GPIOTE
@@ -714,6 +718,30 @@ endif # NRF53_QSPI
 
 endmenu # QSPI Configuration
 
+config NRF53_QDEC0
+       bool "Qencoder 0 (QDEC0) support"
+       default n
+       select NRF53_QDEC
+       ---help---
+               Enable Qencoder 0 support.
+
+config NRF53_QDEC1
+       bool "Qencoder 1 (QDEC1) support"
+       default n
+       select NRF53_QDEC
+       ---help---
+               Enable Qencoder 1 support.
+
+config NRF53_QENCODER_INDEX
+       bool "Enable QDEC index pin support"
+       default n
+       depends on NRF53_GPIOTE && NRF53_QDEC
+       ---help---
+               Enable index/Z channel support for quadrature encoder.
+               Uses a GPIO interrupt to detect index pulses and reset
+               position to configured index value. Requires one GPIOTE
+               channel for interrupt detection.
+
 menuconfig NRF53_SOFTDEVICE_CONTROLLER
        bool "SoftDevice Controller"
        depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf53/Make.defs b/arch/arm/src/nrf53/Make.defs
index d91f55b12e6..9a0a63827c3 100644
--- a/arch/arm/src/nrf53/Make.defs
+++ b/arch/arm/src/nrf53/Make.defs
@@ -101,6 +101,10 @@ ifeq ($(CONFIG_NRF53_QSPI),y)
 CHIP_CSRCS += nrf53_qspi.c
 endif
 
+ifeq ($(CONFIG_NRF53_QDEC),y)
+CHIP_CSRCS += nrf53_qdec.c
+endif
+
 ifeq ($(CONFIG_NRF53_SOFTDEVICE_CONTROLLER),y)
 
 NRFXLIB_UNPACK  := sdk-nrfxlib
diff --git a/arch/arm/src/nrf53/hardware/nrf53_qdec.h 
b/arch/arm/src/nrf53/hardware/nrf53_qdec.h
new file mode 100644
index 00000000000..a2d45933b87
--- /dev/null
+++ b/arch/arm/src/nrf53/hardware/nrf53_qdec.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+ * arch/arm/src/nrf53/hardware/nrf53_qdec.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H
+#define __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf53_memorymap_cpuapp.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* QDEC Base Addresses */
+
+#define NRF53_QDEC0_BASE        NRF53_QDEC_BASE  /* QDEC0: 0x50033000 */
+#define NRF53_QDEC1_BASE        0x50034000       /* QDEC1: 0x50034000 */
+
+/* Register offsets *********************************************************/
+
+#define NRF53_QDEC_TASKS_START_OFFSET       0x0000  /* Task starting the 
quadrature decoder */
+#define NRF53_QDEC_TASKS_STOP_OFFSET        0x0004  /* Task stopping the 
quadrature decoder */
+#define NRF53_QDEC_TASKS_READCLRACC_OFFSET  0x0008  /* Read and clear ACC and 
ACCDBL */
+#define NRF53_QDEC_TASKS_RDCLRACC_OFFSET    0x000c  /* Read and clear ACC */
+#define NRF53_QDEC_TASKS_RDCLRDBL_OFFSET    0x0010  /* Read and clear ACCDBL */
+
+#define NRF53_QDEC_EVENTS_SAMPLERDY_OFFSET  0x0100  /* Event being generated 
for every new sample value */
+#define NRF53_QDEC_EVENTS_REPORTRDY_OFFSET  0x0104  /* Non-null report ready */
+#define NRF53_QDEC_EVENTS_ACCOF_OFFSET      0x0108  /* ACC or ACCDBL register 
overflow */
+#define NRF53_QDEC_EVENTS_DBLRDY_OFFSET     0x010c  /* Double displacement(s) 
detected */
+#define NRF53_QDEC_EVENTS_STOPPED_OFFSET    0x0110  /* QDEC has been stopped */
+
+#define NRF53_QDEC_SHORTS_OFFSET            0x0200  /* Shortcut register */
+#define NRF53_QDEC_INTENSET_OFFSET          0x0304  /* Enable interrupt */
+#define NRF53_QDEC_INTENCLR_OFFSET          0x0308  /* Disable interrupt */
+#define NRF53_QDEC_ENABLE_OFFSET            0x0500  /* Enable the quadrature 
decoder */
+#define NRF53_QDEC_LEDPOL_OFFSET            0x0504  /* LED output pin polarity 
*/
+#define NRF53_QDEC_SAMPLEPER_OFFSET         0x0508  /* Sample period */
+#define NRF53_QDEC_SAMPLE_OFFSET            0x050c  /* Motion sample value */
+#define NRF53_QDEC_REPORTPER_OFFSET         0x0510  /* Number of samples to be 
taken before REPORTRDY and DBLRDY events */
+#define NRF53_QDEC_ACC_OFFSET               0x0514  /* Register accumulating 
the valid transitions */
+#define NRF53_QDEC_ACCREAD_OFFSET           0x0518  /* Snapshot of the ACC 
register */
+#define NRF53_QDEC_PSEL_LED_OFFSET          0x051c  /* Pin select for LED 
signal */
+#define NRF53_QDEC_PSEL_A_OFFSET            0x0520  /* Pin select for A signal 
*/
+#define NRF53_QDEC_PSEL_B_OFFSET            0x0524  /* Pin select for B signal 
*/
+#define NRF53_QDEC_DBFEN_OFFSET             0x0528  /* Enable input debounce 
filters */
+#define NRF53_QDEC_LEDPRE_OFFSET            0x0540  /* Time period the LED is 
switched ON prior to sampling */
+#define NRF53_QDEC_ACCDBL_OFFSET            0x0544  /* Register accumulating 
the number of detected double transitions */
+#define NRF53_QDEC_ACCDBLREAD_OFFSET        0x0548  /* Snapshot of the ACCDBL 
register */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* TASKS_START Register */
+
+#define QDEC_TASKS_START                    (1 << 0)  /* Bit 0: Start QDEC */
+
+/* TASKS_STOP Register */
+
+#define QDEC_TASKS_STOP                     (1 << 0)  /* Bit 0: Stop QDEC */
+
+/* TASKS_READCLRACC Register */
+
+#define QDEC_TASKS_READCLRACC               (1 << 0)  /* Bit 0: Read and clear 
ACC and ACCDBL */
+
+/* TASKS_RDCLRACC Register */
+
+#define QDEC_TASKS_RDCLRACC                 (1 << 0)  /* Bit 0: Read and clear 
ACC */
+
+/* TASKS_RDCLRDBL Register */
+
+#define QDEC_TASKS_RDCLRDBL                 (1 << 0)  /* Bit 0: Read and clear 
ACCDBL */
+
+/* EVENTS_SAMPLERDY Register */
+
+#define QDEC_EVENTS_SAMPLERDY               (1 << 0)  /* Bit 0: Sample ready 
event */
+
+/* EVENTS_REPORTRDY Register */
+
+#define QDEC_EVENTS_REPORTRDY               (1 << 0)  /* Bit 0: Report ready 
event */
+
+/* EVENTS_ACCOF Register */
+
+#define QDEC_EVENTS_ACCOF                   (1 << 0)  /* Bit 0: Accumulator 
overflow event */
+
+/* EVENTS_DBLRDY Register */
+
+#define QDEC_EVENTS_DBLRDY                  (1 << 0)  /* Bit 0: Double 
displacement ready event */
+
+/* EVENTS_STOPPED Register */
+
+#define QDEC_EVENTS_STOPPED                 (1 << 0)  /* Bit 0: QDEC stopped 
event */
+
+/* SHORTS Register */
+
+#define QDEC_SHORTS_REPORTRDY_READCLRACC    (1 << 0)  /* Bit 0: Shortcut 
between REPORTRDY event and READCLRACC task */
+#define QDEC_SHORTS_SAMPLERDY_STOP          (1 << 1)  /* Bit 1: Shortcut 
between SAMPLERDY event and STOP task */
+#define QDEC_SHORTS_REPORTRDY_RDCLRACC      (1 << 2)  /* Bit 2: Shortcut 
between REPORTRDY event and RDCLRACC task */
+#define QDEC_SHORTS_REPORTRDY_STOP          (1 << 3)  /* Bit 3: Shortcut 
between REPORTRDY event and STOP task */
+#define QDEC_SHORTS_DBLRDY_RDCLRDBL         (1 << 4)  /* Bit 4: Shortcut 
between DBLRDY event and RDCLRDBL task */
+#define QDEC_SHORTS_DBLRDY_STOP             (1 << 5)  /* Bit 5: Shortcut 
between DBLRDY event and STOP task */
+#define QDEC_SHORTS_SAMPLERDY_READCLRACC    (1 << 6)  /* Bit 6: Shortcut 
between SAMPLERDY event and READCLRACC task */
+
+/* INTENSET/INTENCLR Register */
+
+#define QDEC_INT_SAMPLERDY                  (1 << 0)  /* Bit 0: Interrupt for 
SAMPLERDY event */
+#define QDEC_INT_REPORTRDY                  (1 << 1)  /* Bit 1: Interrupt for 
REPORTRDY event */
+#define QDEC_INT_ACCOF                      (1 << 2)  /* Bit 2: Interrupt for 
ACCOF event */
+#define QDEC_INT_DBLRDY                     (1 << 3)  /* Bit 3: Interrupt for 
DBLRDY event */
+#define QDEC_INT_STOPPED                    (1 << 4)  /* Bit 4: Interrupt for 
STOPPED event */
+
+/* ENABLE Register */
+
+#define QDEC_ENABLE_DISABLE                 (0 << 0)  /* Bit 0: Disable QDEC */
+#define QDEC_ENABLE_ENABLE                  (1 << 0)  /* Bit 0: Enable QDEC */
+
+/* LEDPOL Register */
+
+#define QDEC_LEDPOL_ACTIVELOW               (0 << 0)  /* Bit 0: LED active on 
output pin low */
+#define QDEC_LEDPOL_ACTIVEHIGH              (1 << 0)  /* Bit 0: LED active on 
output pin high */
+
+/* SAMPLEPER Register */
+
+#define QDEC_SAMPLEPER_SHIFT                (0)       /* Bits 0-3: Sample 
period */
+#define QDEC_SAMPLEPER_MASK                 (0xf << QDEC_SAMPLEPER_SHIFT)
+#  define QDEC_SAMPLEPER_128US              (0 << QDEC_SAMPLEPER_SHIFT)  /* 
128 us */
+#  define QDEC_SAMPLEPER_256US              (1 << QDEC_SAMPLEPER_SHIFT)  /* 
256 us */
+#  define QDEC_SAMPLEPER_512US              (2 << QDEC_SAMPLEPER_SHIFT)  /* 
512 us */
+#  define QDEC_SAMPLEPER_1024US             (3 << QDEC_SAMPLEPER_SHIFT)  /* 
1024 us */
+#  define QDEC_SAMPLEPER_2048US             (4 << QDEC_SAMPLEPER_SHIFT)  /* 
2048 us */
+#  define QDEC_SAMPLEPER_4096US             (5 << QDEC_SAMPLEPER_SHIFT)  /* 
4096 us */
+#  define QDEC_SAMPLEPER_8192US             (6 << QDEC_SAMPLEPER_SHIFT)  /* 
8192 us */
+#  define QDEC_SAMPLEPER_16384US            (7 << QDEC_SAMPLEPER_SHIFT)  /* 
16384 us */
+#  define QDEC_SAMPLEPER_32MS               (8 << QDEC_SAMPLEPER_SHIFT)  /* 
32768 us */
+#  define QDEC_SAMPLEPER_65MS               (9 << QDEC_SAMPLEPER_SHIFT)  /* 
65536 us */
+#  define QDEC_SAMPLEPER_131MS              (10 << QDEC_SAMPLEPER_SHIFT) /* 
131072 us */
+
+/* SAMPLE Register */
+
+#define QDEC_SAMPLE_MASK                    (0xffffffff)
+
+/* REPORTPER Register */
+
+#define QDEC_REPORTPER_SHIFT                (0)       /* Bits 0-3: Report 
period */
+#define QDEC_REPORTPER_MASK                 (0xf << QDEC_REPORTPER_SHIFT)
+#  define QDEC_REPORTPER_10SMPL             (0 << QDEC_REPORTPER_SHIFT)  /* 10 
samples */
+#  define QDEC_REPORTPER_40SMPL             (1 << QDEC_REPORTPER_SHIFT)  /* 40 
samples */
+#  define QDEC_REPORTPER_80SMPL             (2 << QDEC_REPORTPER_SHIFT)  /* 80 
samples */
+#  define QDEC_REPORTPER_120SMPL            (3 << QDEC_REPORTPER_SHIFT)  /* 
120 samples */
+#  define QDEC_REPORTPER_160SMPL            (4 << QDEC_REPORTPER_SHIFT)  /* 
160 samples */
+#  define QDEC_REPORTPER_200SMPL            (5 << QDEC_REPORTPER_SHIFT)  /* 
200 samples */
+#  define QDEC_REPORTPER_240SMPL            (6 << QDEC_REPORTPER_SHIFT)  /* 
240 samples */
+#  define QDEC_REPORTPER_280SMPL            (7 << QDEC_REPORTPER_SHIFT)  /* 
280 samples */
+#  define QDEC_REPORTPER_DISABLED           (8 << QDEC_REPORTPER_SHIFT)  /* 
Disabled */
+
+/* ACC Register */
+
+#define QDEC_ACC_MASK                       (0xffffffff)
+
+/* ACCREAD Register */
+
+#define QDEC_ACCREAD_MASK                   (0xffffffff)
+
+/* PSEL Registers */
+
+#define QDEC_PSEL_PIN_SHIFT                 (0)       /* Bits 0-4: Pin number 
*/
+#define QDEC_PSEL_PIN_MASK                  (0x1f << QDEC_PSEL_PIN_SHIFT)
+#define QDEC_PSEL_PORT_SHIFT                (5)       /* Bit 5: Port number */
+#define QDEC_PSEL_PORT_MASK                 (0x1 << QDEC_PSEL_PORT_SHIFT)
+#define QDEC_PSEL_CONNECT_SHIFT             (31)      /* Bit 31: Connection */
+#define QDEC_PSEL_CONNECT_MASK              (0x1 << QDEC_PSEL_CONNECT_SHIFT)
+#  define QDEC_PSEL_CONNECTED               (0 << QDEC_PSEL_CONNECT_SHIFT)
+#  define QDEC_PSEL_DISCONNECTED            (1 << QDEC_PSEL_CONNECT_SHIFT)
+
+/* DBFEN Register */
+
+#define QDEC_DBFEN_DISABLE                  (0 << 0)  /* Bit 0: Debounce input 
filters disabled */
+#define QDEC_DBFEN_ENABLE                   (1 << 0)  /* Bit 0: Debounce input 
filters enabled */
+
+/* LEDPRE Register */
+
+#define QDEC_LEDPRE_SHIFT                   (0)       /* Bits 0-8: LED pre 
time */
+#define QDEC_LEDPRE_MASK                    (0x1ff << QDEC_LEDPRE_SHIFT)
+
+/* ACCDBL Register */
+
+#define QDEC_ACCDBL_MASK                    (0xf)
+
+/* ACCDBLREAD Register */
+
+#define QDEC_ACCDBLREAD_MASK                (0xf)
+
+#endif /* __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H */
diff --git a/arch/arm/src/nrf53/nrf53_qdec.c b/arch/arm/src/nrf53/nrf53_qdec.c
new file mode 100644
index 00000000000..91c6e3fc43d
--- /dev/null
+++ b/arch/arm/src/nrf53/nrf53_qdec.c
@@ -0,0 +1,464 @@
+/****************************************************************************
+ * arch/arm/src/nrf53/nrf53_qdec.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/sensors/qencoder.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "nrf53_gpio.h"
+#include "nrf53_qdec.h"
+
+#include "hardware/nrf53_qdec.h"
+#include "hardware/nrf53_utils.h"
+
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+#  include <nuttx/irq.h>
+#  include "nrf53_gpiote.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf53_qdec_s
+{
+  const struct qe_ops_s         *ops;
+  const struct nrf53_qeconfig_s *config;
+  uint32_t                       base;
+  uint32_t                       pin_a;
+  uint32_t                       pin_b;
+  int32_t                        position;
+  uint32_t                       posmax;
+  bool                           inuse;
+  bool                           posmax_enabled;
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  uint16_t                       index_count;
+  uint32_t                       pin_index;
+  uint32_t                       index_pos;
+#endif
+  spinlock_t                     lock;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline void nrf53_qdec_putreg(struct nrf53_qdec_s *priv,
+                                     uint32_t offset, uint32_t value);
+static inline uint32_t nrf53_qdec_getreg(struct nrf53_qdec_s *priv,
+                                         uint32_t offset);
+
+static int nrf53_qdec_setup(struct qe_lowerhalf_s *lower);
+static int nrf53_qdec_shutdown(struct qe_lowerhalf_s *lower);
+static int nrf53_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos);
+static int nrf53_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos);
+static int nrf53_qdec_reset(struct qe_lowerhalf_s *lower);
+static int nrf53_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos);
+static int nrf53_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd,
+                            unsigned long arg);
+
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+static int nrf53_qdec_index_interrupt(int irq, void *context, void *arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct qe_ops_s g_nrf53_qdecops =
+{
+  .setup     = nrf53_qdec_setup,
+  .shutdown  = nrf53_qdec_shutdown,
+  .position  = nrf53_qdec_position,
+  .setposmax = nrf53_qdec_setposmax,
+  .reset     = nrf53_qdec_reset,
+  .setindex  = nrf53_qdec_setindex,
+  .ioctl     = nrf53_qdec_ioctl,
+};
+
+#ifdef CONFIG_NRF53_QDEC0
+struct nrf53_qdec_s g_nrf53_qdec0 =
+{
+  .ops   = &g_nrf53_qdecops,
+  .base  = NRF53_QDEC0_BASE,
+  .pin_a = BOARD_QDEC0_A_PIN,
+  .pin_b = BOARD_QDEC0_B_PIN,
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  .pin_index = BOARD_QDEC0_INDEX_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF53_QDEC1
+struct nrf53_qdec_s g_nrf53_qdec1 =
+{
+  .ops       = &g_nrf53_qdecops,
+  .base      = NRF53_QDEC1_BASE,
+  .pin_a     = BOARD_QDEC1_A_PIN,
+  .pin_b     = BOARD_QDEC1_B_PIN,
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  .pin_index = BOARD_QDEC1_INDEX_PIN,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf53_qdec_putreg
+ ****************************************************************************/
+
+static inline void nrf53_qdec_putreg(struct nrf53_qdec_s *priv,
+                                     uint32_t offset, uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_getreg
+ ****************************************************************************/
+
+static inline uint32_t nrf53_qdec_getreg(struct nrf53_qdec_s *priv,
+                                         uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_setup
+ ****************************************************************************/
+
+static int nrf53_qdec_setup(struct qe_lowerhalf_s *lower)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  uint32_t pin;
+  uint32_t port;
+  uint32_t regval;
+  irqstate_t flags;
+  int ret;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  if (priv->inuse)
+    {
+      ret = -EBUSY;
+      goto errout;
+    }
+
+  nrf53_gpio_config(priv->pin_a);
+  nrf53_gpio_config(priv->pin_b);
+
+  /* Configure PSEL A */
+
+  pin  = GPIO_PIN_DECODE(priv->pin_a);
+  port = GPIO_PORT_DECODE(priv->pin_a);
+  regval = (port << QDEC_PSEL_PORT_SHIFT);
+  regval |= (pin << QDEC_PSEL_PIN_SHIFT);
+  nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_A_OFFSET, regval);
+
+  /* Configure PSEL B */
+
+  pin  = GPIO_PIN_DECODE(priv->pin_b);
+  port = GPIO_PORT_DECODE(priv->pin_b);
+  regval = (port << QDEC_PSEL_PORT_SHIFT);
+  regval |= (pin << QDEC_PSEL_PIN_SHIFT);
+  nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_B_OFFSET, regval);
+
+  /* Disable LED */
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_LED_OFFSET,
+                    QDEC_PSEL_DISCONNECTED);
+
+  /* Configure decoder */
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_SAMPLEPER_OFFSET,
+                    priv->config->sample_period & QDEC_SAMPLEPER_MASK);
+  nrf53_qdec_putreg(priv, NRF53_QDEC_REPORTPER_OFFSET,
+                    priv->config->report_period & QDEC_REPORTPER_MASK);
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_DBFEN_OFFSET,
+                    priv->config->enable_debounce ? QDEC_DBFEN_ENABLE :
+                    QDEC_DBFEN_DISABLE);
+
+  /* Start decoder */
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_ENABLE_OFFSET, QDEC_ENABLE_ENABLE);
+  nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_START_OFFSET, QDEC_TASKS_START);
+
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  nrf53_gpio_config(priv->pin_index);
+  ret = nrf53_gpiote_set_event(priv->pin_index, true, false,
+                                nrf53_qdec_index_interrupt, priv);
+  if (ret < 0)
+    {
+      snerr("ERROR: Failed to configure index pin interrupt: %d\n", ret);
+      goto errout;
+    }
+#endif
+
+  priv->inuse          = true;
+  priv->position       = 0;
+  priv->posmax_enabled = false;
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  priv->index_pos      = 0;
+  priv->index_count    = 0;
+#endif
+  ret                  = OK;
+
+errout:
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_shutdown
+ ****************************************************************************/
+
+static int nrf53_qdec_shutdown(struct qe_lowerhalf_s *lower)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_STOP_OFFSET, QDEC_TASKS_STOP);
+  nrf53_qdec_putreg(priv, NRF53_QDEC_ENABLE_OFFSET, QDEC_ENABLE_DISABLE);
+
+  nrf53_gpio_unconfig(priv->pin_a);
+  nrf53_gpio_unconfig(priv->pin_b);
+
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  nrf53_gpiote_set_event(priv->pin_index, false, false, NULL, NULL);
+  nrf53_gpio_unconfig(priv->pin_index);
+#endif
+
+  priv->inuse = false;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_position
+ ****************************************************************************/
+
+static int nrf53_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  irqstate_t flags;
+  int32_t accread;
+
+  DEBUGASSERT(lower && pos);
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_RDCLRACC_OFFSET,
+                    QDEC_TASKS_RDCLRACC);
+
+  accread = (int32_t)nrf53_qdec_getreg(priv, NRF53_QDEC_ACCREAD_OFFSET);
+
+  priv->position += accread;
+
+  if (priv->posmax_enabled && priv->posmax > 0)
+    {
+      priv->position = priv->position % (int32_t)priv->posmax;
+      if (priv->position < 0)
+        {
+          priv->position += (int32_t)priv->posmax;
+        }
+    }
+
+  *pos = priv->position;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_setposmax
+ ****************************************************************************/
+
+static int nrf53_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  priv->posmax = pos;
+  priv->posmax_enabled = (pos > 0);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_reset
+ ****************************************************************************/
+
+static int nrf53_qdec_reset(struct qe_lowerhalf_s *lower)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_READCLRACC_OFFSET,
+                    QDEC_TASKS_READCLRACC);
+
+  priv->position = 0;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf53_qdec_setindex
+ ****************************************************************************/
+
+static int nrf53_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos)
+{
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+  priv->index_pos = pos;
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return OK;
+#else
+  return -ENOTTY;
+#endif
+}
+
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+
+/****************************************************************************
+ * Name: nrf53_qdec_index_interrupt
+ ****************************************************************************/
+
+static int nrf53_qdec_index_interrupt(int irq, void *context, void *arg)
+{
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)arg;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  priv->position = priv->index_pos;
+  priv->index_count++;
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return OK;
+}
+
+#endif
+
+/****************************************************************************
+ * Name: nrf53_qdec_ioctl
+ ****************************************************************************/
+
+static int nrf53_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd,
+                            unsigned long arg)
+{
+#ifdef CONFIG_NRF53_QENCODER_INDEX
+  struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower;
+  struct qe_index_s *index;
+
+  if (cmd == QEIOC_GETINDEX)
+    {
+      index = (struct qe_index_s *)arg;
+      index->qenc_pos = priv->position;
+      index->indx_pos = priv->index_pos;
+      index->indx_cnt = priv->index_count;
+      return OK;
+    }
+#endif
+
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf53_qeinitialize
+ ****************************************************************************/
+
+struct qe_lowerhalf_s *
+nrf53_qeinitialize(int qdec, const struct nrf53_qeconfig_s *config)
+{
+  struct nrf53_qdec_s *priv = NULL;
+
+  DEBUGASSERT(config != NULL);
+
+  switch (qdec)
+    {
+#ifdef CONFIG_NRF53_QDEC0
+      case 0:
+        {
+          priv = &g_nrf53_qdec0;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_NRF53_QDEC1
+      case 1:
+        {
+          priv = &g_nrf53_qdec1;
+          break;
+        }
+#endif
+
+      default:
+        {
+          snerr("ERROR: Invalid QDEC instance: %d\n", qdec);
+          return NULL;
+        }
+    }
+
+  priv->config = config;
+
+  return (struct qe_lowerhalf_s *)priv;
+}
diff --git a/arch/arm/src/nrf53/nrf53_qdec.h b/arch/arm/src/nrf53/nrf53_qdec.h
new file mode 100644
index 00000000000..19b86351175
--- /dev/null
+++ b/arch/arm/src/nrf53/nrf53_qdec.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+ * arch/arm/src/nrf53/nrf53_qdec.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H
+#define __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct nrf53_qeconfig_s
+{
+  uint8_t  sample_period;
+  uint8_t  report_period;
+  bool     enable_debounce;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+struct qe_lowerhalf_s;
+struct qe_lowerhalf_s *
+nrf53_qeinitialize(int qdec, const struct nrf53_qeconfig_s *config);
+
+#endif /* __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H */

Reply via email to