From d98229c352995d90421958378d2abd997f7d6945 Mon Sep 17 00:00:00 2001 From: Teoh Shi Lin Date: Tue, 22 Nov 2022 13:52:10 +0800 Subject: [PATCH] drivers: gpio: Add ALTERA PIO Add driver for altera avalon pio. Signed-off-by: Teoh Shi Lin --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.altera | 9 + drivers/gpio/gpio_altera_pio.c | 295 ++++++++++++++++++++ dts/bindings/gpio/altr,pio-1.0.yaml | 42 +++ tests/drivers/build_all/gpio/altera.overlay | 37 +++ tests/drivers/build_all/gpio/testcase.yaml | 6 + 7 files changed, 392 insertions(+) create mode 100644 drivers/gpio/Kconfig.altera create mode 100644 drivers/gpio/gpio_altera_pio.c create mode 100644 dts/bindings/gpio/altr,pio-1.0.yaml create mode 100644 tests/drivers/build_all/gpio/altera.overlay diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 60ea5c52fad..b168aa38a5e 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -83,6 +83,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NUMAKER gpio_numaker.c) zephyr_library_sources_ifdef(CONFIG_GPIO_EFINIX_SAPPHIRE gpio_efinix_sapphire.c) zephyr_library_sources_ifdef(CONFIG_GPIO_DAVINCI gpio_davinci.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SEDI gpio_sedi.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_ALTERA_PIO gpio_altera_pio.c) if (CONFIG_GPIO_EMUL_SDL) zephyr_library_sources(gpio_emul_sdl.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f0e1a63e364..b008da0bd32 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -206,4 +206,6 @@ source "drivers/gpio/Kconfig.efinix_sapphire" source "drivers/gpio/Kconfig.davinci" source "drivers/gpio/Kconfig.sedi" +source "drivers/gpio/Kconfig.altera" + endif # GPIO diff --git a/drivers/gpio/Kconfig.altera b/drivers/gpio/Kconfig.altera new file mode 100644 index 00000000000..2bb03287494 --- /dev/null +++ b/drivers/gpio/Kconfig.altera @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_ALTERA_PIO + bool "Altera PIO" + default y + depends on DT_HAS_ALTR_PIO_1_0_ENABLED + help + Enable driver for Altera PIO diff --git a/drivers/gpio/gpio_altera_pio.c b/drivers/gpio/gpio_altera_pio.c new file mode 100644 index 00000000000..be822adcfa7 --- /dev/null +++ b/drivers/gpio/gpio_altera_pio.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2023, Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT altr_pio_1_0 + +#include +#include +#include +#include + +#define ALTERA_AVALON_PIO_DATA_OFFSET 0x00 +#define ALTERA_AVALON_PIO_DIRECTION_OFFSET 0x04 +#define ALTERA_AVALON_PIO_IRQ_OFFSET 0x08 +#define ALTERA_AVALON_PIO_SET_BITS 0x10 +#define ALTERA_AVALON_PIO_CLEAR_BITS 0x14 + +typedef void (*altera_cfg_func_t)(void); + +struct gpio_altera_config { + struct gpio_driver_config common; + uintptr_t reg_base; + uint32_t irq_num; + uint8_t direction; + uint8_t outset; + uint8_t outclear; + altera_cfg_func_t cfg_func; +}; + +struct gpio_altera_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + /* list of callbacks */ + sys_slist_t cb; + struct k_spinlock lock; +}; + +static bool gpio_pin_direction(const struct device *dev, uint32_t pin_mask) +{ + const struct gpio_altera_config *cfg = dev->config; + const int direction = cfg->direction; + uintptr_t reg_base = cfg->reg_base; + uint32_t addr; + uint32_t pin_direction; + + if (pin_mask == 0) { + return -EINVAL; + } + + /* Check if the direction is Bidirectional */ + if (direction != 0) { + return -EINVAL; + } + + addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET; + + pin_direction = sys_read32(addr); + + if (!(pin_direction & pin_mask)) { + return false; + } + + return true; +} + +static int gpio_altera_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_altera_config *cfg = dev->config; + struct gpio_altera_data * const data = dev->data; + const int port_pin_mask = cfg->common.port_pin_mask; + const int direction = cfg->direction; + uintptr_t reg_base = cfg->reg_base; + k_spinlock_key_t key; + uint32_t addr; + + /* Check if pin number is within range */ + if ((port_pin_mask & BIT(pin)) == 0) { + return -EINVAL; + } + + /* Check if the direction is Bidirectional */ + if (direction != 0) { + return -EINVAL; + } + + addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET; + + key = k_spin_lock(&data->lock); + + if (flags == GPIO_INPUT) { + sys_clear_bits(addr, BIT(pin)); + } else if (flags == GPIO_OUTPUT) { + sys_set_bits(addr, BIT(pin)); + } else { + return -EINVAL; + } + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int gpio_altera_port_get_raw(const struct device *dev, uint32_t *value) +{ + const struct gpio_altera_config *cfg = dev->config; + uintptr_t reg_base = cfg->reg_base; + uint32_t addr; + + addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; + + *value = sys_read32((addr)); + + return 0; +} + +static int gpio_altera_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask) +{ + const struct gpio_altera_config *cfg = dev->config; + const int outset = cfg->outset; + const int port_pin_mask = cfg->common.port_pin_mask; + uintptr_t reg_base = cfg->reg_base; + uint32_t addr; + + if ((port_pin_mask & mask) == 0) { + return -EINVAL; + } + + if (!gpio_pin_direction(dev, mask)) { + return -EINVAL; + } + + if (outset) { + addr = reg_base + ALTERA_AVALON_PIO_SET_BITS; + sys_write32(mask, addr); + } else { + addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; + sys_set_bits(addr, mask); + } + + return 0; +} + +static int gpio_altera_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask) +{ + const struct gpio_altera_config *cfg = dev->config; + const int outclear = cfg->outclear; + const int port_pin_mask = cfg->common.port_pin_mask; + uintptr_t reg_base = cfg->reg_base; + uint32_t addr; + + /* Check if mask range within 32 */ + if ((port_pin_mask & mask) == 0) { + return -EINVAL; + } + + if (!gpio_pin_direction(dev, mask)) { + return -EINVAL; + } + + if (outclear) { + addr = reg_base + ALTERA_AVALON_PIO_CLEAR_BITS; + sys_write32(mask, addr); + } else { + addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; + sys_clear_bits(addr, mask); + } + + return 0; +} + +static int gpio_init(const struct device *dev) +{ + const struct gpio_altera_config *cfg = dev->config; + + /* Configure GPIO device */ + cfg->cfg_func(); + + return 0; +} + +static int gpio_altera_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + ARG_UNUSED(trig); + + const struct gpio_altera_config *cfg = dev->config; + uintptr_t reg_base = cfg->reg_base; + const int port_pin_mask = cfg->common.port_pin_mask; + uint32_t addr; + + /* Check if pin number is within range */ + if ((port_pin_mask & BIT(pin)) == 0) { + return -EINVAL; + } + + addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET; + + switch (mode) { + case GPIO_INT_MODE_DISABLED: + /* Disable interrupt of pin */ + sys_clear_bits(addr, BIT(pin)); + irq_disable(cfg->irq_num); + break; + case GPIO_INT_MODE_LEVEL: + case GPIO_INT_MODE_EDGE: + /* Enable interrupt of pin */ + sys_set_bits(addr, BIT(pin)); + irq_enable(cfg->irq_num); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gpio_altera_manage_callback(const struct device *dev, + struct gpio_callback *callback, + bool set) +{ + + struct gpio_altera_data * const data = dev->data; + + return gpio_manage_callback(&data->cb, callback, set); +} + +static void gpio_altera_irq_handler(const struct device *dev) +{ + const struct gpio_altera_config *cfg = dev->config; + struct gpio_altera_data *data = dev->data; + uintptr_t reg_base = cfg->reg_base; + uint32_t port_value; + uint32_t addr; + + addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET; + + port_value = sys_read32(addr); + + sys_clear_bits(addr, port_value); + + /* Call the corresponding callback registered for the pin */ + gpio_fire_callbacks(&data->cb, dev, port_value); +} + +static const struct gpio_driver_api gpio_altera_driver_api = { + .pin_configure = gpio_altera_configure, + .port_get_raw = gpio_altera_port_get_raw, + .port_set_masked_raw = NULL, + .port_set_bits_raw = gpio_altera_port_set_bits_raw, + .port_clear_bits_raw = gpio_altera_port_clear_bits_raw, + .port_toggle_bits = NULL, + .pin_interrupt_configure = gpio_altera_pin_interrupt_configure, + .manage_callback = gpio_altera_manage_callback +}; + +#define CREATE_GPIO_DEVICE(n) \ + static void gpio_altera_cfg_func_##n(void); \ + static struct gpio_altera_data gpio_altera_data_##n; \ + static struct gpio_altera_config gpio_config_##n = { \ + .common = { \ + .port_pin_mask = \ + GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ + }, \ + .reg_base = DT_INST_REG_ADDR(n), \ + .direction = DT_INST_ENUM_IDX(n, direction), \ + .irq_num = DT_INST_IRQN(n), \ + .cfg_func = gpio_altera_cfg_func_##n, \ + .outset = DT_INST_PROP(n, outset), \ + .outclear = DT_INST_PROP(n, outclear), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + gpio_init, \ + NULL, \ + &gpio_altera_data_##n, \ + &gpio_config_##n, \ + POST_KERNEL, \ + CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_altera_driver_api); \ + \ + static void gpio_altera_cfg_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + gpio_altera_irq_handler, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + } + +DT_INST_FOREACH_STATUS_OKAY(CREATE_GPIO_DEVICE) diff --git a/dts/bindings/gpio/altr,pio-1.0.yaml b/dts/bindings/gpio/altr,pio-1.0.yaml new file mode 100644 index 00000000000..d9711130e7c --- /dev/null +++ b/dts/bindings/gpio/altr,pio-1.0.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Altera PIO node + +compatible: "altr,pio-1.0" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + direction: + type: string + description: | + Direction can be set to bidir, input, inout or output. + It cannot be configure when during the runtime and should be + set according to the synthesized FPGA design. + default: output + enum: + - "bidir" + - "input" + - "inout" + - "output" + + outset: + type: boolean + description: | + Enable outset register to specify which bit of the output port to set. + + outclear: + type: boolean + description: | + Enable outclear register to specify which output bit to clear. + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/tests/drivers/build_all/gpio/altera.overlay b/tests/drivers/build_all/gpio/altera.overlay new file mode 100644 index 00000000000..2d92889aff4 --- /dev/null +++ b/tests/drivers/build_all/gpio/altera.overlay @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + test { + #address-cells = <1>; + #size-cells = <1>; + + test_int_gpio { + #address-cells = <1>; + #size-cells = <1>; + test_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + #address-cells = < 0x0 >; + #interrupt-cells = < 0x1 >; + interrupt-controller; + phandle = < 0x1 >; + }; + + test_gpio0: gpio@30070 { + interrupt-parent = < &test_intc >; + compatible = "altr,pio-1.0"; + reg = < 0x30070 0x10 >; + gpio-controller; + #gpio-cells = < 0x2 >; + status = "okay"; + ngpios = < 16 >; + #direction = "input"; + interrupts = <28>; + }; + }; + }; + +}; diff --git a/tests/drivers/build_all/gpio/testcase.yaml b/tests/drivers/build_all/gpio/testcase.yaml index bd720d569d5..a95b4625725 100644 --- a/tests/drivers/build_all/gpio/testcase.yaml +++ b/tests/drivers/build_all/gpio/testcase.yaml @@ -17,3 +17,9 @@ tests: platform_allow: titanium_ti60_f225 depends_on: gpio extra_args: DTC_OVERLAY_FILE="efinix_sapphire.overlay" + + drivers.gpio.build.altera_pio: + min_ram: 32 + platform_allow: niosv_m niosv_g + depends_on: gpio + extra_args: DTC_OVERLAY_FILE="altera.overlay"