diff --git a/drivers/gpio/gpio_nxp_s32.c b/drivers/gpio/gpio_nxp_s32.c index c40b5691931..2d272cdea09 100644 --- a/drivers/gpio/gpio_nxp_s32.c +++ b/drivers/gpio/gpio_nxp_s32.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -18,7 +19,12 @@ LOG_MODULE_REGISTER(nxp_s32_gpio, CONFIG_GPIO_LOG_LEVEL); #ifdef CONFIG_NXP_S32_EIRQ #include +#endif +#ifdef CONFIG_NXP_S32_WKPU +#include +#endif +#if defined(CONFIG_NXP_S32_EIRQ) || defined(CONFIG_NXP_S32_WKPU) #define NXP_S32_GPIO_LINE_NOT_FOUND 0xff struct gpio_nxp_s32_irq_map { @@ -43,14 +49,20 @@ struct gpio_nxp_s32_config { #ifdef CONFIG_NXP_S32_EIRQ struct gpio_nxp_s32_irq_config *eirq_info; #endif +#ifdef CONFIG_NXP_S32_WKPU + struct gpio_nxp_s32_irq_config *wkpu_info; +#endif }; struct gpio_nxp_s32_data { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; -#ifdef CONFIG_NXP_S32_EIRQ +#if defined(CONFIG_NXP_S32_EIRQ) || defined(CONFIG_NXP_S32_WKPU) sys_slist_t callbacks; +#if defined(CONFIG_NXP_S32_WKPU) + uint32_t pin_wkpu_mask; +#endif /* defined(CONFIG_NXP_S32_WKPU) */ #endif }; @@ -66,6 +78,16 @@ static int nxp_s32_gpio_configure(const struct device *dev, gpio_pin_t pin, return -ENOTSUP; } +#if defined(CONFIG_NXP_S32_WKPU) + struct gpio_nxp_s32_data *data = dev->data; + + WRITE_BIT(data->pin_wkpu_mask, pin, (flags & NXP_S32_GPIO_INT_WKPU)); +#else + if (flags & NXP_S32_GPIO_INT_WKPU) { + return -ENOTSUP; + } +#endif + switch (flags & GPIO_DIR_MASK) { case GPIO_INPUT: Siul2_Port_Ip_SetPinDirection(port_base, pin, SIUL2_PORT_IN); @@ -160,7 +182,7 @@ static int nxp_s32_gpio_port_toggle_bits(const struct device *port, return 0; } -#ifdef CONFIG_NXP_S32_EIRQ +#if defined(CONFIG_NXP_S32_EIRQ) || defined(CONFIG_NXP_S32_WKPU) static uint8_t nxp_s32_gpio_pin_to_line(const struct gpio_nxp_s32_irq_config *irq_cfg, uint8_t pin) @@ -184,6 +206,7 @@ static void nxp_s32_gpio_isr(uint8_t pin, void *arg) gpio_fire_callbacks(&data->callbacks, dev, BIT(pin)); } +#if defined(CONFIG_NXP_S32_EIRQ) static int nxp_s32_gpio_eirq_get_trigger(Siul2_Icu_Ip_EdgeType *edge_type, enum gpio_int_mode mode, enum gpio_int_trig trigger) @@ -257,13 +280,100 @@ static int nxp_s32_gpio_config_eirq(const struct device *dev, return 0; } +#endif /* CONFIG_NXP_S32_EIRQ */ + +#if defined(CONFIG_NXP_S32_WKPU) +static int nxp_s32_gpio_wkpu_get_trigger(Wkpu_Ip_EdgeType *edge_type, + enum gpio_int_mode mode, + enum gpio_int_trig trigger) +{ + if (mode == GPIO_INT_MODE_DISABLED) { + *edge_type = WKPU_IP_NONE_EDGE; + return 0; + } + + if (mode == GPIO_INT_MODE_LEVEL) { + return -ENOTSUP; + } + + switch (trigger) { + case GPIO_INT_TRIG_LOW: + *edge_type = WKPU_IP_FALLING_EDGE; + break; + case GPIO_INT_TRIG_HIGH: + *edge_type = WKPU_IP_RISING_EDGE; + break; + case GPIO_INT_TRIG_BOTH: + *edge_type = WKPU_IP_BOTH_EDGES; + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int nxp_s32_gpio_config_wkpu(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct gpio_nxp_s32_config *config = dev->config; + const struct gpio_nxp_s32_irq_config *irq_cfg = config->wkpu_info; + uint8_t irq_line; + Wkpu_Ip_EdgeType edge_type; + + if (irq_cfg == NULL) { + LOG_ERR("WKPU controller not available or enabled"); + return -ENOTSUP; + } + + if (nxp_s32_gpio_wkpu_get_trigger(&edge_type, mode, trig)) { + LOG_ERR("trigger or mode not supported"); + return -ENOTSUP; + } + + irq_line = nxp_s32_gpio_pin_to_line(irq_cfg, pin); + if (irq_line == NXP_S32_GPIO_LINE_NOT_FOUND) { + if (edge_type == WKPU_IP_NONE_EDGE) { + return 0; + } + LOG_ERR("pin %d cannot be used for external interrupt", pin); + return -ENOTSUP; + } + + if (edge_type == WKPU_IP_NONE_EDGE) { + wkpu_nxp_s32_disable_interrupt(irq_cfg->ctrl, irq_line); + wkpu_nxp_s32_unset_callback(irq_cfg->ctrl, irq_line); + } else { + if (wkpu_nxp_s32_set_callback(irq_cfg->ctrl, irq_line, + nxp_s32_gpio_isr, pin, (void *)dev)) { + LOG_ERR("pin %d is already in use", pin); + return -EBUSY; + } + wkpu_nxp_s32_enable_interrupt(irq_cfg->ctrl, irq_line, edge_type); + } + + return 0; +} +#endif /* CONFIG_NXP_S32_WKPU */ static int nxp_s32_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { +#if defined(CONFIG_NXP_S32_WKPU) + struct gpio_nxp_s32_data *data = dev->data; + + if (data->pin_wkpu_mask & BIT(pin)) { + return nxp_s32_gpio_config_wkpu(dev, pin, mode, trig); + } +#endif + +#if defined(CONFIG_NXP_S32_EIRQ) return nxp_s32_gpio_config_eirq(dev, pin, mode, trig); +#endif } static int nxp_s32_gpio_manage_callback(const struct device *dev, @@ -273,27 +383,7 @@ static int nxp_s32_gpio_manage_callback(const struct device *dev, return gpio_manage_callback(&data->callbacks, cb, set); } - -static uint32_t nxp_s32_gpio_get_pending_int(const struct device *dev) -{ - const struct gpio_nxp_s32_config *config = dev->config; - const struct gpio_nxp_s32_irq_config *eirq_info = config->eirq_info; - - if (eirq_info == NULL) { - /* - * There is no external interrupt device for - * the GPIO port or exists but is not enabled - */ - return 0; - } - - /* - * Return all pending lines of the interrupt controller - * that GPIO port belongs to - */ - return eirq_nxp_s32_get_pending(eirq_info->ctrl); -} -#endif /* CONFIG_NXP_S32_EIRQ */ +#endif /* defined(CONFIG_NXP_S32_EIRQ) || defined(CONFIG_NXP_S32_WKPU) */ #ifdef CONFIG_GPIO_GET_CONFIG static int nxp_s32_gpio_pin_get_config(const struct device *dev, @@ -386,10 +476,9 @@ static const struct gpio_driver_api gpio_nxp_s32_driver_api = { .port_set_bits_raw = nxp_s32_gpio_port_set_bits_raw, .port_clear_bits_raw = nxp_s32_gpio_port_clear_bits_raw, .port_toggle_bits = nxp_s32_gpio_port_toggle_bits, -#ifdef CONFIG_NXP_S32_EIRQ +#if defined(CONFIG_NXP_S32_EIRQ) || defined(CONFIG_NXP_S32_WKPU) .pin_interrupt_configure = nxp_s32_gpio_pin_interrupt_configure, .manage_callback = nxp_s32_gpio_manage_callback, - .get_pending_int = nxp_s32_gpio_get_pending_int, #endif #ifdef CONFIG_GPIO_GET_CONFIG .pin_get_config = nxp_s32_gpio_pin_get_config, @@ -464,8 +553,36 @@ static const struct gpio_driver_api gpio_nxp_s32_driver_api = { #define GPIO_NXP_S32_GET_EIRQ_INFO(n) #endif /* CONFIG_NXP_S32_EIRQ */ +#ifdef CONFIG_NXP_S32_WKPU +#define GPIO_NXP_S32_WKPU_NODE(n) DT_INST_PHANDLE(n, nxp_wkpu) + +#define GPIO_NXP_S32_SET_WKPU_INFO(n) \ + BUILD_ASSERT((DT_INST_NODE_HAS_PROP(n, nxp_wkpu) == \ + DT_INST_NODE_HAS_PROP(n, nxp_wkpu_interrupts)), \ + "nxp,wkpu and nxp,wkpu-interrupts must be provided"); \ + IF_ENABLED(DT_NODE_HAS_STATUS(GPIO_NXP_S32_WKPU_NODE(n), okay), ( \ + static uint8_t gpio_nxp_s32_wkpu_data_##n[] = \ + DT_INST_PROP(n, nxp_wkpu_interrupts); \ + static struct gpio_nxp_s32_irq_config gpio_nxp_s32_wkpu_##n = { \ + .ctrl = DEVICE_DT_GET(GPIO_NXP_S32_WKPU_NODE(n)), \ + .map_cnt = sizeof(gpio_nxp_s32_wkpu_data_##n) / \ + sizeof(struct gpio_nxp_s32_irq_map), \ + .map = (struct gpio_nxp_s32_irq_map *) \ + gpio_nxp_s32_wkpu_data_##n, \ + }; \ + )) + +#define GPIO_NXP_S32_GET_WKPU_INFO(n) \ + .wkpu_info = UTIL_AND(DT_NODE_HAS_STATUS(GPIO_NXP_S32_WKPU_NODE(n), okay),\ + &gpio_nxp_s32_wkpu_##n) +#else +#define GPIO_NXP_S32_SET_WKPU_INFO(n) +#define GPIO_NXP_S32_GET_WKPU_INFO(n) +#endif /* CONFIG_NXP_S32_WKPU */ + #define GPIO_NXP_S32_DEVICE_INIT(n) \ GPIO_NXP_S32_SET_EIRQ_INFO(n) \ + GPIO_NXP_S32_SET_WKPU_INFO(n) \ static const struct gpio_nxp_s32_config gpio_nxp_s32_config_##n = { \ .common = { \ .port_pin_mask = GPIO_NXP_S32_PORT_PIN_MASK(n), \ @@ -473,6 +590,7 @@ static const struct gpio_driver_api gpio_nxp_s32_driver_api = { .gpio_base = GPIO_NXP_S32_REG_ADDR(n), \ .port_base = GPIO_NXP_S32_PORT_REG_ADDR(n), \ GPIO_NXP_S32_GET_EIRQ_INFO(n) \ + GPIO_NXP_S32_GET_WKPU_INFO(n) \ }; \ static struct gpio_nxp_s32_data gpio_nxp_s32_data_##n; \ static int gpio_nxp_s32_init_##n(const struct device *dev) \ diff --git a/dts/bindings/gpio/nxp,s32-gpio.yaml b/dts/bindings/gpio/nxp,s32-gpio.yaml index 3d19b61c9b3..5ebdb38979b 100644 --- a/dts/bindings/gpio/nxp,s32-gpio.yaml +++ b/dts/bindings/gpio/nxp,s32-gpio.yaml @@ -1,7 +1,39 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # SPDX-License-Identifier: Apache-2.0 -description: NXP S32 GPIO node +description: | + NXP S32 GPIO controller. + + The GPIO controller provides the option to route external input pad interrupts + to either the SIUL2 EIRQ interrupt controller or, when available on the SoC, + the WKPU interrupt controller. By default, GPIO interrupts are routed to the + SIUL2 EIRQ interrupt controller. + + To route external interrupts to the WKPU interrupt controller, the GPIO + specifier must be supplied with the flag `NXP_S32_GPIO_INT_WKPU`. For example, + the following snippet of devicetree source code instructs the GPIO controller + to route the interrupt from pin 9 of `gpioa` to the WKPU interrupt controller: + + #include + + &device { + gpios = <&gpioa 9 (NXP_S32_GPIO_INT_WKPU | GPIO_ACTIVE_HIGH)>; + }; + + Explicitly specifying the routing of a GPIO interrupt to a particular + interrupt controller allows for the allocation of distinct interrupt + priorities according to application-specific requirements. This is owing to + the fact that each interrupt controller features its own interrupt vector. + To illustrate, it is plausible to allocate the board's button interrupts to + the interrupt controller configured with a lower priority compared to the one + designated for the data-ready interrupt originating from a sensor. This + decision is justified by the potentially higher importance of the latter + interrupt to the overall system operation. + + The `NXP_S32_GPIO_INT_WKPU` flag is intended exclusively for specifying WKPU + as the interrupt controller for the corresponding GPIO. It's worth noting that + despite being named WKPU, the flag is not meant to configure GPIOs as wake-up + sources. compatible: "nxp,s32-gpio" @@ -20,6 +52,17 @@ properties: external interrupt signal, this is a list of GPIO pins and respective external interrupt lines (). + nxp,wkpu: + type: phandle + description: | + NXP WKPU controller associated to this GPIO port. + + nxp,wkpu-interrupts: + type: array + description: | + Map between WKPU external interrupt sources and pins of this GPIO port, + as in a tuple ``. + "#gpio-cells": const: 2 diff --git a/include/zephyr/dt-bindings/gpio/nxp-s32-gpio.h b/include/zephyr/dt-bindings/gpio/nxp-s32-gpio.h new file mode 100644 index 00000000000..ebd789e5d8b --- /dev/null +++ b/include/zephyr/dt-bindings/gpio/nxp-s32-gpio.h @@ -0,0 +1,39 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NXP_S32_GPIO_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NXP_S32_GPIO_H_ + +/** + * @brief NXP S32 GPIO specific flags + * + * The driver flags are encoded in the 8 upper bits of @ref gpio_dt_flags_t as + * follows: + * + * - Bit 8: Interrupt controller to which the respective GPIO interrupt is routed. + * + * @ingroup gpio_interface + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ +#define NXP_S32_GPIO_INT_CONTROLLER_POS 8 +#define NXP_S32_GPIO_INT_CONTROLLER_MASK (0x1U << NXP_S32_GPIO_INT_CONTROLLER_POS) +/** @endcond */ + +/** + * @name NXP S32 GPIO interrupt controller routing flags + * @brief NXP S32 GPIO interrupt controller routing flags + * @{ + */ + +/** Interrupt routed to the WKPU controller */ +#define NXP_S32_GPIO_INT_WKPU (0x1U << NXP_S32_GPIO_INT_CONTROLLER_POS) + +/** @} */ + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NXP_S32_GPIO_H_ */