From af450ea3cc9a325002c3c383813ab5b7c43ac843 Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Fri, 10 May 2024 12:10:06 +0800 Subject: [PATCH] drivers: gpio: add Broadcom iProc GPIO controller driver Add device driver, bindings and build-only test for Broadcom iProc GPIO controller. Signed-off-by: Yong Cong Sin --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 1 + drivers/gpio/Kconfig.iproc | 11 + drivers/gpio/gpio_iproc.c | 225 +++++++++++++++++++++ dts/bindings/gpio/brcm,iproc-gpio.yaml | 29 +++ tests/drivers/build_all/gpio/iproc.overlay | 35 ++++ tests/drivers/build_all/gpio/testcase.yaml | 5 + 7 files changed, 307 insertions(+) create mode 100644 drivers/gpio/Kconfig.iproc create mode 100644 drivers/gpio/gpio_iproc.c create mode 100644 dts/bindings/gpio/brcm,iproc-gpio.yaml create mode 100644 tests/drivers/build_all/gpio/iproc.overlay diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 787a10d7dd5..6e1cab6d248 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -32,6 +32,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_GECKO gpio_gecko.c) zephyr_library_sources_ifdef(CONFIG_GPIO_IMX gpio_imx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_INFINEON_CAT1 gpio_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_INTEL gpio_intel.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_IPROC gpio_iproc.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ITE_IT8XXX2 gpio_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ITE_IT8XXX2_V2 gpio_ite_it8xxx2_v2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_KSCAN_ITE_IT8XXX2 gpio_kscan_ite_it8xxx2.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9ce2b7f5859..f9126fef8d9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -124,6 +124,7 @@ source "drivers/gpio/Kconfig.gecko" source "drivers/gpio/Kconfig.ifx_cat1" source "drivers/gpio/Kconfig.imx" source "drivers/gpio/Kconfig.intel" +source "drivers/gpio/Kconfig.iproc" source "drivers/gpio/Kconfig.it8xxx2" source "drivers/gpio/Kconfig.litex" source "drivers/gpio/Kconfig.lmp90xxx" diff --git a/drivers/gpio/Kconfig.iproc b/drivers/gpio/Kconfig.iproc new file mode 100644 index 00000000000..8bec1c966d4 --- /dev/null +++ b/drivers/gpio/Kconfig.iproc @@ -0,0 +1,11 @@ +# Copyright 2020 Broadcom +# Copyright 2024 Meta +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_IPROC + bool "Broadcom iProc GPIO controller driver" + default y + depends on DT_HAS_BRCM_IPROC_GPIO_ENABLED + help + This option enables the GPIO driver for iProc family + of GPIO controller. diff --git a/drivers/gpio/gpio_iproc.c b/drivers/gpio/gpio_iproc.c new file mode 100644 index 00000000000..def1a07dd4e --- /dev/null +++ b/drivers/gpio/gpio_iproc.c @@ -0,0 +1,225 @@ +/* + * Copyright 2020 Broadcom + * Copyright 2024 Meta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT brcm_iproc_gpio + +#include +#include +#include +#include +#include +#include + +#define IPROC_GPIO_DATA_IN_OFFSET 0x00 +#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 +#define IPROC_GPIO_OUT_EN_OFFSET 0x08 +#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c +#define IPROC_GPIO_INT_DE_OFFSET 0x10 +#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 +#define IPROC_GPIO_INT_MSK_OFFSET 0x18 +#define IPROC_GPIO_INT_STAT_OFFSET 0x1c +#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 +#define IPROC_GPIO_INT_CLR_OFFSET 0x24 +#define IPROC_GPIO_PAD_RES_OFFSET 0x34 +#define IPROC_GPIO_RES_EN_OFFSET 0x38 + +struct gpio_iproc_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + mem_addr_t base; + void (*irq_config_func)(const struct device *dev); +}; + +struct gpio_iproc_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + sys_slist_t cb; +}; + +#define DEV_CFG(dev) ((const struct gpio_iproc_config *const)(dev)->config) +#define DEV_DATA(dev) ((struct gpio_iproc_data *const)(dev)->data) + +static int gpio_iproc_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + /* Setup the pin direcion. */ + if (flags & GPIO_OUTPUT) { + /* configure pin for output */ + sys_set_bit(base + IPROC_GPIO_OUT_EN_OFFSET, pin); + } else if (flags & GPIO_INPUT) { + /* configure pin for input */ + sys_clear_bit(base + IPROC_GPIO_OUT_EN_OFFSET, pin); + } + + return 0; +} + +static int gpio_iproc_port_get_raw(const struct device *dev, uint32_t *value) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + *value = sys_read32(base + IPROC_GPIO_DATA_IN_OFFSET); + + return 0; +} + +static int gpio_iproc_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET); + value = (value & (~mask)) | (value & mask); + sys_write32(base + IPROC_GPIO_DATA_OUT_OFFSET, value); + + return 0; +} + +static int gpio_iproc_port_set_bits_raw(const struct device *dev, uint32_t mask) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + sys_write32(base + IPROC_GPIO_DATA_OUT_OFFSET, mask); + + return 0; +} + +static int gpio_iproc_port_clear_bits_raw(const struct device *dev, uint32_t mask) +{ + uint32_t value; + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + /* Clear pins. */ + value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET); + value = (value & ~mask); + sys_write32(base + IPROC_GPIO_DATA_OUT_OFFSET, value); + + return 0; +} + +static int gpio_iproc_port_toggle_bits(const struct device *dev, uint32_t mask) +{ + uint32_t value; + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + /* toggles pins. */ + value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET); + value = (value ^ mask); + sys_write32(base + IPROC_GPIO_DATA_OUT_OFFSET, value); + + return 0; +} + +static int gpio_iproc_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + + /* check for interrupt configurations */ + if (mode & GPIO_INT_ENABLE) { + if (mode & GPIO_INT_EDGE) { + sys_clear_bit(base + IPROC_GPIO_INT_TYPE_OFFSET, pin); + } else { + sys_set_bit(base + IPROC_GPIO_INT_TYPE_OFFSET, pin); + } + + /* Generate interrupt of both falling/rising edge */ + if (trig & GPIO_INT_EDGE_BOTH) { + sys_set_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin); + } else if (trig & GPIO_INT_HIGH_1) { + /* Generate interrupt on rising edge */ + sys_clear_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin); + sys_set_bit(base + IPROC_GPIO_INT_EDGE_OFFSET, pin); + } else if (trig & GPIO_INT_LOW_0) { + /* Generate interrupt on falling edge */ + sys_clear_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin); + sys_clear_bit(base + IPROC_GPIO_INT_EDGE_OFFSET, pin); + } + + /* Unmask the interrupt */ + sys_clear_bit(base + IPROC_GPIO_INT_MSTAT_OFFSET, pin); + } else { + sys_set_bit(base + IPROC_GPIO_INT_MSK_OFFSET, pin); + } + return 0; +} + +static void gpio_iproc_isr(const struct device *dev) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + mem_addr_t base = cfg->base; + struct gpio_iproc_data *context = dev->data; + uint32_t int_stat; + + int_stat = sys_read32(base + IPROC_GPIO_INT_STAT_OFFSET); + + /* Clear the source of the interrupt */ + sys_write32(int_stat, base + IPROC_GPIO_INT_CLR_OFFSET); + + /* Handle the interrupt */ + gpio_fire_callbacks(&context->cb, dev, int_stat); +} + +static int gpio_iproc_manage_callback(const struct device *port, struct gpio_callback *callback, + bool set) +{ + struct gpio_iproc_data *context = port->data; + + return gpio_manage_callback(&context->cb, callback, set); +} + +static const struct gpio_driver_api gpio_iproc_api = { + .pin_configure = gpio_iproc_configure, + .port_get_raw = gpio_iproc_port_get_raw, + .port_set_masked_raw = gpio_iproc_port_set_masked_raw, + .port_set_bits_raw = gpio_iproc_port_set_bits_raw, + .port_clear_bits_raw = gpio_iproc_port_clear_bits_raw, + .port_toggle_bits = gpio_iproc_port_toggle_bits, + .pin_interrupt_configure = gpio_iproc_pin_interrupt_configure, + .manage_callback = gpio_iproc_manage_callback, +}; + +int gpio_iproc_init(const struct device *dev) +{ + const struct gpio_iproc_config *const cfg = DEV_CFG(dev); + + cfg->irq_config_func(dev); + + return 0; +} + +#define GPIO_IPROC_INIT(n) \ + static void port_iproc_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), gpio_iproc_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static const struct gpio_iproc_config gpio_port_config_##n = { \ + .common = \ + { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ + }, \ + .base = DT_INST_REG_ADDR(n), \ + .irq_config_func = port_iproc_config_func_##n, \ + }; \ + \ + static struct gpio_iproc_data gpio_port_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, gpio_iproc_init, NULL, &gpio_port_data_##n, \ + &gpio_port_config_##n, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_iproc_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_IPROC_INIT) diff --git a/dts/bindings/gpio/brcm,iproc-gpio.yaml b/dts/bindings/gpio/brcm,iproc-gpio.yaml new file mode 100644 index 00000000000..11543d6fe73 --- /dev/null +++ b/dts/bindings/gpio/brcm,iproc-gpio.yaml @@ -0,0 +1,29 @@ +# Copyright 2020 Broadcom +# SPDX-License-Identifier: Apache-2.0 + +description: Broadcom iProc GPIO Controller + +compatible: "brcm,iproc-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + description: | + Define the base and range of the I/O address space that contains SoC + GPIO/PINCONF controller registers + + ngpios: + required: true + description: Total number of in-use slots in GPIO controller + + interrupts: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/tests/drivers/build_all/gpio/iproc.overlay b/tests/drivers/build_all/gpio/iproc.overlay new file mode 100644 index 00000000000..b25d6ec64bf --- /dev/null +++ b/tests/drivers/build_all/gpio/iproc.overlay @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Meta Platforms + * + * SPDX-License-Identifier: Apache-2.0 + * + * Application overlay for testing iproc driver builds + */ + + / { + test { + test_int_gpio { + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&test_nvic>; + + test_nvic: interrupt-controller@bbbbcccc { + compatible = "arm,v6m-nvic"; + reg = <0xbbbbcccc 0xc00>; + interrupt-controller; + #interrupt-cells = <2>; + arm,num-irq-priority-bits = <3>; + }; + + test_gpio_iproc: gpio@c10fee { + compatible = "brcm,iproc-gpio"; + gpio-controller; + reg = <0xc10fee 0x4c>; + ngpios = <6>; + interrupts = <28 1>; + #gpio-cells = <0x2>; + status = "okay"; + }; + }; + }; +}; diff --git a/tests/drivers/build_all/gpio/testcase.yaml b/tests/drivers/build_all/gpio/testcase.yaml index b3f76141396..6b87243550d 100644 --- a/tests/drivers/build_all/gpio/testcase.yaml +++ b/tests/drivers/build_all/gpio/testcase.yaml @@ -34,3 +34,8 @@ tests: extra_args: - DTC_OVERLAY_FILE="app.overlay;adc_ads1145s0x_gpio.overlay" - CONF_FILE="adc_ads1145s0x_gpio.conf" + + drivers.gpio.build.iproc: + platform_allow: qemu_cortex_m3 + depends_on: gpio + extra_args: DTC_OVERLAY_FILE="iproc.overlay"