You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by li...@apache.org on 2026/04/12 23:41:59 UTC
(nuttx) 01/03: arch/nrf52|nrf53: add qencoder support
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 <ra...@railab.me>
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 <ra...@railab.me>
---
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 */