From e1d495be811d000f2840c045e6964a4f72c39a64 Mon Sep 17 00:00:00 2001 From: Chekhov Ma Date: Tue, 9 Jan 2024 14:52:51 +0800 Subject: [PATCH] driver: add new gpio driver "gpio_mcux_rgpio" Add RGPIO gpio driver. This driver is used for i.MX93 and i.MX8ULP. GPIO pinctrl, read/write and interrupt is supported. Runtime mmio configuration is enabled, so no need for region definition in mimx9/mmu_region.c Signed-off-by: Chekhov Ma --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.mcux_rgpio | 12 + drivers/gpio/gpio_mcux_rgpio.c | 308 ++++++++++++++++++++++++++ dts/bindings/gpio/nxp,imx-rgpio.yaml | 32 +++ soc/arm64/nxp_imx/mimx9/pinctrl_soc.h | 3 +- 6 files changed, 356 insertions(+), 2 deletions(-) create mode 100644 drivers/gpio/Kconfig.mcux_rgpio create mode 100644 drivers/gpio/gpio_mcux_rgpio.c create mode 100644 dts/bindings/gpio/nxp,imx-rgpio.yaml diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 56018e7744f..f3f00d79db6 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_MCP230XX gpio_mcp230xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_BD8LB600FS gpio_bd8lb600fs.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX gpio_mcux.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_IGPIO gpio_mcux_igpio.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_RGPIO gpio_mcux_rgpio.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_LPC gpio_mcux_lpc.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MMIO32 gpio_mmio32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_XEC gpio_mchp_xec.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6b6928f3971..a6c2417144d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -105,6 +105,8 @@ source "drivers/gpio/Kconfig.mcux" source "drivers/gpio/Kconfig.mcux_igpio" +source "drivers/gpio/Kconfig.mcux_rgpio" + source "drivers/gpio/Kconfig.mcux_lpc" source "drivers/gpio/Kconfig.mmio32" diff --git a/drivers/gpio/Kconfig.mcux_rgpio b/drivers/gpio/Kconfig.mcux_rgpio new file mode 100644 index 00000000000..6446137542a --- /dev/null +++ b/drivers/gpio/Kconfig.mcux_rgpio @@ -0,0 +1,12 @@ +# MCUX RGPIO configuration options + +# Copyright 2023, NXP +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_MCUX_RGPIO + bool "MCUX RGPIO driver" + default y + depends on DT_HAS_NXP_IMX_RGPIO_ENABLED + select PINCTRL + help + Enable the MCUX RGPIO driver. diff --git a/drivers/gpio/gpio_mcux_rgpio.c b/drivers/gpio/gpio_mcux_rgpio.c new file mode 100644 index 00000000000..16ad02c6ae8 --- /dev/null +++ b/drivers/gpio/gpio_mcux_rgpio.c @@ -0,0 +1,308 @@ +/* + * Copyright 2023, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_imx_rgpio + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct gpio_pin_gaps { + uint8_t start; + uint8_t len; +}; + +/* Required by DEVICE_MMIO_NAMED_* macros */ +#define DEV_CFG(_dev) \ + ((const struct mcux_rgpio_config *)(_dev)->config) +#define DEV_DATA(_dev) ((struct mcux_rgpio_data *)(_dev)->data) + +struct mcux_rgpio_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + + DEVICE_MMIO_NAMED_ROM(reg_base); + + const struct pinctrl_soc_pinmux *pin_muxes; + const struct gpio_pin_gaps *pin_gaps; + uint8_t mux_count; + uint8_t gap_count; +}; + +struct mcux_rgpio_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data general; + + DEVICE_MMIO_NAMED_RAM(reg_base); + + /* port ISR callback routine address */ + sys_slist_t callbacks; +}; + +static int mcux_rgpio_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + const struct mcux_rgpio_config *config = dev->config; + + struct pinctrl_soc_pin pin_cfg; + int cfg_idx = pin, i; + + /* Some SOCs have non-contiguous gpio pin layouts, account for this */ + for (i = 0; i < config->gap_count; i++) { + if (pin >= config->pin_gaps[i].start) { + if (pin < (config->pin_gaps[i].start + + config->pin_gaps[i].len)) { + /* Pin is not connected to a mux */ + return -ENOTSUP; + } + cfg_idx -= config->pin_gaps[i].len; + } + } + + /* Init pin configuration struct, and use pinctrl api to apply settings */ + if (cfg_idx >= config->mux_count) { + /* Pin is not connected to a mux */ + return -ENOTSUP; + } + + /* Set appropriate bits in pin configuration register */ + volatile uint32_t *gpio_cfg_reg = (volatile uint32_t *) + ((size_t)config->pin_muxes[cfg_idx].config_register); + uint32_t reg = *gpio_cfg_reg; + + /* TODO: Default flags, work for i.MX 9352 */ + if ((flags & GPIO_SINGLE_ENDED) != 0) { + /* Set ODE bit */ + reg |= (0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT); + } else { + reg &= ~(0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT); + } + if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) { + /* i.MX93 has no pull enable bit */ + if (((flags & GPIO_PULL_UP) != 0)) { + reg |= (0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT); + reg &= ~(0x1 << MCUX_IMX_BIAS_PULL_DOWN_SHIFT); + } else { + reg |= (0x1 << MCUX_IMX_BIAS_PULL_DOWN_SHIFT); + reg &= ~(0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT); + } + } else { + /* Set pin to highz */ + reg &= ~((0x1 << MCUX_IMX_BIAS_PULL_DOWN_SHIFT) | + (0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT)); + } + + memcpy(&pin_cfg.pinmux, &config->pin_muxes[cfg_idx], sizeof(pin_cfg)); + /* cfg register will be set by pinctrl_configure_pins */ + pin_cfg.pin_ctrl_flags = reg; + pinctrl_configure_pins(&pin_cfg, 1, PINCTRL_REG_NONE); + + if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) { + return -ENOTSUP; + } + + if (flags & GPIO_OUTPUT_INIT_HIGH) { + RGPIO_WritePinOutput(base, pin, 1); + } + + if (flags & GPIO_OUTPUT_INIT_LOW) { + RGPIO_WritePinOutput(base, pin, 0); + } + + WRITE_BIT(base->PDDR, pin, flags & GPIO_OUTPUT); + + return 0; +} + +static int mcux_rgpio_port_get_raw(const struct device *dev, uint32_t *value) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + + *value = base->PDIR; + + return 0; +} + +static int mcux_rgpio_port_set_masked_raw(const struct device *dev, + uint32_t mask, + uint32_t value) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + + base->PDOR = (base->PDOR & ~mask) | (mask & value); + + return 0; +} + +static int mcux_rgpio_port_set_bits_raw(const struct device *dev, + uint32_t mask) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + + RGPIO_PortSet(base, mask); + + return 0; +} + +static int mcux_rgpio_port_clear_bits_raw(const struct device *dev, + uint32_t mask) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + + RGPIO_PortClear(base, mask); + + return 0; +} + +static int mcux_rgpio_port_toggle_bits(const struct device *dev, + uint32_t mask) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + + RGPIO_PortToggle(base, mask); + + return 0; +} + +static int mcux_rgpio_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + unsigned int key; + uint8_t irqs, irqc; + + irqs = 0; /* only irq0 is used for irq */ + + if (mode == GPIO_INT_MODE_DISABLED) { + irqc = kRGPIO_InterruptOrDMADisabled; + } else if ((mode == GPIO_INT_MODE_EDGE) && + (trig == GPIO_INT_TRIG_LOW)) { + irqc = kRGPIO_InterruptFallingEdge; + } else if ((mode == GPIO_INT_MODE_EDGE) && + (trig == GPIO_INT_TRIG_HIGH)) { + irqc = kRGPIO_InterruptRisingEdge; + } else if ((mode == GPIO_INT_MODE_EDGE) && + (trig == GPIO_INT_TRIG_BOTH)) { + irqc = kRGPIO_InterruptEitherEdge; + } else if ((mode == GPIO_INT_MODE_LEVEL) && + (trig == GPIO_INT_TRIG_LOW)) { + irqc = kRGPIO_InterruptLogicZero; + } else if ((mode == GPIO_INT_MODE_LEVEL) && + (trig == GPIO_INT_TRIG_HIGH)) { + irqc = kRGPIO_InterruptLogicOne; + } else { + return -EINVAL; /* should never end up here */ + } + + key = irq_lock(); + RGPIO_SetPinInterruptConfig(base, pin, irqs, irqc); + irq_unlock(key); + + return 0; +} + +static int mcux_rgpio_manage_callback(const struct device *dev, + struct gpio_callback *callback, + bool set) +{ + struct mcux_rgpio_data *data = dev->data; + + return gpio_manage_callback(&data->callbacks, callback, set); +} + +static void mcux_rgpio_port_isr(const struct device *dev) +{ + RGPIO_Type *base = (RGPIO_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + struct mcux_rgpio_data *data = dev->data; + uint32_t int_flags; + + int_flags = base->ISFR[0]; /* Notice: only irq0 is used for now */ + base->ISFR[0] = int_flags; + + gpio_fire_callbacks(&data->callbacks, dev, int_flags); +} + +static const struct gpio_driver_api mcux_rgpio_driver_api = { + .pin_configure = mcux_rgpio_configure, + .port_get_raw = mcux_rgpio_port_get_raw, + .port_set_masked_raw = mcux_rgpio_port_set_masked_raw, + .port_set_bits_raw = mcux_rgpio_port_set_bits_raw, + .port_clear_bits_raw = mcux_rgpio_port_clear_bits_raw, + .port_toggle_bits = mcux_rgpio_port_toggle_bits, + .pin_interrupt_configure = mcux_rgpio_pin_interrupt_configure, + .manage_callback = mcux_rgpio_manage_callback, +}; + +/* These macros will declare an array of pinctrl_soc_pinmux types */ +#define PINMUX_INIT(node, prop, idx) MCUX_IMX_PINMUX(DT_PROP_BY_IDX(node, prop, idx)), +#define MCUX_RGPIO_PIN_DECLARE(n) \ + const struct pinctrl_soc_pinmux mcux_rgpio_pinmux_##n[] = { \ + DT_FOREACH_PROP_ELEM(DT_DRV_INST(n), pinmux, PINMUX_INIT) \ + }; \ + const uint8_t mcux_rgpio_pin_gaps_##n[] = \ + DT_INST_PROP_OR(n, gpio_reserved_ranges, {}); +#define MCUX_RGPIO_PIN_INIT(n) \ + .pin_muxes = mcux_rgpio_pinmux_##n, \ + .pin_gaps = (const struct gpio_pin_gaps *)mcux_rgpio_pin_gaps_##n, \ + .mux_count = DT_PROP_LEN(DT_DRV_INST(n), pinmux), \ + .gap_count = (ARRAY_SIZE(mcux_rgpio_pin_gaps_##n) / 2) + +#define MCUX_RGPIO_IRQ_INIT(n, i) \ + do { \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, i, irq), \ + DT_INST_IRQ_BY_IDX(n, i, priority), \ + mcux_rgpio_port_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQ_BY_IDX(n, i, irq)); \ + } while (false) + +#define MCUX_RGPIO_INIT(n) \ + MCUX_RGPIO_PIN_DECLARE(n) \ + static int mcux_rgpio_##n##_init(const struct device *dev); \ + \ + static const struct mcux_rgpio_config mcux_rgpio_##n##_config = {\ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\ + }, \ + DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)), \ + MCUX_RGPIO_PIN_INIT(n) \ + }; \ + \ + static struct mcux_rgpio_data mcux_rgpio_##n##_data; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + mcux_rgpio_##n##_init, \ + NULL, \ + &mcux_rgpio_##n##_data, \ + &mcux_rgpio_##n##_config, \ + POST_KERNEL, \ + CONFIG_GPIO_INIT_PRIORITY, \ + &mcux_rgpio_driver_api); \ + \ + static int mcux_rgpio_##n##_init(const struct device *dev) \ + { \ + DEVICE_MMIO_NAMED_MAP(dev, reg_base, \ + K_MEM_CACHE_NONE | K_MEM_DIRECT_MAP); \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), \ + (MCUX_RGPIO_IRQ_INIT(n, 0);)) \ + \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 1), \ + (MCUX_RGPIO_IRQ_INIT(n, 1);)) \ + \ + return 0; \ + } + +DT_INST_FOREACH_STATUS_OKAY(MCUX_RGPIO_INIT) diff --git a/dts/bindings/gpio/nxp,imx-rgpio.yaml b/dts/bindings/gpio/nxp,imx-rgpio.yaml new file mode 100644 index 00000000000..eaa4e08374e --- /dev/null +++ b/dts/bindings/gpio/nxp,imx-rgpio.yaml @@ -0,0 +1,32 @@ +# Copyright 2024, NXP +# SPDX-License-Identifier: Apache-2.0 + +description: i.MX RGPIO node + +compatible: "nxp,imx-rgpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + rdc: + type: int + description: Set the RDC permission for this peripheral + + pinmux: + type: phandles + description: | + IMX pin selection peripheral does not follow specific + pattern for which GPIO port uses which pinmux. Use this property to specify + pinctrl nodes to use for the gpio port when CONFIG_PINCTRL=y. Note that + the order of the nodes matters. The first node for gpio1 will be used + as the pinmux for gpio0, port 0. + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/soc/arm64/nxp_imx/mimx9/pinctrl_soc.h b/soc/arm64/nxp_imx/mimx9/pinctrl_soc.h index c3c66d532ec..bd9496ec582 100644 --- a/soc/arm64/nxp_imx/mimx9/pinctrl_soc.h +++ b/soc/arm64/nxp_imx/mimx9/pinctrl_soc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NXP + * Copyright (c) 2022-2023, NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,7 +19,6 @@ extern "C" { #define MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT IOMUXC1_SW_PAD_CTL_PAD_OD_SHIFT #define MCUX_IMX_BIAS_PULL_DOWN_SHIFT IOMUXC1_SW_PAD_CTL_PAD_PD_SHIFT #define MCUX_IMX_BIAS_PULL_UP_SHIFT IOMUXC1_SW_PAD_CTL_PAD_PU_SHIFT -#define MCUX_IMX_BIAS_PULL_ENABLE_SHIFT IOMUXC1_SW_PAD_CTL_PAD_PE_SHIFT #define MCUX_IMX_SLEW_RATE_SHIFT IOMUXC1_SW_PAD_CTL_PAD_FSEL1_SHIFT #define MCUX_IMX_DRIVE_STRENGTH_SHIFT IOMUXC1_SW_PAD_CTL_PAD_DSE_SHIFT #define MCUX_IMX_INPUT_ENABLE_SHIFT 23 /* Shift to a bit not used by IOMUXC_SW_PAD_CTL */