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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5fda288a2a5 arm/nrf54l: add GPIOTE support
5fda288a2a5 is described below

commit 5fda288a2a58f1f2c89b738507aa22382a0d4397
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:17:23 2026 +0200

    arm/nrf54l: add GPIOTE support
    
    arm/nrf54l: add GPIOTE support
    
    Assisted-by: Codex:GPT-6
    Signed-off-by: raiden00pl <[email protected]>
---
 Documentation/platforms/arm/nrf54l/index.rst |   6 +-
 arch/arm/src/nrf54l/CMakeLists.txt           |   3 +
 arch/arm/src/nrf54l/Kconfig                  |  15 +
 arch/arm/src/nrf54l/Make.defs                |   4 +
 arch/arm/src/nrf54l/hardware/nrf54l_gpiote.h | 103 ++++
 arch/arm/src/nrf54l/nrf54l_gpiote.c          | 704 +++++++++++++++++++++++++++
 arch/arm/src/nrf54l/nrf54l_gpiote.h          | 174 +++++++
 arch/arm/src/nrf54l/nrf54l_irq.c             |  10 +
 8 files changed, 1018 insertions(+), 1 deletion(-)

diff --git a/Documentation/platforms/arm/nrf54l/index.rst 
b/Documentation/platforms/arm/nrf54l/index.rst
index 025ca3d9482..b3293e5a51a 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -15,7 +15,7 @@ The following list indicates peripherals supported in NuttX:
 Peripheral  Support Notes
 ==========  ======= =====================================
 GPIO        Yes
-GPIOTE      No
+GPIOTE      Yes
 GRTC        Yes     Counter and tickless scheduling
 PWM         No
 QDEC        No
@@ -34,6 +34,10 @@ GPIO
 ----
 
 Pins can be configured and operated using ``nrf54l_gpio_*`` functions.
+GPIOTE supports channel events and tasks, with optional per-pin callbacks
+for PORT events. Channels 0 through 7 belong to GPIOTE20 and serve P1 and
+P3. Channels 8 through 11 belong to GPIOTE30 and serve P0. P2 does not
+support GPIOTE.
 
 GRTC
 ----
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt 
b/arch/arm/src/nrf54l/CMakeLists.txt
index 029e6a34f81..e545c8db9e6 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -46,6 +46,9 @@ endif()
 if(CONFIG_NRF54L_UART)
   list(APPEND SRCS nrf54l_serial.c)
 endif()
+if(CONFIG_NRF54L_GPIOTE)
+  list(APPEND SRCS nrf54l_gpiote.c)
+endif()
 if(CONFIG_NRF54L_TIMER)
   list(APPEND SRCS nrf54l_tim.c)
   if(CONFIG_TIMER)
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index 76dd4ebcbb7..e0f9107e2f6 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -35,6 +35,10 @@ config NRF54L_TIMER
 
 menu "nRF54L Peripheral Selection"
 
+config NRF54L_GPIOTE
+       bool "GPIOTE (GPIO interrupts)"
+       default n
+
 config NRF54L_UART0
        bool "UART0"
        default n
@@ -210,6 +214,17 @@ config NRF54L_DEBUG_ACCESS
                Open unlocked TAMPC debug signals for SWD/RTT development.
                Signals locked by an earlier boot stage are left unchanged.
 
+menu "GPIO Interrupt Configuration"
+
+config NRF54L_PER_PIN_INTERRUPTS
+       bool "Per-pin interrupt callbacks"
+       default n
+       depends on NRF54L_GPIOTE
+       ---help---
+               Dispatch PORT events to separate callbacks for each pin.
+
+endmenu # GPIO Interrupt Configuration
+
 menuconfig NRF54L_SOFTDEVICE_CONTROLLER
        bool "SoftDevice Controller"
        depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs
index 1764d2088b4..fe9629b9e25 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -46,6 +46,10 @@ ifeq ($(CONFIG_NRF54L_UART),y)
 CHIP_CSRCS += nrf54l_serial.c
 endif
 
+ifeq ($(CONFIG_NRF54L_GPIOTE),y)
+CHIP_CSRCS += nrf54l_gpiote.c
+endif
+
 ifeq ($(CONFIG_NRF54L_TIMER),y)
 CHIP_CSRCS += nrf54l_tim.c
 ifeq ($(CONFIG_TIMER),y)
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_gpiote.h 
b/arch/arm/src/nrf54l/hardware/nrf54l_gpiote.h
new file mode 100644
index 00000000000..27d74cab18f
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_gpiote.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_gpiote.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_NRF54L_HARDWARE_NRF54L_GPIOTE_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_GPIOTE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets for GPIOTE **********************************************/
+
+#define NRF54L_GPIOTE_TASKS_OUT_OFFSET(x)           (0x0000 + (0x04 * (x))) /* 
TASKS_OUT[x] */
+#define NRF54L_GPIOTE_TASKS_SET_OFFSET(x)           (0x0030 + (0x04 * (x))) /* 
TASKS_SET[x] */
+#define NRF54L_GPIOTE_TASKS_CLR_OFFSET(x)           (0x0060 + (0x04 * (x))) /* 
TASKS_CLR[x] */
+#define NRF54L_GPIOTE_SUBSCRIBE_OUT_OFFSET(x)       (0x0080 + (0x04 * (x))) /* 
SUBSCRIBE_OUT[x] */
+#define NRF54L_GPIOTE_SUBSCRIBE_SET_OFFSET(x)       (0x00b0 + (0x04 * (x))) /* 
SUBSCRIBE_SET[x] */
+#define NRF54L_GPIOTE_SUBSCRIBE_CLR_OFFSET(x)       (0x00e0 + (0x04 * (x))) /* 
SUBSCRIBE_CLR[x] */
+#define NRF54L_GPIOTE_EVENTS_IN_OFFSET(x)           (0x0100 + (0x04 * (x))) /* 
EVENTS_IN[x] */
+#define NRF54L_GPIOTE_EVENTS_PORT_NONSECURE_OFFSET  0x0140                  /* 
EVENTS_PORT.NONSECURE */
+#define NRF54L_GPIOTE_EVENTS_PORT_SECURE_OFFSET     0x0144                  /* 
EVENTS_PORT.SECURE */
+#define NRF54L_GPIOTE_PUBLISH_IN_OFFSET(x)          (0x0180 + (0x04 * (x))) /* 
PUBLISH_IN[x] */
+#define NRF54L_GPIOTE_PUBLISH_PORT_NONSECURE_OFFSET 0x01c0                  /* 
PUBLISH_PORT.NONSECURE */
+#define NRF54L_GPIOTE_PUBLISH_PORT_SECURE_OFFSET    0x01c4                  /* 
PUBLISH_PORT.SECURE */
+#define NRF54L_GPIOTE_INTENSET0_OFFSET              0x0304                  /* 
INTENSET0 */
+#define NRF54L_GPIOTE_INTENCLR0_OFFSET              0x0308                  /* 
INTENCLR0 */
+#define NRF54L_GPIOTE_INTENSET1_OFFSET              0x0314                  /* 
INTENSET1 */
+#define NRF54L_GPIOTE_INTENCLR1_OFFSET              0x0318                  /* 
INTENCLR1 */
+#define NRF54L_GPIOTE_CONFIG_OFFSET(x)              (0x0510 + (0x04 * (x))) /* 
CONFIG[x] */
+
+/* Register bitfield definitions ********************************************/
+
+/* SUBSCRIBE and PUBLISH Registers */
+
+#define GPIOTE_SUBSCRIBE_CHIDX_SHIFT (0)
+#define GPIOTE_SUBSCRIBE_CHIDX_MASK  (0xff << GPIOTE_SUBSCRIBE_CHIDX_SHIFT)
+#define GPIOTE_SUBSCRIBE_EN          (1 << 31)
+#define GPIOTE_PUBLISH_CHIDX_SHIFT   (0)
+#define GPIOTE_PUBLISH_CHIDX_MASK    (0xff << GPIOTE_PUBLISH_CHIDX_SHIFT)
+#define GPIOTE_PUBLISH_EN            (1 << 31)
+
+/* EVENT_IN Register */
+
+#define GPIOTE_EVENT_IN_EVENT       (1 << 0) /* Bit 0: Event generated from 
pin */
+
+/* INTENSET/INTENCLR Register */
+
+#define GPIOTE_INT_IN_SHIFT         0    /* Bits 0-7: Enable interrupt for 
event IN[i] */
+
+#define GPIOTE_INT_IN_MASK          (0xff << GPIOTE_INT_IN_SHIFT)
+#  define GPIOTE_INT_IN(i)          ((1 << (i + GPIOTE_INT_IN_SHIFT)) & 
GPIOTE_INT_IN_MASK)
+
+#define GPIOTE_INT_PORT_NONSECURE   (1 << 16) /* Non-secure PORT event */
+#define GPIOTE_INT_PORT_SECURE      (1 << 17) /* Secure PORT event */
+
+/* CONFIG Register */
+
+#define GPIOTE_CONFIG_MODE_SHIFT    0    /* Bits 0-1: Mode */
+#define GPIOTE_CONFIG_MODE_MASK     (0x3 << GPIOTE_CONFIG_MODE_SHIFT)
+#  define GPIOTE_CONFIG_MODE_DIS    (0x0 << GPIOTE_CONFIG_MODE_SHIFT) /* 0: 
Disabled */
+#  define GPIOTE_CONFIG_MODE_EV     (0x1 << GPIOTE_CONFIG_MODE_SHIFT) /* 1: 
Event */
+#  define GPIOTE_CONFIG_MODE_TS     (0x3 << GPIOTE_CONFIG_MODE_SHIFT) /* 2: 
Task */
+
+#define GPIOTE_CONFIG_PSEL_SHIFT    (4)  /* Bits 4-8: GPIO number */
+#define GPIOTE_CONFIG_PSEL_MASK     (0x1f << GPIOTE_CONFIG_PSEL_SHIFT)
+#define GPIOTE_CONFIG_PORT_SHIFT    (9)  /* Bits 9-12: GPIO port */
+#define GPIOTE_CONFIG_PORT_MASK     (0xf << GPIOTE_CONFIG_PORT_SHIFT)
+#define GPIOTE_CONFIG_POL_SHIFT     (16) /* Bits 16-17: Polarity */
+#define GPIOTE_CONFIG_POL_MASK      (0x3 << GPIOTE_CONFIG_POL_SHIFT)
+#  define GPIOTE_CONFIG_POL_NONE    (0x0 << GPIOTE_CONFIG_POL_SHIFT) /* 0: 
None */
+#  define GPIOTE_CONFIG_POL_LTH     (0x1 << GPIOTE_CONFIG_POL_SHIFT) /* 1: 
LoToHi */
+#  define GPIOTE_CONFIG_POL_HTL     (0x2 << GPIOTE_CONFIG_POL_SHIFT) /* 2: 
HiToLo */
+#  define GPIOTE_CONFIG_POL_TG      (0x3 << GPIOTE_CONFIG_POL_SHIFT) /* 3: 
Toggle */
+
+#define GPIOTE_CONFIG_OUTINIT_SHIFT (20) /* Bit 20: Initial value */
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_GPIOTE_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_gpiote.c 
b/arch/arm/src/nrf54l/nrf54l_gpiote.c
new file mode 100644
index 00000000000..c7e6890929c
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_gpiote.c
@@ -0,0 +1,704 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_gpiote.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 <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+#include <string.h>
+
+#include <arch/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "nrf54l_gpio.h"
+#include "nrf54l_gpiote.h"
+
+#include "hardware/nrf54l_gpiote.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* GPIOTE20 serves P1 and P3 with 8 channels, GPIOTE30 serves P0 with
+ * 4 channels. Channels 0-7 map to GPIOTE20 and 8-11 to GPIOTE30.
+ */
+
+#define GPIOTE_PER_CHANNEL     (8)
+#define GPIOTE_CHANNELS        (12)
+#define GPIOTE_NCHANNELS(inst) ((inst) == 0 ? 8 : 4)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_gpiote_callback_s
+{
+  xcpt_t    callback;
+  void     *arg;
+  uint32_t  pinset;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Callbacks attached to each GPIOTE channel */
+
+static struct nrf54l_gpiote_callback_s
+g_gpiote_ch_callbacks[GPIOTE_CHANNELS];
+
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+/* Callbacks attached to each GPIO pin */
+
+static struct nrf54l_gpiote_callback_s
+g_gpiote_pin_callbacks[NRF54L_GPIO_NPORTS][32];
+#else
+/* Callback for the PORT event */
+
+static struct nrf54l_gpiote_callback_s
+g_gpiote_port_callback[NRF54L_GPIO_NPORTS];
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_putreg
+ *
+ * Description:
+ *   Put a 32-bit register value by offset for a given GPIOTE instance
+ *
+ ****************************************************************************/
+
+static inline void nrf54l_gpiote_putreg(int inst, uint32_t offset,
+                                       uint32_t value)
+{
+  if (inst == 1)
+    {
+      putreg32(value, NRF54L_GPIOTE30_BASE + offset);
+    }
+  else
+    {
+      putreg32(value, NRF54L_GPIOTE20_BASE + offset);
+    }
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_getreg
+ *
+ * Description:
+ *   Get a 32-bit register value by offset for a given GPIOTE instance
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf54l_gpiote_getreg(int inst, uint32_t offset)
+{
+  if (inst == 1)
+    {
+      return getreg32(NRF54L_GPIOTE30_BASE + offset);
+    }
+  else
+    {
+      return getreg32(NRF54L_GPIOTE20_BASE + offset);
+    }
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_inst
+ *
+ * Description:
+ *   Get the GPIOTE instance connected to the given GPIO port
+ *
+ ****************************************************************************/
+
+static inline int nrf54l_gpiote_inst(int port)
+{
+  return (port == 0) ? 1 : 0;
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_isr
+ *
+ * Description:
+ *   Common GPIOTE interrupt handler (GPIOTE20 and GPIOTE30)
+ *
+ ****************************************************************************/
+
+static int nrf54l_gpiote_isr(int irq, void *context, void *arg)
+{
+  uint32_t regval = 0;
+  int      ret    = OK;
+  int      i      = 0;
+  int      inst   = 0;
+  int      off    = 0;
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+  int      j      = 0;
+#endif
+
+  /* Get GPIOTE instance */
+
+  inst = (irq == NRF54L_IRQ_GPIOTE20_0) ? 0 : 1;
+
+  /* Scan all GPIOTE channels */
+
+  for (i = 0; i < GPIOTE_NCHANNELS(inst); i += 1)
+    {
+      off = i + GPIOTE_PER_CHANNEL * inst;
+
+      /* Only if callback is registered */
+
+      if (g_gpiote_ch_callbacks[off].callback != NULL)
+        {
+          /* Get input event register */
+
+          regval = nrf54l_gpiote_getreg(inst,
+                                       NRF54L_GPIOTE_EVENTS_IN_OFFSET(i));
+          if (regval == GPIOTE_EVENT_IN_EVENT)
+            {
+              /* Execute callback */
+
+              xcpt_t callback = g_gpiote_ch_callbacks[off].callback;
+              void *cbarg = g_gpiote_ch_callbacks[off].arg;
+
+              ret = callback(irq, context, cbarg);
+
+              /* Clear event */
+
+              nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_EVENTS_IN_OFFSET(i),
+                                   0);
+            }
+        }
+    }
+
+  /* Check for PORT event */
+
+  regval = nrf54l_gpiote_getreg(inst,
+                                NRF54L_GPIOTE_EVENTS_PORT_SECURE_OFFSET);
+  if (regval)
+    {
+      uint32_t addr = 0;
+
+      /* Ack PORT event */
+
+      nrf54l_gpiote_putreg(inst,
+                           NRF54L_GPIOTE_EVENTS_PORT_SECURE_OFFSET, 0);
+
+      /* For each GPIO port connected to this instance, get LATCH register */
+
+      for (i = 0; i < NRF54L_GPIO_NPORTS; i++)
+        {
+          if (nrf54l_gpiote_inst(i) != inst)
+            {
+              continue;
+            }
+
+          switch (i)
+            {
+              case 0:
+                addr = NRF54L_GPIO_P0_BASE + NRF54L_GPIO_LATCH_OFFSET;
+                break;
+
+              case 1:
+                addr = NRF54L_GPIO_P1_BASE + NRF54L_GPIO_LATCH_OFFSET;
+                break;
+
+              case 2:
+                addr = NRF54L_GPIO_P2_BASE + NRF54L_GPIO_LATCH_OFFSET;
+                break;
+
+#if NRF54L_GPIO_NPORTS > 3
+              case 3:
+                addr = NRF54L_GPIO_P3_BASE + NRF54L_GPIO_LATCH_OFFSET;
+                break;
+#endif
+            }
+
+          /* Retrieve LATCH register */
+
+          regval = getreg32(addr);
+
+          /* Clear LATCH register (this may set PORT again) */
+
+          putreg32(0xffffffff, addr);
+
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+          /* Check for pins with DETECT bit high in LATCH register
+           * and dispatch callback if set
+           */
+
+          for (j = 0; j < 32 && regval; j++)
+            {
+              if (regval & (1 << j) && g_gpiote_pin_callbacks[i][j].callback)
+                {
+                  /* Run callback */
+
+                  xcpt_t callback = g_gpiote_pin_callbacks[i][j].callback;
+                  void *cbarg = g_gpiote_pin_callbacks[i][j].arg;
+
+                  ret = callback(irq, context, cbarg);
+
+                  /* Mark bit is as "visited", we can stop looping sooner
+                   * this way
+                   */
+
+                  regval &= ~(1 << j);
+                }
+            }
+#else
+          if (g_gpiote_port_callback[i].callback)
+            {
+              xcpt_t callback = g_gpiote_port_callback[i].callback;
+              void *cbarg = g_gpiote_port_callback[i].arg;
+
+              ret = callback(irq, context, cbarg);
+            }
+#endif
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_pin_event
+ *
+ * Description:
+ *   Sets/clears a handler for a given pin for the GPIO PORT event. This
+ *   will mean edge-sensitive or level-sensitive according to GPIO detect
+ *   mode configuration for the port (see nrf54l_gpio_detectmode()). Pin
+ *   will be sensitive to high/low according to GPIO_SENSE_LOW/HIGH
+ *   (set via nrf54l_gpio_config()).
+ *
+ *   The passed handler will be invoked from the main ISR for the PORT
+ *   event and will take care of clearing the LATCH register.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_pin_event(uint32_t pinset, xcpt_t func, void *arg)
+{
+  int        pin    = 0;
+  int        port   = 0;
+  irqstate_t flags;
+
+  pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
+  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+
+  flags = enter_critical_section();
+
+  g_gpiote_pin_callbacks[port][pin].callback = func;
+  g_gpiote_pin_callbacks[port][pin].arg = arg;
+
+  leave_critical_section(flags);
+}
+#else
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_port_event
+ *
+ * Description:
+ *   Sets/clears the handler for the GPIO PORT event.
+ *
+ *   The passed handler will be invoked from the main ISR for the PORT
+ *   event and will take care of clearing the LATCH register.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO port will be extracted from this parameter
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_port_event(uint32_t pinset, xcpt_t func, void *arg)
+{
+  int        port   = 0;
+  int        inst   = 0;
+  irqstate_t flags;
+
+  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+  inst = nrf54l_gpiote_inst(port);
+
+  flags = enter_critical_section();
+
+  g_gpiote_port_callback[port].callback = func;
+  g_gpiote_port_callback[port].arg      = arg;
+
+  if (func)
+    {
+      /* Enable the ISR */
+
+      nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_INTENSET0_OFFSET,
+                           GPIOTE_INT_PORT_SECURE);
+    }
+  else
+    {
+      /* Check if we can disable the ISR */
+
+      int i;
+
+      for (i = 0; i < NRF54L_GPIO_NPORTS; i++)
+        {
+          if (nrf54l_gpiote_inst(i) == inst &&
+              g_gpiote_port_callback[i].callback)
+            {
+              break;
+            }
+        }
+
+      if (i == NRF54L_GPIO_NPORTS)
+        {
+          nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_INTENCLR0_OFFSET,
+                               GPIOTE_INT_PORT_SECURE);
+        }
+    }
+
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_ch_event
+ *
+ * Description:
+ *   Configures a GPIOTE channel in EVENT mode, assigns it to a given pin
+ *   and sets a handler for the corresponding channel events.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - channel:     GPIOTE channel used to capture events
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_ch_event(uint32_t pinset, int channel,
+                                bool risingedge, bool fallingedge,
+                                xcpt_t func, void *arg)
+{
+  int        pin    = 0;
+  int        port   = 0;
+  int        inst   = 0;
+  uint32_t   regval = 0;
+  uint32_t   rchan  = 0;
+  irqstate_t flags;
+
+  DEBUGASSERT(channel < GPIOTE_CHANNELS);
+
+  /* Get GPIOTE instance */
+
+  inst = (channel < GPIOTE_PER_CHANNEL) ? 0 : 1;
+  DEBUGASSERT(nrf54l_gpiote_inst(GPIO_PORT_DECODE(pinset)) == inst);
+
+  rchan = (inst == 1) ? (channel - GPIOTE_PER_CHANNEL) : channel;
+
+  /* NOTE: GPIOTE module has priority over GPIO module
+   *       so GPIO configuration will be ignored
+   */
+
+  flags = enter_critical_section();
+
+  if (func)
+    {
+      /* Select EVENT mode */
+
+      regval |= GPIOTE_CONFIG_MODE_EV;
+
+      /* Select GPIOTE pin */
+
+      pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
+      regval |= (pin << GPIOTE_CONFIG_PSEL_SHIFT);
+
+      port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+      regval |= (port << GPIOTE_CONFIG_PORT_SHIFT);
+
+      /* Select polarity */
+
+      if (risingedge == true && fallingedge == true)
+        {
+          regval |= GPIOTE_CONFIG_POL_TG;
+        }
+      else if (risingedge == true)
+        {
+          regval |= GPIOTE_CONFIG_POL_LTH;
+        }
+      else if (fallingedge == true)
+        {
+          regval |= GPIOTE_CONFIG_POL_HTL;
+        }
+
+      /* Enable callback for channel */
+
+      g_gpiote_ch_callbacks[channel].callback = func;
+      g_gpiote_ch_callbacks[channel].arg      = arg;
+
+      /* Enable interrupt for given event */
+
+      nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_INTENSET0_OFFSET,
+                           GPIOTE_INT_IN(rchan));
+    }
+  else
+    {
+      /* Leave register as zero (disabled mode) */
+
+      /* Disable interrupt for given event */
+
+      nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_INTENCLR0_OFFSET,
+                           GPIOTE_INT_IN(rchan));
+
+      /* Remove callback configuration */
+
+      g_gpiote_ch_callbacks[channel].callback = NULL;
+      g_gpiote_ch_callbacks[channel].arg      = NULL;
+    }
+
+  /* Write CONFIG register */
+
+  nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_CONFIG_OFFSET(rchan), regval);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_event
+ *
+ * Description:
+ *   Configures a GPIOTE channel in EVENT mode, assigns it to a given pin
+ *   and sets a handler for the first available GPIOTE channel.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure indicating the
+ *   nature of the failure.
+ *
+ ****************************************************************************/
+
+int nrf54l_gpiote_set_event(uint32_t pinset,
+                            bool risingedge, bool fallingedge,
+                            xcpt_t func, void *arg)
+{
+  irqstate_t flags;
+  int ret = -ENOMEM;
+  int inst;
+  int first;
+  int last;
+  int i = 0;
+
+  /* Only the channels of the instance connected to this port can be used */
+
+  inst  = nrf54l_gpiote_inst(GPIO_PORT_DECODE(pinset));
+  first = inst * GPIOTE_PER_CHANNEL;
+  last  = first + GPIOTE_NCHANNELS(inst);
+
+  flags = enter_critical_section();
+
+  /* Get channel already used by pinset */
+
+  for (i = first; i < last; i++)
+    {
+      if (g_gpiote_ch_callbacks[i].callback != NULL &&
+          g_gpiote_ch_callbacks[i].pinset == pinset)
+        {
+          break;
+        }
+    }
+
+  /* Otherwise get a free channel */
+
+  if (i == last && func != NULL)
+    {
+      for (i = first; i < last; i++)
+        {
+          if (g_gpiote_ch_callbacks[i].callback == NULL)
+            {
+              break;
+            }
+        }
+    }
+
+  if (i < last)
+    {
+      g_gpiote_ch_callbacks[i].pinset = pinset;
+
+      nrf54l_gpiote_set_ch_event(pinset, i,
+                                 risingedge, fallingedge,
+                                 func, arg);
+
+      /* Return the channel index */
+
+      ret = i;
+    }
+
+  leave_critical_section(flags);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_task
+ *
+ * Description:
+ *   Configure GPIO in TASK mode (to be controlled via tasks).
+ *   Note that a pin can only be either in TASK or EVENT mode (set by
+ *   nrf54l_gpiosetevent with event set to true). Also, once set to TASK
+ *   mode, pin control is only possible via tasks on the via
+ *   nrf54l_gpio_write and will automatically set the output mode.
+ *   Finally, a given pin should only be assigned to a given channel.
+ *
+ * Input Parameters:
+ *  - pinset:      gpio pin configuration (only port + pin is important here)
+ *  - channel:     the GPIOTE channel used to control the given pin
+ *  - output_high: set pin initially to output HIGH or LOW.
+ *  - outcfg:      configure pin behavior one OUT task is triggered
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_task(uint32_t pinset, int channel,
+                            bool output_high,
+                            enum nrf54l_gpiote_outcfg_e outcfg)
+{
+  uint32_t regval;
+  uint32_t rchan;
+  int pin;
+  int port;
+  int inst;
+
+  /* Get GPIOTE instance */
+
+  inst = (channel < GPIOTE_PER_CHANNEL) ? 0 : 1;
+  DEBUGASSERT(nrf54l_gpiote_inst(GPIO_PORT_DECODE(pinset)) == inst);
+
+  rchan = (inst == 1) ? (channel - GPIOTE_PER_CHANNEL) : channel;
+
+  /* Select GPIOTE pin */
+
+  pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
+  regval = (pin << GPIOTE_CONFIG_PSEL_SHIFT);
+
+  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+  regval |= (port << GPIOTE_CONFIG_PORT_SHIFT);
+
+  /* Select TASK mode */
+
+  regval |= GPIOTE_CONFIG_MODE_TS;
+
+  /* Select pin number */
+
+  regval |= (pin << GPIOTE_CONFIG_PSEL_SHIFT);
+
+  /* Select initial output */
+
+  if (output_high)
+    {
+      regval |= (1 << GPIOTE_CONFIG_OUTINIT_SHIFT);
+    }
+
+  /* Set polarity mode */
+
+  switch (outcfg)
+    {
+      case NRF54L_GPIOTE_SET:
+        regval |= GPIOTE_CONFIG_POL_LTH;
+        break;
+      case NRF54L_GPIOTE_CLEAR:
+        regval |= GPIOTE_CONFIG_POL_HTL;
+        break;
+      case NRF54L_GPIOTE_TOGGLE:
+        regval |= GPIOTE_CONFIG_POL_TG;
+        break;
+    }
+
+  /* Write register */
+
+  nrf54l_gpiote_putreg(inst, NRF54L_GPIOTE_CONFIG_OFFSET(rchan), regval);
+}
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_init
+ *
+ * Description:
+ *   Initialize GPIOTE
+ *
+ ****************************************************************************/
+
+int nrf54l_gpiote_init(void)
+{
+  /* Clear LATCH register(s) */
+
+  putreg32(0xffffffff, NRF54L_GPIO_P0_BASE + NRF54L_GPIO_LATCH_OFFSET);
+  putreg32(0xffffffff, NRF54L_GPIO_P1_BASE + NRF54L_GPIO_LATCH_OFFSET);
+  putreg32(0xffffffff, NRF54L_GPIO_P2_BASE + NRF54L_GPIO_LATCH_OFFSET);
+#if NRF54L_GPIO_NPORTS > 3
+  putreg32(0xffffffff, NRF54L_GPIO_P3_BASE + NRF54L_GPIO_LATCH_OFFSET);
+#endif
+
+  /* Reset GPIOTE data */
+
+  memset(&g_gpiote_ch_callbacks, 0, sizeof(g_gpiote_ch_callbacks));
+
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+  memset(&g_gpiote_pin_callbacks, 0, sizeof(g_gpiote_pin_callbacks));
+
+  /* Enable PORT event interrupt */
+
+  nrf54l_gpiote_putreg(0, NRF54L_GPIOTE_INTENSET0_OFFSET,
+                       GPIOTE_INT_PORT_SECURE);
+  nrf54l_gpiote_putreg(1, NRF54L_GPIOTE_INTENSET0_OFFSET,
+                       GPIOTE_INT_PORT_SECURE);
+#else
+  memset(&g_gpiote_port_callback, 0, sizeof(g_gpiote_port_callback));
+#endif
+
+  /* Attach GPIOTE interrupt handler */
+
+  irq_attach(NRF54L_IRQ_GPIOTE20_0, nrf54l_gpiote_isr, NULL);
+  up_enable_irq(NRF54L_IRQ_GPIOTE20_0);
+  irq_attach(NRF54L_IRQ_GPIOTE30_0, nrf54l_gpiote_isr, NULL);
+  up_enable_irq(NRF54L_IRQ_GPIOTE30_0);
+
+  return OK;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_gpiote.h 
b/arch/arm/src/nrf54l/nrf54l_gpiote.h
new file mode 100644
index 00000000000..1d514188ba4
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_gpiote.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_gpiote.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_NRF54L_NRF54L_GPIOTE_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_GPIOTE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/irq.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* GPIOTE OUT task configuration */
+
+enum nrf54l_gpiote_outcfg_e
+{
+  NRF54L_GPIOTE_SET    = 0,
+  NRF54L_GPIOTE_CLEAR  = 1,
+  NRF54L_GPIOTE_TOGGLE = 2,
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_ch_event
+ *
+ * Description:
+ *   Configures a GPIOTE channel in EVENT mode, assigns it to a given pin
+ *   and sets a handler for the corresponding channel events.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - channel:     GPIOTE channel used to capture events
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_ch_event(uint32_t pinset, int channel,
+                                bool risingedge, bool fallingedge,
+                                xcpt_t func, void *arg);
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_event
+ *
+ * Description:
+ *   Configures a GPIOTE channel in EVENT mode, assigns it to a given pin
+ *   and sets a handler for the first available GPIOTE channel.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure indicating the
+ *   nature of the failure.
+ *
+ ****************************************************************************/
+
+int nrf54l_gpiote_set_event(uint32_t pinset,
+                            bool risingedge, bool fallingedge,
+                            xcpt_t func, void *arg);
+
+#ifdef CONFIG_NRF54L_PER_PIN_INTERRUPTS
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_pin_event
+ *
+ * Description:
+ *   Sets/clears a handler for a given pin for the GPIO PORT event. This
+ *   will mean edge-sensitive or level-sensitive according to GPIO detect
+ *   mode configuration for the port (see nrf54l_gpio_detectmode()). Pin
+ *   will be sensitive to high/low according to GPIO_SENSE_LOW/HIGH
+ *   (set via nrf54l_gpio_config()).
+ *
+ *   The passed handler will be invoked from the main ISR for the PORT
+ *   event and will take care of clearing the LATCH register.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO pin configuration
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_pin_event(uint32_t pinset, xcpt_t func, void *arg);
+#else
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_port_event
+ *
+ * Description:
+ *   Sets/clears the handler for the GPIO PORT event.
+ *
+ *   The passed handler will be invoked from the main ISR for the PORT
+ *   event and will take care of clearing the LATCH register.
+ *
+ * Input Parameters:
+ *  - pinset:      GPIO port will be extracted from this parameter
+ *  - func:        When non-NULL, generate interrupt
+ *  - arg:         Argument passed to the interrupt callback
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_port_event(uint32_t pinset, xcpt_t func, void *arg);
+
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_set_task
+ *
+ * Description:
+ *   Configure GPIO in TASK mode (to be controlled via tasks).
+ *   Note that a pin can only be either in TASK or EVENT mode (set by
+ *   nrf54l_gpiosetevent with event set to true). Also, once set to TASK
+ *   mode, pin control is only possible via tasks on the via
+ *   nrf54l_gpio_write and will automatically set the output mode.
+ *   Finally, a given pin should only be assigned to a given channel.
+ *
+ * Input Parameters:
+ *  - pinset:      gpio pin configuration (only port + pin is important here)
+ *  - channel:     the GPIOTE channel used to control the given pin
+ *  - output_high: set pin initially to output HIGH or LOW.
+ *  - outcfg:      configure pin behavior one OUT task is triggered
+ *
+ ****************************************************************************/
+
+void nrf54l_gpiote_set_task(uint32_t pinset, int channel,
+                       bool output_high, enum nrf54l_gpiote_outcfg_e outcfg);
+
+/****************************************************************************
+ * Name: nrf54l_gpiote_init
+ *
+ * Description:
+ *   Initialize GPIOTE
+ *
+ ****************************************************************************/
+
+int nrf54l_gpiote_init(void);
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_GPIOTE_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_irq.c b/arch/arm/src/nrf54l/nrf54l_irq.c
index 54606350e50..d3a3567c40d 100644
--- a/arch/arm/src/nrf54l/nrf54l_irq.c
+++ b/arch/arm/src/nrf54l/nrf54l_irq.c
@@ -41,6 +41,10 @@
 #include "arm_internal.h"
 #include "nrf54l_irq.h"
 
+#ifdef CONFIG_NRF54L_GPIOTE
+#  include "nrf54l_gpiote.h"
+#endif
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
@@ -349,6 +353,12 @@ void up_irqinitialize(void)
 
   nrf54l_dumpnvic("initial", NRF54L_IRQ_NIRQS);
 
+#ifdef CONFIG_NRF54L_GPIOTE
+  /* Initialize GPIO interrupt support */
+
+  DEBUGVERIFY(nrf54l_gpiote_init());
+#endif
+
 #ifndef CONFIG_SUPPRESS_INTERRUPTS
   /* And finally, enable interrupts */
 

Reply via email to