Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 102 additions & 27 deletions drivers/gpio/gpio_nrfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@
#define GPIO_HAS_PAD_GROUP 0
#endif

#define GPIOTE_PHANDLE(id) DT_INST_PHANDLE(id, gpiote_instance)
#define GPIOTE_PROP(idx, prop) DT_PROP(GPIOTE(idx), prop)

#define IS_NO_PORT_INSTANCE(id) DT_PROP_OR(GPIOTE_PHANDLE(id), no_port_event, 0) ||
#define IS_FIXED_CH_INSTANCE(id) DT_PROP_OR(GPIOTE_PHANDLE(id), fixed_channels_supported, 0) ||

#if DT_INST_FOREACH_STATUS_OKAY(IS_NO_PORT_INSTANCE) 0
#define GPIOTE_NO_PORT_EVT_SUPPORT 1
#endif

#if DT_INST_FOREACH_STATUS_OKAY(IS_FIXED_CH_INSTANCE) 0
#define GPIOTE_FIXED_CH_SUPPORT 1
#endif

#if defined(GPIOTE_NO_PORT_EVT_SUPPORT) || defined(GPIOTE_FIXED_CH_SUPPORT)
#define GPIOTE_FEATURE_FLAG 1
#define GPIOTE_FLAG_NO_PORT_EVT BIT(0)
#define GPIOTE_FLAG_FIXED_CHAN BIT(1)
#endif

struct gpio_nrfx_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
Expand All @@ -38,6 +58,9 @@ struct gpio_nrfx_cfg {
#if GPIO_HAS_PAD_GROUP
const struct device *pad_group;
#endif
#if defined(GPIOTE_FEATURE_FLAG)
uint32_t flags;
#endif
};

static inline struct gpio_nrfx_data *get_port_data(const struct device *port)
Expand Down Expand Up @@ -77,6 +100,7 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin,
nrfx_gpiote_pin_t abs_pin = NRF_GPIO_PIN_MAP(cfg->port_num, pin);
nrf_gpio_pin_pull_t pull = get_pull(flags);
nrf_gpio_pin_drive_t drive;
int pm_ret;

switch (flags & (NRF_GPIO_DRIVE_MSK | GPIO_OPEN_DRAIN)) {
case NRF_GPIO_DRIVE_S0S1:
Expand Down Expand Up @@ -156,6 +180,7 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin,
abs_pin, &input_pin_config);
if (err != NRFX_SUCCESS) {
ret = -EINVAL;

goto end;
}
}
Expand Down Expand Up @@ -187,12 +212,20 @@ static int gpio_nrfx_pin_configure(const struct device *port, gpio_pin_t pin,
}

if (IS_ENABLED(CONFIG_GPIO_NRFX_INTERRUPT) && free_ch) {
#ifdef GPIOTE_FEATURE_FLAG
/* Fixed channel was used, no need to free. */
if (cfg->flags & GPIOTE_FLAG_FIXED_CHAN) {
goto end;
}
#endif
err = nrfx_gpiote_channel_free(&cfg->gpiote, ch);
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
}

end:
return pm_device_runtime_put(port);
pm_ret = pm_device_runtime_put(port);

return (ret != 0) ? ret : pm_ret;
}

#ifdef CONFIG_GPIO_GET_CONFIG
Expand Down Expand Up @@ -360,6 +393,37 @@ static nrfx_gpiote_trigger_t get_trigger(enum gpio_int_mode mode,
NRFX_GPIOTE_TRIGGER_LOTOHI;
}

static nrfx_err_t chan_alloc(const struct gpio_nrfx_cfg *cfg, gpio_pin_t pin, uint8_t *ch)
{
#ifdef GPIOTE_FEATURE_FLAG
if (cfg->flags & GPIOTE_FLAG_FIXED_CHAN) {
/* Currently fixed channel relation is only present in one instance (GPIOTE0 on
* cpurad). The rules are following:
* - GPIOTE0 can only be used with P1 (pins 4-11) and P2 (pins (0-11))
* - P1: channel => pin - 4, e.g. P1.4 => channel 0, P1.5 => channel 1
* - P2: channel => pin % 8, e.g. P2.0 => channel 0, P2.8 => channel 0
*/
nrfx_err_t err = NRFX_SUCCESS;

if (cfg->port_num == 1) {
if (pin < 4) {
err = NRFX_ERROR_INVALID_PARAM;
} else {
*ch = pin - 4;
}
} else if (cfg->port_num == 2) {
*ch = pin & 0x7;
} else {
err = NRFX_ERROR_INVALID_PARAM;
}

return err;
}
#endif

return nrfx_gpiote_channel_alloc(&cfg->gpiote, ch);
}

static int gpio_nrfx_pin_interrupt_configure(const struct device *port,
gpio_pin_t pin,
enum gpio_int_mode mode,
Expand Down Expand Up @@ -395,14 +459,19 @@ static int gpio_nrfx_pin_interrupt_configure(const struct device *port,
(nrf_gpio_pin_dir_get(abs_pin) == NRF_GPIO_PIN_DIR_INPUT)) {
err = nrfx_gpiote_channel_get(&cfg->gpiote, abs_pin, &ch);
if (err == NRFX_ERROR_INVALID_PARAM) {
err = nrfx_gpiote_channel_alloc(&cfg->gpiote, &ch);
err = chan_alloc(cfg, pin, &ch);
if (err != NRFX_SUCCESS) {
return -ENOMEM;
}
}

trigger_config.p_in_channel = &ch;
} else {
#ifdef GPIOTE_FEATURE_FLAG
if (cfg->flags & GPIOTE_FLAG_NO_PORT_EVT) {
return -ENOTSUP;
}
#endif
/* If edge mode with channel was previously used and we are changing to sense or
* level triggered, we must free the channel.
*/
Expand Down Expand Up @@ -600,7 +669,6 @@ static DEVICE_API(gpio, gpio_nrfx_drv_api_funcs) = {
#endif
};

#define GPIOTE_PHANDLE(id) DT_INST_PHANDLE(id, gpiote_instance)
#define GPIOTE_INST(id) DT_PROP(GPIOTE_PHANDLE(id), instance)

#define GPIOTE_INSTANCE(id) \
Expand All @@ -626,30 +694,37 @@ static DEVICE_API(gpio, gpio_nrfx_drv_api_funcs) = {
#define GPIO_NRF_PAD_GROUP_INIT(id)
#endif

#define GPIO_NRF_DEVICE(id) \
GPIOTE_CHECK(id); \
static const struct gpio_nrfx_cfg gpio_nrfx_p##id##_cfg = { \
.common = { \
.port_pin_mask = \
GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \
}, \
.port = _CONCAT(NRF_P, DT_INST_PROP(id, port)), \
.port_num = DT_INST_PROP(id, port), \
.edge_sense = DT_INST_PROP_OR(id, sense_edge_mask, 0), \
.gpiote = GPIOTE_INSTANCE(id), \
GPIO_NRF_PAD_GROUP_INIT(id) \
}; \
\
static struct gpio_nrfx_data gpio_nrfx_p##id##_data; \
\
PM_DEVICE_DT_INST_DEFINE(id, gpio_nrfx_pm_hook); \
\
DEVICE_DT_INST_DEFINE(id, gpio_nrfx_init, \
PM_DEVICE_DT_INST_GET(id), \
&gpio_nrfx_p##id##_data, \
&gpio_nrfx_p##id##_cfg, \
PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, \
#define GPIO_NRF_DEVICE(id) \
GPIOTE_CHECK(id); \
static const struct gpio_nrfx_cfg gpio_nrfx_p##id##_cfg = { \
.common = { \
.port_pin_mask = \
GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \
}, \
.port = _CONCAT(NRF_P, DT_INST_PROP(id, port)), \
.port_num = DT_INST_PROP(id, port), \
.edge_sense = DT_INST_PROP_OR(id, sense_edge_mask, 0), \
.gpiote = GPIOTE_INSTANCE(id), \
GPIO_NRF_PAD_GROUP_INIT(id) \
IF_ENABLED(GPIOTE_FEATURE_FLAG, \
(.flags = \
(DT_PROP_OR(GPIOTE_PHANDLE(id), no_port_event, 0) ? \
GPIOTE_FLAG_NO_PORT_EVT : 0) | \
(DT_PROP_OR(GPIOTE_PHANDLE(id), fixed_channels_supported, 0) ? \
GPIOTE_FLAG_FIXED_CHAN : 0),) \
) \
}; \
\
static struct gpio_nrfx_data gpio_nrfx_p##id##_data; \
\
PM_DEVICE_DT_INST_DEFINE(id, gpio_nrfx_pm_hook); \
\
DEVICE_DT_INST_DEFINE(id, gpio_nrfx_init, \
PM_DEVICE_DT_INST_GET(id), \
&gpio_nrfx_p##id##_data, \
&gpio_nrfx_p##id##_cfg, \
PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, \
&gpio_nrfx_drv_api_funcs);

DT_INST_FOREACH_STATUS_OKAY(GPIO_NRF_DEVICE)
12 changes: 12 additions & 0 deletions dts/bindings/gpio/nordic,nrf-gpiote.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ compatible: "nordic,nrf-gpiote"
include:
- base.yaml
- nordic,split-channels.yaml
- pinctrl-device.yaml

properties:
reg:
Expand All @@ -16,6 +17,17 @@ properties:
interrupts:
required: true

no-port-event:
type: boolean
description: |
Indicates that the GPIOTE instance does not support PORT event.

fixed-channels-supported:
type: boolean
description: |
Indicates that the GPIOTE instance has fixed connection between pins and TE channels.
It means that a specific TE channel must be used for a given pin.

instance:
type: int
required: true
Expand Down
10 changes: 10 additions & 0 deletions dts/vendor/nordic/nrf54h20.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,16 @@
interrupts = <37 NRF_DEFAULT_IRQ_PRIORITY>;
};

gpiote0: gpiote@27000 {
compatible = "nordic,nrf-gpiote";
reg = <0x27000 0x1000>;
status = "disabled";
interrupts = <39 NRF_DEFAULT_IRQ_PRIORITY>;
instance = <0>;
no-port-event;
fixed-channels-supported;
};
Comment on lines +415 to +423
Copy link
Contributor

@jonathannilsen jonathannilsen Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this node causes this check in validate_base_addresses.c to fail for us, since in our case the MDK only defines NRF_RADIOCORE_GPIOTE and not NRF_GPIOTE or NRF_GPIOTE0:

CHECK_DT_REG(gpiote0, NRF_GPIOTE0);

Would it be possible to add something like

#if !defined(NRF_GPIOTE0) && defined(NRF_RADIOCORE_GPIOTE)
#define NRF_GPIOTE0 NRF_RADIOCORE_GPIOTE
#endif

to validate_base_addresses.c?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordic-krch - will you be able to address this comment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordic-krch is on vacation, I have pushed the suggestion


timer020: timer@28000 {
compatible = "nordic,nrf-timer";
reg = <0x28000 0x1000>;
Expand Down
2 changes: 1 addition & 1 deletion modules/hal_nordic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if(CONFIG_NRF_REGTOOL_GENERATE_UICR)
list(APPEND nrf_regtool_components GENERATE:UICR)
endif()
if(DEFINED nrf_regtool_components)
find_package(nrf-regtool 9.1.0
find_package(nrf-regtool 9.2.0
COMPONENTS ${nrf_regtool_components}
PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool
NO_CMAKE_PATH
Expand Down
4 changes: 4 additions & 0 deletions modules/hal_nordic/nrfx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ config NRFX_GPIOTE131
depends on $(dt_nodelabel_exists,gpiote131)
select NRFX_GPIOTE

config NRFX_GPIOTE_NONUNIFORM_INSTANCES
def_bool $(dt_nodelabel_enabled,gpiote0)
depends on $(dt_nodelabel_bool_prop,gpiote0,fixed-channels-supported)

config NRFX_GPIOTE_NUM_OF_EVT_HANDLERS
int "Number of event handlers"
depends on NRFX_GPIOTE
Expand Down
3 changes: 3 additions & 0 deletions modules/hal_nordic/nrfx/nrfx_kconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@
#ifdef CONFIG_NRFX_GPIOTE131
#define NRFX_GPIOTE131_ENABLED 1
#endif
#ifdef CONFIG_NRFX_GPIOTE_NONUNIFORM_INSTANCES
#define NRFX_GPIOTE_CONFIG_NONUNIFORM_INSTANCES 1
#endif

#ifdef CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS
#define NRFX_GPIOTE_CONFIG_NUM_OF_EVT_HANDLERS CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: Apache-2.0 */

&pinctrl {
gpiote0_default_alt: gpiote0_default_alt {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 1, 4)>;
};
};
};

/ {
resources {
compatible = "test-gpio-basic-api";
out-gpios = <&gpio1 5 0>;
in-gpios = <&gpio1 4 0>;
};
};

&gpio1 {
status = "okay";
gpiote-instance = <&gpiote0>;
};

&gpiote0 {
status = "okay";
pinctrl-0 = <&gpiote0_default_alt>;
pinctrl-names = "default";
};
7 changes: 5 additions & 2 deletions tests/drivers/gpio/gpio_basic_api/src/test_callback_manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,19 @@ static int init_callback(const struct device *dev_in, const struct device *dev_o
static void trigger_callback(const struct device *dev_in, const struct device *dev_out,
int enable_cb)
{
int rc;

gpio_pin_set(dev_out, PIN_OUT, 0);
k_sleep(K_MSEC(100));

cb_cnt[0] = 0;
cb_cnt[1] = 0;
if (enable_cb == 1) {
gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_EDGE_RISING);
rc = gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_EDGE_RISING);
} else {
gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_DISABLE);
rc = gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_DISABLE);
}
zassert_equal(rc, 0);
k_sleep(K_MSEC(100));
gpio_pin_set(dev_out, PIN_OUT, 1);
k_sleep(K_MSEC(1000));
Expand Down
6 changes: 6 additions & 0 deletions tests/drivers/gpio/gpio_basic_api/src/test_config_trigger.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ ZTEST(after_flash_gpio_config_trigger, test_gpio_config_twice_trigger)
k_sleep(K_MSEC(10));
zassert_between_inclusive(cb_cnt, 0, 1, "Got %d interrupts", cb_cnt);

ret = gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_DISABLE);
zassert_ok(ret, "interrupt disabling failed");

gpio_remove_callback(dev_in, &drv_data->gpio_cb);
}

Expand Down Expand Up @@ -125,5 +128,8 @@ ZTEST(after_flash_gpio_config_trigger, test_gpio_config_trigger)
k_sleep(K_MSEC(10));
zassert_between_inclusive(cb_cnt, 0, 1, "Got %d interrupts", cb_cnt);

ret = gpio_pin_interrupt_configure(dev_in, PIN_IN, GPIO_INT_DISABLE);
zassert_ok(ret, "interrupt disabling failed");

gpio_remove_callback(dev_in, &drv_data->gpio_cb);
}
7 changes: 7 additions & 0 deletions tests/drivers/gpio/gpio_basic_api/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,10 @@ tests:
platform_allow:
- siwx917_rb4338a
extra_args: "DTC_OVERLAY_FILE=boards/siwx917_rb4338a-uulp.overlay"
drivers.gpio.2pin.nrf54h20_cpurad_gpiote0:
harness_config:
fixture: i2s_loopback # Loopback includes the pin pair needed for that test
platform_allow:
- nrf54h20dk/nrf54h20/cpurad
extra_args:
- DTC_OVERLAY_FILE="boards/nrf54h20dk_nrf54h20_cpurad_gpiote0.overlay"