gpio: nxp_s32: support passing external interrupts to WKPU

Extend the NXP S32 GPIO driver to be able to route external interrupts
to either SIUL2 EIRQ interrupt controller or, when available on the
SoC, WKPU interrupt controller.

Since WKPU can support up to 64 external interrupt sources and SIUL2
EIRQ up to 32, gpio_get_pending_int() is removed and the interrupt
controller specific API must be used instead.

Signed-off-by: Manuel Argüelles <manuel.arguelles@nxp.com>
This commit is contained in:
Manuel Argüelles 2023-09-11 14:47:48 +07:00 committed by Fabio Baltieri
parent ea08227dd0
commit a034cce23c
3 changed files with 227 additions and 27 deletions

View file

@ -9,6 +9,7 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_utils.h>
#include <zephyr/dt-bindings/gpio/nxp-s32-gpio.h>
#include <zephyr/logging/log.h>
#include <Siul2_Port_Ip.h>
@ -18,7 +19,12 @@ LOG_MODULE_REGISTER(nxp_s32_gpio, CONFIG_GPIO_LOG_LEVEL);
#ifdef CONFIG_NXP_S32_EIRQ
#include <zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h>
#endif
#ifdef CONFIG_NXP_S32_WKPU
#include <zephyr/drivers/interrupt_controller/intc_wkpu_nxp_s32.h>
#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) \

View file

@ -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 <zephyr/dt-bindings/gpio/nxp-s32-gpio.h>
&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 (<gpio-pin eirq-line>).
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-pin wkpu-interrupt-source>`.
"#gpio-cells":
const: 2

View file

@ -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_ */