From 36cc74e7e81a334dc076c8c745beff5953f9605e Mon Sep 17 00:00:00 2001 From: Daniel DeGrasse Date: Tue, 11 Apr 2023 14:57:03 -0500 Subject: [PATCH] drivers: gpio: gpio_mcux_lpc: add support for module interrupts On iMX.RT devices, the number of GPIO pins exceeds the maximum of 64 that the PINT interrupt controller can support. Therefore, two interrupt lines are now shared between the GPIO modules. This patch allows the user to set the interrupt source for a GPIO peripheral. For most LPC devices, this will always be the PINT. For some RT devices, the PINT cannot use pins on GPIO modules other than 0 and 1 as input, and thus the INTA and INTB sources should be used. Since Zephyr does not support sharing these interrupt between all GPIO controllers, the user must configure a subset of all GPIO controllers to use the shared module interrupts. An example of how to do so is provided for the RT595 EVK. Signed-off-by: Daniel DeGrasse --- .../arm/mimxrt595_evk/mimxrt595_evk_cm33.dts | 12 ++ drivers/gpio/gpio_mcux_lpc.c | 139 ++++++++++++++++-- dts/arm/nxp/nxp_lpc51u68.dtsi | 2 + dts/arm/nxp/nxp_lpc54xxx.dtsi | 2 + dts/arm/nxp/nxp_lpc55S0x_common.dtsi | 2 + dts/arm/nxp/nxp_lpc55S1x_common.dtsi | 2 + dts/arm/nxp/nxp_lpc55S2x_common.dtsi | 2 + dts/arm/nxp/nxp_lpc55S3x_common.dtsi | 2 + dts/arm/nxp/nxp_lpc55S6x_common.dtsi | 2 + dts/arm/nxp/nxp_rt5xx_common.dtsi | 2 + dts/arm/nxp/nxp_rt6xx_common.dtsi | 2 + dts/bindings/gpio/nxp,lpc-gpio.yaml | 15 ++ 12 files changed, 170 insertions(+), 14 deletions(-) diff --git a/boards/arm/mimxrt595_evk/mimxrt595_evk_cm33.dts b/boards/arm/mimxrt595_evk/mimxrt595_evk_cm33.dts index f21a14b12a..0ecd6aedbb 100644 --- a/boards/arm/mimxrt595_evk/mimxrt595_evk_cm33.dts +++ b/boards/arm/mimxrt595_evk/mimxrt595_evk_cm33.dts @@ -304,12 +304,24 @@ arduino_serial: &flexcomm12 { status = "okay"; }; +/* + * GPIO module interrupts are shared between all GPIO devices on this + * SOC, but Zephyr does not currently support sharing interrupts between + * devices. The user can select GPIO modules to support interrupts by + * setting the appropriate `int-source` and `interrupt` property for + * a given module. On this board, GPIO3 and GPIO4 are configured to support + * interrupts. + */ &gpio3 { status = "okay"; + int-source = "int-a"; + interrupts = <2 0>; }; &gpio4 { status = "okay"; + int-source = "int-b"; + interrupts = <3 0>; }; &gpio5 { diff --git a/drivers/gpio/gpio_mcux_lpc.c b/drivers/gpio/gpio_mcux_lpc.c index 303016b8f8..5558f7e2f7 100644 --- a/drivers/gpio/gpio_mcux_lpc.c +++ b/drivers/gpio/gpio_mcux_lpc.c @@ -25,10 +25,17 @@ #include #include +/* Interrupt sources, matching int-source enum in DTS binding definition */ +#define INT_SOURCE_PINT 0 +#define INT_SOURCE_INTA 1 +#define INT_SOURCE_INTB 2 +#define INT_SOURCE_NONE 3 + struct gpio_mcux_lpc_config { /* gpio_driver_config needs to be first */ struct gpio_driver_config common; GPIO_Type *gpio_base; + uint8_t int_source; #ifdef IOPCTL IOPCTL_Type *pinmux_base; #else @@ -207,26 +214,20 @@ static void gpio_mcux_lpc_pint_cb(uint8_t pin, void *user) } -static int gpio_mcux_lpc_pin_interrupt_configure(const struct device *dev, - gpio_pin_t pin, - enum gpio_int_mode mode, - enum gpio_int_trig trig) +/* Installs interrupt handler using PINT interrupt controller. */ +static int gpio_mcux_lpc_pint_interrupt_cfg(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) { const struct gpio_mcux_lpc_config *config = dev->config; enum nxp_pint_trigger interrupt_mode = NXP_PINT_NONE; - GPIO_Type *gpio_base = config->gpio_base; uint32_t port = config->port_no; int ret; - /* Ensure pin used as interrupt is set as input*/ - if ((mode & GPIO_INT_ENABLE) && - ((gpio_base->DIR[port] & BIT(pin)) != 0)) { - return -ENOTSUP; - } - switch (mode) { case GPIO_INT_MODE_DISABLED: - nxp_pint_pin_disable((config->port_no * 32) + pin); + nxp_pint_pin_disable((port * 32) + pin); return 0; case GPIO_INT_MODE_LEVEL: if (trig == GPIO_INT_TRIG_HIGH) { @@ -251,16 +252,112 @@ static int gpio_mcux_lpc_pin_interrupt_configure(const struct device *dev, } /* PINT treats GPIO pins as continuous. Each port has 32 pins */ - ret = nxp_pint_pin_enable((config->port_no * 32) + pin, interrupt_mode); + ret = nxp_pint_pin_enable((port * 32) + pin, interrupt_mode); if (ret < 0) { return ret; } /* Install callback */ - return nxp_pint_pin_set_callback((config->port_no * 32) + pin, + return nxp_pint_pin_set_callback((port * 32) + pin, gpio_mcux_lpc_pint_cb, (struct device *)dev); } + +#if (defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT) + +static int gpio_mcux_lpc_module_interrupt_cfg(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct gpio_mcux_lpc_config *config = dev->config; + gpio_interrupt_index_t int_idx; + gpio_interrupt_config_t pin_config; + + if (config->int_source == INT_SOURCE_NONE) { + return -ENOTSUP; + } + + /* Route interrupt to source A or B based on interrupt source */ + int_idx = (config->int_source == INT_SOURCE_INTA) ? + kGPIO_InterruptA : kGPIO_InterruptB; + + /* Disable interrupt if requested */ + if (mode == GPIO_INT_MODE_DISABLED) { + GPIO_PinDisableInterrupt(config->gpio_base, config->port_no, pin, int_idx); + return 0; + } + + /* Set pin interrupt level */ + if (mode == GPIO_INT_MODE_LEVEL) { + pin_config.mode = kGPIO_PinIntEnableLevel; + } else if (mode == GPIO_INT_MODE_EDGE) { + pin_config.mode = kGPIO_PinIntEnableEdge; + } else { + return -ENOTSUP; + } + + /* Set pin interrupt trigger */ + if (trig == GPIO_INT_TRIG_HIGH) { + pin_config.polarity = kGPIO_PinIntEnableHighOrRise; + } else if (trig == GPIO_INT_TRIG_LOW) { + pin_config.polarity = kGPIO_PinIntEnableLowOrFall; + } else { + return -ENOTSUP; + } + + /* Enable interrupt with new configuration */ + GPIO_SetPinInterruptConfig(config->gpio_base, config->port_no, pin, &pin_config); + GPIO_PinEnableInterrupt(config->gpio_base, config->port_no, pin, int_idx); + return 0; +} + +/* GPIO module interrupt handler */ +void gpio_mcux_lpc_module_isr(const struct device *dev) +{ + const struct gpio_mcux_lpc_config *config = dev->config; + struct gpio_mcux_lpc_data *data = dev->data; + uint32_t status; + + status = GPIO_PortGetInterruptStatus(config->gpio_base, + config->port_no, + config->int_source == INT_SOURCE_INTA ? + kGPIO_InterruptA : kGPIO_InterruptB); + GPIO_PortClearInterruptFlags(config->gpio_base, + config->port_no, + config->int_source == INT_SOURCE_INTA ? + kGPIO_InterruptA : kGPIO_InterruptB, + status); + gpio_fire_callbacks(&data->callbacks, dev, status); +} + +#endif /* FSL_FEATURE_GPIO_HAS_INTERRUPT */ + + +static int gpio_mcux_lpc_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct gpio_mcux_lpc_config *config = dev->config; + GPIO_Type *gpio_base = config->gpio_base; + uint32_t port = config->port_no; + + /* Ensure pin used as interrupt is set as input*/ + if ((mode & GPIO_INT_ENABLE) && + ((gpio_base->DIR[port] & BIT(pin)) != 0)) { + return -ENOTSUP; + } + if (config->int_source == INT_SOURCE_PINT) { + return gpio_mcux_lpc_pint_interrupt_cfg(dev, pin, mode, trig); + } +#if (defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT) + return gpio_mcux_lpc_module_interrupt_cfg(dev, pin, mode, trig); +#else + return -ENOTSUP; +#endif +} + static int gpio_mcux_lpc_manage_cb(const struct device *port, struct gpio_callback *callback, bool set) { @@ -297,6 +394,18 @@ static const clock_ip_name_t gpio_clock_names[] = GPIO_CLOCKS; #define PINMUX_BASE IOCON #endif +#define GPIO_MCUX_LPC_MODULE_IRQ_CONNECT(inst) \ + do { \ + IRQ_CONNECT(DT_INST_IRQ(inst, irq), \ + DT_INST_IRQ(inst, priority), \ + gpio_mcux_lpc_module_isr, DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ(inst, irq)); \ + } while (false) + +#define GPIO_MCUX_LPC_MODULE_IRQ(inst) \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(inst, 0), \ + (GPIO_MCUX_LPC_MODULE_IRQ_CONNECT(inst))) + #define GPIO_MCUX_LPC(n) \ static int lpc_gpio_init_##n(const struct device *dev); \ @@ -307,6 +416,7 @@ static const clock_ip_name_t gpio_clock_names[] = GPIO_CLOCKS; }, \ .gpio_base = GPIO, \ .pinmux_base = PINMUX_BASE, \ + .int_source = DT_INST_ENUM_IDX(n, int_source), \ .port_no = DT_INST_PROP(n, port), \ .clock_ip_name = gpio_clock_names[DT_INST_PROP(n, port)], \ }; \ @@ -322,6 +432,7 @@ static const clock_ip_name_t gpio_clock_names[] = GPIO_CLOCKS; static int lpc_gpio_init_##n(const struct device *dev) \ { \ gpio_mcux_lpc_init(dev); \ + GPIO_MCUX_LPC_MODULE_IRQ(n); \ \ return 0; \ } diff --git a/dts/arm/nxp/nxp_lpc51u68.dtsi b/dts/arm/nxp/nxp_lpc51u68.dtsi index 0c6e3265f5..15a5e0ba9b 100644 --- a/dts/arm/nxp/nxp_lpc51u68.dtsi +++ b/dts/arm/nxp/nxp_lpc51u68.dtsi @@ -57,6 +57,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x4008c000 0x2484>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -65,6 +66,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x4008C000 0x2484>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc54xxx.dtsi b/dts/arm/nxp/nxp_lpc54xxx.dtsi index d933c1280f..3655a21c62 100644 --- a/dts/arm/nxp/nxp_lpc54xxx.dtsi +++ b/dts/arm/nxp/nxp_lpc54xxx.dtsi @@ -112,6 +112,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x4008c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -120,6 +121,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x4008C000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc55S0x_common.dtsi b/dts/arm/nxp/nxp_lpc55S0x_common.dtsi index 96c6b04117..86a96e6e93 100644 --- a/dts/arm/nxp/nxp_lpc55S0x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S0x_common.dtsi @@ -116,6 +116,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -124,6 +125,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi index d05086fabb..d4343d5d8d 100644 --- a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi @@ -120,6 +120,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -128,6 +129,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc55S2x_common.dtsi b/dts/arm/nxp/nxp_lpc55S2x_common.dtsi index 95d36bf5a7..8696dac7d1 100644 --- a/dts/arm/nxp/nxp_lpc55S2x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S2x_common.dtsi @@ -127,6 +127,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -135,6 +136,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc55S3x_common.dtsi b/dts/arm/nxp/nxp_lpc55S3x_common.dtsi index 6a78150819..3e2c49e946 100644 --- a/dts/arm/nxp/nxp_lpc55S3x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S3x_common.dtsi @@ -123,6 +123,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x278c>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -131,6 +132,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x278c>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi index bcc4c6ae8a..2d24a7bf98 100644 --- a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi @@ -146,6 +146,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -154,6 +155,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x8c000 0x2488>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_rt5xx_common.dtsi b/dts/arm/nxp/nxp_rt5xx_common.dtsi index f320edd062..646e9434a3 100644 --- a/dts/arm/nxp/nxp_rt5xx_common.dtsi +++ b/dts/arm/nxp/nxp_rt5xx_common.dtsi @@ -98,6 +98,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x100000 0x1000>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -106,6 +107,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x100000 0x1000>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/arm/nxp/nxp_rt6xx_common.dtsi b/dts/arm/nxp/nxp_rt6xx_common.dtsi index 565d080e36..ca98bd2426 100644 --- a/dts/arm/nxp/nxp_rt6xx_common.dtsi +++ b/dts/arm/nxp/nxp_rt6xx_common.dtsi @@ -97,6 +97,7 @@ gpio0: gpio@0 { compatible = "nxp,lpc-gpio"; reg = <0x100000 0x1000>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <0>; @@ -105,6 +106,7 @@ gpio1: gpio@1 { compatible = "nxp,lpc-gpio"; reg = <0x100000 0x1000>; + int-source = "pint"; gpio-controller; #gpio-cells = <2>; port = <1>; diff --git a/dts/bindings/gpio/nxp,lpc-gpio.yaml b/dts/bindings/gpio/nxp,lpc-gpio.yaml index 0f38e0267a..52716cba7d 100644 --- a/dts/bindings/gpio/nxp,lpc-gpio.yaml +++ b/dts/bindings/gpio/nxp,lpc-gpio.yaml @@ -28,6 +28,21 @@ properties: - 6 - 7 + int-source: + type: string + default: "none" + enum: + - "pint" + - "int-a" + - "int-b" + - "none" + description: | + Interrupt source for the gpio port. For ports that can use the PINT + as an interrupt source for their pins (typically ports 0 and 1), + this can be set to PINT. Otherwise, the property should be set to "int-a" + or "int-b" if interrupt support is desired, and the appropriate IRQ number + should set for the device. + gpio-cells: - pin - flags