You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ra...@apache.org on 2020/11/09 19:23:38 UTC

[incubator-nuttx] branch master updated: nrf52 GPIO/GPIOTE: better expose pin interrupt capability

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f55a287  nrf52 GPIO/GPIOTE: better expose pin interrupt capability
f55a287 is described below

commit f55a2879ca94a8eda439d30e561884b446b8781d
Author: Matias N <ma...@protobits.dev>
AuthorDate: Sat Nov 7 17:12:47 2020 -0300

    nrf52 GPIO/GPIOTE: better expose pin interrupt capability
    
    This change improves upon current support for pin interrupts. Before,
    a pin interrupt was handled (with nrf52_gpiote_setevent) using one
    of the eight available GPIOTE channels. Moreover, it didn't event let
    the user specify which channel to use (simply tried to get a free one).
    Also, it was buggy since it did not consider unsetting the callback.
    
    Besides GPIOTE channels, there is another way to deal with pin interrupts.
    The GPIO peripheral is capable of generating a PORT event
    (for the whole GPIO port) depending on the pin SENSE configuration
    (HIGH or LOW, or NONE) and GPIO DETECTMODE register
    (latching or non-latching).
    
    This change then renames nrf52_gpiote_setevent into nrf52_gpiote_set_ch_event,
    maintaining functionality of original function, but now allows specifying
    channel (and correctly handles unsetting the callback). Then, a
    new nrf52_gpiote_set_pin_event is added, which allows to set a callback
    for a given pin. During initialization, interrupt for the PORT event is
    enabled and handled in such way that for each pin whose corresponding
    bit in LATCH register (indicates the result of pin SENSEing) the
    callback for this pin will be invoked. This mechanism means that
    every pin can get an ISR. It also avoids using GPIOTE channels for this
    purpose which carry higher current consumption.
    
    This new per-pin callback mechanism has some added memory requirement
    so it can be disabled and its default is dependant on DEFAULT_SMALL.
    When disabled, a callback for the PORT event can be set directly
    with nrf52_gpiote_set_port_event
    
    There was only one use of nrf52_gpio_setevent() which was migrated
    into nrf52_gpio_set_ch_event() passing channel zero.
---
 arch/arm/src/nrf52/Kconfig                      |  18 ++
 arch/arm/src/nrf52/hardware/nrf52_gpio.h        |  20 ++
 arch/arm/src/nrf52/hardware/nrf52_gpiote.h      |   3 +-
 arch/arm/src/nrf52/nrf52_gpio.c                 |  58 +++-
 arch/arm/src/nrf52/nrf52_gpio.h                 |   6 +
 arch/arm/src/nrf52/nrf52_gpiote.c               | 369 ++++++++++++++++++------
 arch/arm/src/nrf52/nrf52_gpiote.h               |  80 ++++-
 boards/arm/nrf52/nrf52840-dk/src/nrf52_sx127x.c |   2 +-
 8 files changed, 453 insertions(+), 103 deletions(-)

diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig
index b104a5e..3cdb768 100644
--- a/arch/arm/src/nrf52/Kconfig
+++ b/arch/arm/src/nrf52/Kconfig
@@ -386,6 +386,24 @@ config NRF52_PROGMEM
 
 menu "GPIO Interrupt Configuration"
 
+config NRF52_PER_PIN_INTERRUPTS
+	bool "Per-pin interrupt callbacks"
+	default n if DEFAULT_SMALL
+	default y if !DEFAULT_SMALL
+	depends on NRF52_GPIOTE
+	---help---
+		The GPIOTE peripheral supports a limited number of channels which can
+		be set to EVENT mode and thus generate interrupts on pin state changes.
+		Another mechanism offered by the GPIO/GPIOTE peripherals is the PORT
+		event. This event is generated from a signal shared by all pins in
+		the GPIO port.
+
+		This option enables the ability to set per-pin callbacks that will
+		be invoked from the main GPIOTE ISR when a PORT event is generated.
+		As this involves extra storage to store each callback, this option can
+		be disabled to save space. In such case, it is possible to set a callback
+		for the whole PORT event directly.
+
 endmenu # GPIO Interrupt Configuration
 
 menu "PWM configuration"
diff --git a/arch/arm/src/nrf52/hardware/nrf52_gpio.h b/arch/arm/src/nrf52/hardware/nrf52_gpio.h
index d14eaa4..57ab771 100644
--- a/arch/arm/src/nrf52/hardware/nrf52_gpio.h
+++ b/arch/arm/src/nrf52/hardware/nrf52_gpio.h
@@ -57,6 +57,8 @@
 #  define NRF52_GPIO_NPORTS         1
 #endif
 
+#define NRF52_GPIO_NPINS            32
+
 /* Register offsets *****************************************************************/
 
 #define NRF52_GPIO_OUT_OFFSET        0x0504 /* Write GPIO port */
@@ -95,6 +97,9 @@
 
 /* Register bit definitions *********************************************************/
 
+#define GPIO_DETECTMODE_DEFAULT         (0)
+#define GPIO_DETECTMODE_LDETECT         (1)
+
 #define GPIO_CNF_DIR                    (1 << 0) /* Bit 0: Pin direction */
 #define GPIO_CNF_INPUT                  (1 << 1) /* Bit 1: Input buffer disconnect */
 #define GPIO_CNF_PULL_SHIFT             (2)
@@ -102,5 +107,20 @@
 #  define GPIO_CNF_PULL_DISABLED        (0 << GPIO_CNF_PULL_SHIFT)
 #  define GPIO_CNF_PULL_DOWN            (1 << GPIO_CNF_PULL_SHIFT)
 #  define GPIO_CNF_PULL_UP              (3 << GPIO_CNF_PULL_SHIFT)
+#define GPIO_CNF_DRIVE_SHIFT            (8)
+#define GPIO_CNF_DRIVE_MASK             (0x7 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_S0S1           (0 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_H0S1           (1 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_S0H1           (2 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_H0H1           (3 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_D0S1           (4 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_D0H1           (5 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_S0D1           (6 << GPIO_CNF_DRIVE_SHIFT)
+#  define GPIO_CNF_DRIVE_H0D1           (7 << GPIO_CNF_DRIVE_SHIFT)
+#define GPIO_CNF_SENSE_SHIFT            (16)
+#define GPIO_CNF_SENSE_MASK             (0x3 << GPIO_CNF_SENSE_SHIFT)
+#  define GPIO_CNF_SENSE_DISABLED       (0 << GPIO_CNF_SENSE_SHIFT)
+#  define GPIO_CNF_SENSE_HIGH           (2 << GPIO_CNF_SENSE_SHIFT)
+#  define GPIO_CNF_SENSE_LOW            (3 << GPIO_CNF_SENSE_SHIFT)
 
 #endif /* __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_GPIO_H */
diff --git a/arch/arm/src/nrf52/hardware/nrf52_gpiote.h b/arch/arm/src/nrf52/hardware/nrf52_gpiote.h
index e4fb92b..37a8d20 100644
--- a/arch/arm/src/nrf52/hardware/nrf52_gpiote.h
+++ b/arch/arm/src/nrf52/hardware/nrf52_gpiote.h
@@ -67,7 +67,8 @@
 #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             31   /* Bit 31: Enable interrupt for event PORT */
+#define GPIOTE_INT_PORT_SHIFT       31   /* Bit 31: Enable interrupt for event PORT */
+#define GPIOTE_INT_PORT             (1 << GPIOTE_INT_PORT_SHIFT)
 
 /* CONFIG Register */
 
diff --git a/arch/arm/src/nrf52/nrf52_gpio.c b/arch/arm/src/nrf52/nrf52_gpio.c
index 3b63113..6aaff7f 100644
--- a/arch/arm/src/nrf52/nrf52_gpio.c
+++ b/arch/arm/src/nrf52/nrf52_gpio.c
@@ -144,16 +144,14 @@ static inline void nrf52_gpio_mode(nrf52_pinset_t cfgset,
   mode = cfgset & GPIO_MODE_MASK;
 
   regval = getreg32(offset);
-  regval &= GPIO_CNF_PULL_MASK;
+  regval &= ~GPIO_CNF_PULL_MASK;
 
   if (mode == GPIO_PULLUP)
     {
-      regval &= GPIO_CNF_PULL_MASK;
       regval |= GPIO_CNF_PULL_UP;
     }
   else if (mode == GPIO_PULLDOWN)
     {
-      regval &= GPIO_CNF_PULL_MASK;
       regval |= GPIO_CNF_PULL_DOWN;
     }
 
@@ -161,6 +159,40 @@ static inline void nrf52_gpio_mode(nrf52_pinset_t cfgset,
 }
 
 /****************************************************************************
+ * Name: nrf52_gpio_sense
+ *
+ * Description:
+ *   Set SENSE configuration for an input pin
+ *
+ ****************************************************************************/
+
+static inline void nrf52_gpio_sense(nrf52_pinset_t cfgset,
+                                    unsigned int port, unsigned int pin)
+{
+  uint32_t mode;
+  uint32_t regval;
+  uint32_t offset;
+
+  mode = cfgset & GPIO_SENSE_MASK;
+
+  offset = nrf52_gpio_regget(port, NRF52_GPIO_PIN_CNF_OFFSET(pin));
+  regval = getreg32(offset);
+
+  regval &= ~GPIO_CNF_SENSE_MASK;
+
+  if (mode == GPIO_SENSE_HIGH)
+    {
+      regval |= GPIO_CNF_SENSE_HIGH;
+    }
+  else
+    {
+      regval |= GPIO_CNF_SENSE_LOW;
+    }
+
+  putreg32(regval, offset);
+}
+
+/****************************************************************************
  * Public Functions
  ****************************************************************************/
 
@@ -204,7 +236,8 @@ int nrf52_gpio_config(nrf52_pinset_t cfgset)
       switch (cfgset & GPIO_FUNC_MASK)
         {
         case GPIO_INPUT:   /* GPIO input pin */
-          break;           /* Already configured */
+          nrf52_gpio_sense(cfgset, port, pin);
+          break;
 
         case GPIO_OUTPUT:  /* GPIO outpout pin */
           nrf52_gpio_output(cfgset, port, pin);
@@ -313,3 +346,20 @@ bool nrf52_gpio_read(nrf52_pinset_t pinset)
 
   return (regval >> pin) & 1UL;
 }
+
+/****************************************************************************
+ * Name: nrf52_gpio_detectmode
+ *
+ * Description:
+ *  Set DETECTMODE to either default or latched
+ *
+ ****************************************************************************/
+
+void nrf52_gpio_detectmode(int port, enum nrf52_gpio_detectmode_e mode)
+{
+  uint32_t offset = nrf52_gpio_regget(port, NRF52_GPIO_DETECTMODE_OFFSET);
+
+  putreg32(mode == NRF52_GPIO_DETECTMODE_DETECT ?
+           GPIO_DETECTMODE_DEFAULT :
+           GPIO_DETECTMODE_LDETECT, offset);
+}
diff --git a/arch/arm/src/nrf52/nrf52_gpio.h b/arch/arm/src/nrf52/nrf52_gpio.h
index 85fe0d0..31bd808 100644
--- a/arch/arm/src/nrf52/nrf52_gpio.h
+++ b/arch/arm/src/nrf52/nrf52_gpio.h
@@ -187,6 +187,12 @@
 
 typedef uint32_t nrf52_pinset_t;
 
+enum nrf52_gpio_detectmode_e
+{
+  NRF52_GPIO_DETECTMODE_DETECT,
+  NRF52_GPIO_DETECTMODE_LDETECT,
+};
+
 /************************************************************************************
  * Public Data
  ************************************************************************************/
diff --git a/arch/arm/src/nrf52/nrf52_gpiote.c b/arch/arm/src/nrf52/nrf52_gpiote.c
index 62cf57c..777b131 100644
--- a/arch/arm/src/nrf52/nrf52_gpiote.c
+++ b/arch/arm/src/nrf52/nrf52_gpiote.c
@@ -61,9 +61,21 @@ struct nrf52_gpiote_callback_s
  * Private Data
  ****************************************************************************/
 
-/* Interrupt handlers attached to each GPIOTE */
+/* Callbacks attached to each GPIOTE channel */
 
-static struct nrf52_gpiote_callback_s g_gpiote_callbacks[GPIOTE_CHANNELS];
+static struct nrf52_gpiote_callback_s g_gpiote_ch_callbacks[GPIOTE_CHANNELS];
+
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
+/* Callbacks attached to each GPIO pin */
+
+static struct nrf52_gpiote_callback_s
+    g_gpiote_pin_callbacks[NRF52_GPIO_NPORTS][NRF52_GPIO_NPINS];
+#else
+/* Callback for the PORT event */
+
+static struct nrf52_gpiote_callback_s
+    g_gpiote_port_callback[NRF52_GPIO_NPORTS];
+#endif
 
 /****************************************************************************
  * Private Functions
@@ -108,6 +120,9 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
   uint32_t regval = 0;
   int      ret    = OK;
   int      i      = 0;
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
+  int      j      = 0;
+#endif
 
   /* Scan all GPIOTE channels */
 
@@ -115,7 +130,7 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
     {
       /* Only if callback is registered */
 
-      if (g_gpiote_callbacks[i].callback != NULL)
+      if (g_gpiote_ch_callbacks[i].callback != NULL)
         {
           /* Get input event register */
 
@@ -124,8 +139,8 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
             {
               /* Execute callback */
 
-              xcpt_t callback = g_gpiote_callbacks[i].callback;
-              FAR void *cbarg = g_gpiote_callbacks[i].arg;
+              xcpt_t callback = g_gpiote_ch_callbacks[i].callback;
+              FAR void *cbarg = g_gpiote_ch_callbacks[i].arg;
               ret = callback(irq, context, cbarg);
 
               /* Clear event */
@@ -135,6 +150,76 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
         }
     }
 
+  /* Check for PORT event */
+
+  regval = nrf52_gpiote_getreg(NRF52_GPIOTE_EVENTS_PORT_OFFSET);
+  if (regval)
+    {
+      uint32_t addr = 0;
+
+      /* Ack PORT event */
+
+      nrf52_gpiote_putreg(NRF52_GPIOTE_EVENTS_PORT_OFFSET, 0);
+
+      /* For each GPIO port, get LATCH register */
+
+      for (i = 0; i < NRF52_GPIO_NPORTS; i++)
+        {
+          switch (i)
+            {
+              case 0:
+                addr = NRF52_GPIO_P0_BASE + NRF52_GPIO_LATCH_OFFSET;
+              break;
+#ifdef CONFIG_NRF52_HAVE_PORT1
+              case 1:
+                addr = NRF52_GPIO_P1_BASE + NRF52_GPIO_LATCH_OFFSET;
+              break;
+#endif
+            }
+
+          /* Retrieve LATCH register */
+
+          regval = getreg32(addr);
+
+          /* Clear LATCH register (this may set PORT again) */
+
+          putreg32(0xffffffff, addr);
+
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
+          /* Check for pins with DETECT bit high in LATCH register
+           * and dispatch callback if set
+           */
+
+          for (j = 0; j < NRF52_GPIO_NPINS && 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;
+                  FAR 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;
+              FAR void *cbarg = g_gpiote_port_callback[i].arg;
+
+              ret = callback(irq, context, cbarg);
+            }
+#endif
+       }
+    }
+
   return ret;
 }
 
@@ -142,17 +227,22 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
  * Public Functions
  ****************************************************************************/
 
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
 /****************************************************************************
- * Name: nrf52_gpiosetevent
+ * Name: nrf52_gpiote_set_pin_event
  *
  * Description:
- *   Sets/clears GPIO based event and interrupt triggers.
+ *   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 nrf52_gpio_detectmode()). Pin
+ *   will be sensitive to high/low according to GPIO_SENSE_LOW/HIGH
+ *   (set via nrf52_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
- *  - risingedge:  Enables interrupt on rising edges
- *  - fallingedge: Enables interrupt on falling edges
- *  - event:       Generate event when set
  *  - func:        When non-NULL, generate interrupt
  *  - arg:         Argument passed to the interrupt callback
  *
@@ -162,122 +252,200 @@ static int nrf52_gpiote_isr(int irq, FAR void *context, FAR void *arg)
  *
  ****************************************************************************/
 
-int nrf52_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
-                       bool event, xcpt_t func, FAR void *arg)
+void nrf52_gpiote_set_pin_event(uint32_t pinset, xcpt_t func, FAR void *arg)
 {
-  int        ret    = OK;
-  int        i      = 0;
   int        pin    = 0;
-#ifdef CONFIG_NRF52_HAVE_PORT1
   int        port   = 0;
+  irqstate_t flags;
+
+  pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
+#ifdef CONFIG_NRF52_HAVE_PORT1
+  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
 #endif
-  uint32_t   regval = 0;
-  bool       found  = false;
+
+  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: nrf52_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
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure indicating the
+ *   nature of the failure.
+ *
+ ****************************************************************************/
+
+void nrf52_gpiote_set_port_event(uint32_t pinset, xcpt_t func, FAR void *arg)
+{
+  int        port   = 0;
   irqstate_t flags;
 
-  /* Find available GPIOTE channel */
+#ifdef CONFIG_NRF52_HAVE_PORT1
+  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+#endif
 
   flags = enter_critical_section();
 
-  for (i = 0; i < GPIOTE_CHANNELS; i += 1)
+  g_gpiote_port_callback[port].callback = func;
+  g_gpiote_port_callback[port].arg      = arg;
+
+  if (func)
+    {
+      /* Enable the ISR */
+
+      nrf52_gpiote_putreg(NRF52_GPIOTE_INTENSET_OFFSET, GPIOTE_INT_PORT);
+    }
+  else
     {
-      if (g_gpiote_callbacks[i].callback == NULL)
+#if NRF52_GPIO_NPORTS > 1
+      /* Check if we can disable the ISR */
+
+      int i;
+
+      for (i = 0; i < NRF52_GPIO_NPORTS; i++)
+        {
+          if (g_gpiote_port_callback[port].callback)
+            {
+              break;
+            }
+        }
+
+      if (i == NRF52_GPIO_NPORTS)
         {
-          found = true;
-          break;
+          nrf52_gpiote_putreg(NRF52_GPIOTE_INTENCLR_OFFSET, GPIOTE_INT_PORT);
         }
+#else
+      /* Disable the ISR */
+
+      nrf52_gpiote_putreg(NRF52_GPIOTE_INTENCLR_OFFSET, GPIOTE_INT_PORT);
+#endif
     }
 
   leave_critical_section(flags);
+}
+#endif
 
-  /* Return error if there is no free channel */
+/****************************************************************************
+ * Name: nrf52_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
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - event:       Generate event when set
+ *  - 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.
+ *
+ ****************************************************************************/
 
-  if (found == false)
-    {
-      ret = -ENODEV;
-      goto errout;
-    }
+void nrf52_gpiote_set_ch_event(uint32_t pinset, int channel,
+                               bool risingedge, bool fallingedge,
+                               xcpt_t func, FAR void *arg)
+{
+  int        pin    = 0;
+#ifdef CONFIG_NRF52_HAVE_PORT1
+  int        port   = 0;
+#endif
+  uint32_t   regval = 0;
+  irqstate_t flags;
+
+  DEBUGASSERT(channel < GPIOTE_CHANNELS);
 
   /* NOTE: GPIOTE module has priority over GPIO module
    *       so GPIO configuration will be ignored
    */
 
-  /* Select GPIOTE pin */
-
-  pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
-  regval = (pin << GPIOTE_CONFIG_PSEL_SHIFT);
-
-#ifdef CONFIG_NRF52_HAVE_PORT1
-  port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
-  regval |= (port << GPIOTE_CONFIG_PORT_SHIFT);
-#endif
-
-  /* Select EVENT mode */
+  flags = enter_critical_section();
 
-  if (event || func)
+  if (func)
     {
-      regval &= ~GPIOTE_CONFIG_MODE_MASK;
+      /* Select EVENT mode */
+
       regval |= GPIOTE_CONFIG_MODE_EV;
-    }
 
-  /* Select polarity */
+      /* Select GPIOTE pin */
 
-  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;
-    }
+      pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
+      regval |= (pin << GPIOTE_CONFIG_PSEL_SHIFT);
 
-  /* Write CONFIG register */
+#ifdef CONFIG_NRF52_HAVE_PORT1
+      port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
+      regval |= (port << GPIOTE_CONFIG_PORT_SHIFT);
+#endif
 
-  nrf52_gpiote_putreg(NRF52_GPIOTE_CONFIG_OFFSET(i), regval);
+      /* 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 interrupt for given event */
+      /* Enable callback for channel */
 
-  nrf52_gpiote_putreg(NRF52_GPIOTE_INTENSET_OFFSET, GPIOTE_INT_IN(i));
+      g_gpiote_ch_callbacks[channel].callback = func;
+      g_gpiote_ch_callbacks[channel].arg      = arg;
 
-  /* Connect callback */
+      /* Enable interrupt for given event */
 
-  g_gpiote_callbacks[i].callback = func;
-  g_gpiote_callbacks[i].arg      = arg;
+      nrf52_gpiote_putreg(NRF52_GPIOTE_INTENSET_OFFSET,
+                          GPIOTE_INT_IN(channel));
+    }
+  else
+    {
+      /* Leave register as zero (disabled mode) */
 
-errout:
-  return ret;
-}
+      /* Disable interrupt for given event */
 
-/****************************************************************************
- * Name: nrf52_gpiote_init
- *
- * Description:
- *   Initialize GPIOTE
- *
- ****************************************************************************/
+      nrf52_gpiote_putreg(NRF52_GPIOTE_INTENCLR_OFFSET,
+                          GPIOTE_INT_IN(channel));
 
-int nrf52_gpiote_init(void)
-{
-  /* Reset GPIOTE data */
+      /* Remove callback configuration */
 
-  memset(&g_gpiote_callbacks,
-         0,
-         sizeof(struct nrf52_gpiote_callback_s)*GPIOTE_CHANNELS);
+      g_gpiote_ch_callbacks[channel].callback = NULL;
+      g_gpiote_ch_callbacks[channel].arg      = NULL;
+    }
 
-  /* Attach GPIOTE interrupt handler */
+  /* Write CONFIG register */
 
-  irq_attach(NRF52_IRQ_GPIOTE, nrf52_gpiote_isr, NULL);
-  up_enable_irq(NRF52_IRQ_GPIOTE);
+  nrf52_gpiote_putreg(NRF52_GPIOTE_CONFIG_OFFSET(channel), regval);
 
-  return OK;
+  leave_critical_section(flags);
 }
 
 /****************************************************************************
- * Name: nrf52_gpiotaskset
+ * Name: nrf52_gpio_set_task
  *
  * Description:
  *   Configure GPIO in TASK mode (to be controlled via tasks).
@@ -299,8 +467,9 @@ int nrf52_gpiote_init(void)
  *
  ****************************************************************************/
 
-int nrf52_gpiotaskset(uint32_t pinset, int channel,
-                       bool output_high, enum nrf52_gpiote_outcfg_e outcfg)
+void nrf52_gpiote_set_task(uint32_t pinset, int channel,
+                           bool output_high,
+                           enum nrf52_gpiote_outcfg_e outcfg)
 {
   uint32_t regval;
   int pin;
@@ -351,6 +520,36 @@ int nrf52_gpiotaskset(uint32_t pinset, int channel,
   /* Write register */
 
   nrf52_gpiote_putreg(NRF52_GPIOTE_CONFIG_OFFSET(channel), regval);
+}
+
+/****************************************************************************
+ * Name: nrf52_gpiote_init
+ *
+ * Description:
+ *   Initialize GPIOTE
+ *
+ ****************************************************************************/
+
+int nrf52_gpiote_init(void)
+{
+  /* Reset GPIOTE data */
+
+  memset(&g_gpiote_ch_callbacks, 0, sizeof(g_gpiote_ch_callbacks));
+
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
+  memset(&g_gpiote_pin_callbacks, 0, sizeof(g_gpiote_pin_callbacks));
+
+  /* Enable PORT event interrupt */
+
+  nrf52_gpiote_putreg(NRF52_GPIOTE_INTENSET_OFFSET, GPIOTE_INT_PORT);
+#else
+  memset(&g_gpiote_port_callback, 0, sizeof(g_gpiote_port_callback));
+#endif
+
+  /* Attach GPIOTE interrupt handler */
+
+  irq_attach(NRF52_IRQ_GPIOTE, nrf52_gpiote_isr, NULL);
+  up_enable_irq(NRF52_IRQ_GPIOTE);
 
   return OK;
 }
diff --git a/arch/arm/src/nrf52/nrf52_gpiote.h b/arch/arm/src/nrf52/nrf52_gpiote.h
index 1081121..75a7a8e 100644
--- a/arch/arm/src/nrf52/nrf52_gpiote.h
+++ b/arch/arm/src/nrf52/nrf52_gpiote.h
@@ -49,17 +49,19 @@ enum nrf52_gpiote_outcfg_e
  ****************************************************************************/
 
 /****************************************************************************
- * Name: nrf52_gpiosetevent
+ * Name: nrf52_gpiote_set_ch_event
  *
  * Description:
- *   Sets/clears GPIO based event and interrupt triggers.
+ *   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
- *  - rising/falling edge: enables
- *  - event:  generate event when set
- *  - func:   when non-NULL, generate interrupt
- *  - arg:    Argument passed to the interrupt callback
+ *  - pinset:      GPIO pin configuration
+ *  - risingedge:  Enables interrupt on rising edges
+ *  - fallingedge: Enables interrupt on falling edges
+ *  - event:       Generate event when set
+ *  - 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
@@ -67,11 +69,65 @@ enum nrf52_gpiote_outcfg_e
  *
  ****************************************************************************/
 
-int nrf52_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
-                       bool event, xcpt_t func, FAR void *arg);
+void nrf52_gpiote_set_ch_event(uint32_t pinset, int channel,
+                               bool risingedge, bool fallingedge,
+                               xcpt_t func, FAR void *arg);
 
+#ifdef CONFIG_NRF52_PER_PIN_INTERRUPTS
 /****************************************************************************
- * Name: nrf52_gpiotaskset
+ * Name: nrf52_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 nrf52_gpio_detectmode()). Pin
+ *   will be sensitive to high/low according to GPIO_SENSE_LOW/HIGH
+ *   (set via nrf52_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
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure indicating the
+ *   nature of the failure.
+ *
+ ****************************************************************************/
+
+void nrf52_gpiote_set_pin_event(uint32_t pinset, xcpt_t func, FAR void *arg);
+#else
+
+/****************************************************************************
+ * Name: nrf52_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
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure indicating the
+ *   nature of the failure.
+ *
+ ****************************************************************************/
+
+void nrf52_gpiote_set_port_event(uint32_t pinset, xcpt_t func,
+                                 FAR void *arg);
+
+#endif
+
+/****************************************************************************
+ * Name: nrf52_gpio_set_task
  *
  * Description:
  *   Configure GPIO in TASK mode (to be controlled via tasks).
@@ -93,8 +149,8 @@ int nrf52_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
  *
  ****************************************************************************/
 
-int nrf52_gpiotaskset(uint32_t pinset, int channel, bool output_high,
-                      enum nrf52_gpiote_outcfg_e outcfg);
+void nrf52_gpio_set_task(uint32_t pinset, int channel,
+                        bool output_high, enum nrf52_gpiote_outcfg_e outcfg);
 
 /****************************************************************************
  * Name: nrf52_gpiote_init
diff --git a/boards/arm/nrf52/nrf52840-dk/src/nrf52_sx127x.c b/boards/arm/nrf52/nrf52840-dk/src/nrf52_sx127x.c
index 61a640e..0625a8b 100644
--- a/boards/arm/nrf52/nrf52840-dk/src/nrf52_sx127x.c
+++ b/boards/arm/nrf52/nrf52840-dk/src/nrf52_sx127x.c
@@ -88,7 +88,7 @@ static int sx127x_irq0_attach(xcpt_t isr, FAR void *arg)
 
   /* IRQ on rising edge */
 
-  nrf52_gpiosetevent(GPIO_SX127X_DIO0, true, false, false, isr, arg);
+  nrf52_gpiote_set_ch_event(GPIO_SX127X_DIO0, 0, true, false, isr, arg);
   return OK;
 }