diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index fd77bdbc186..f63c99d8ff4 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -45,6 +45,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_XEC_V2 gpio_mchp_xec_v2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCA953X gpio_pca953x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_FXL6408 gpio_fxl6408.c) zephyr_library_sources_ifdef(CONFIG_GPIO_ANDES_ATCGPIO100 gpio_andes_atcgpio100.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_NEORV32 gpio_neorv32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3c7cbe9ac11..d2cb13ec53e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,4 +103,6 @@ source "drivers/gpio/Kconfig.fxl6408" source "drivers/gpio/Kconfig.andes_atcgpio100" +source "drivers/gpio/Kconfig.neorv32" + endif # GPIO diff --git a/drivers/gpio/Kconfig.neorv32 b/drivers/gpio/Kconfig.neorv32 new file mode 100644 index 00000000000..ae9befb31bd --- /dev/null +++ b/drivers/gpio/Kconfig.neorv32 @@ -0,0 +1,11 @@ +# NEORV32 GPIO configuration options + +# Copyright (c) 2021 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_NEORV32 + bool "NEORV32 GPIO driver" + default $(dt_compat_enabled,neorv32-gpio) + depends on SOC_SERIES_NEORV32 && SYSCON + help + Enable NEORV32 GPIO driver. diff --git a/drivers/gpio/gpio_neorv32.c b/drivers/gpio/gpio_neorv32.c new file mode 100644 index 00000000000..74e5b0e7474 --- /dev/null +++ b/drivers/gpio/gpio_neorv32.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2021 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT neorv32_gpio + +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(gpio_neorv32, CONFIG_GPIO_LOG_LEVEL); + +#include "gpio_utils.h" + +/* Maximum number of GPIOs supported */ +#define MAX_GPIOS 32 + +struct neorv32_gpio_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + const struct device *syscon; + mm_reg_t input; + mm_reg_t output; +}; + +struct neorv32_gpio_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + /* Shadow register for output */ + uint32_t output; +}; + +static inline uint32_t neorv32_gpio_read(const struct device *dev) +{ + const struct neorv32_gpio_config *config = dev->config; + + return sys_read32(config->input); +} + +static inline void neorv32_gpio_write(const struct device *dev, uint32_t val) +{ + const struct neorv32_gpio_config *config = dev->config; + + sys_write32(val, config->output); +} + +static int neorv32_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, + gpio_flags_t flags) +{ + const struct neorv32_gpio_config *config = dev->config; + struct neorv32_gpio_data *data = dev->data; + unsigned int key; + + if (!(BIT(pin) & config->common.port_pin_mask)) { + return -EINVAL; + } + + if ((flags & GPIO_SINGLE_ENDED) != 0) { + return -ENOTSUP; + } + + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + return -ENOTSUP; + } + + if ((flags & GPIO_OUTPUT) != 0) { + key = irq_lock(); + + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { + data->output |= BIT(pin); + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { + data->output &= ~BIT(pin); + } + + neorv32_gpio_write(dev, data->output); + irq_unlock(key); + } + + return 0; +} + +static int neorv32_gpio_port_get_raw(const struct device *dev, + gpio_port_value_t *value) +{ + *value = neorv32_gpio_read(dev); + return 0; +} + +static int neorv32_gpio_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + struct neorv32_gpio_data *data = dev->data; + unsigned int key; + + key = irq_lock(); + data->output = (data->output & ~mask) | (mask & value); + neorv32_gpio_write(dev, data->output); + irq_unlock(key); + + return 0; +} + +static int neorv32_gpio_port_set_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + struct neorv32_gpio_data *data = dev->data; + unsigned int key; + + key = irq_lock(); + data->output |= pins; + neorv32_gpio_write(dev, data->output); + irq_unlock(key); + + return 0; +} + +static int neorv32_gpio_port_clear_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + struct neorv32_gpio_data *data = dev->data; + unsigned int key; + + key = irq_lock(); + data->output &= ~pins; + neorv32_gpio_write(dev, data->output); + irq_unlock(key); + + return 0; +} + +static int neorv32_gpio_port_toggle_bits(const struct device *dev, + gpio_port_pins_t pins) +{ + struct neorv32_gpio_data *data = dev->data; + unsigned int key; + + key = irq_lock(); + data->output ^= pins; + neorv32_gpio_write(dev, data->output); + irq_unlock(key); + + return 0; +} + +static int neorv32_gpio_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pin); + ARG_UNUSED(mode); + ARG_UNUSED(trig); + + return -ENOTSUP; +} + +static int neorv32_gpio_manage_callback(const struct device *dev, + struct gpio_callback *cb, + bool set) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(set); + + return -ENOTSUP; +} + +static uint32_t neorv32_gpio_get_pending_int(const struct device *dev) +{ + return 0; +} + +static int neorv32_gpio_init(const struct device *dev) +{ + const struct neorv32_gpio_config *config = dev->config; + struct neorv32_gpio_data *data = dev->data; + uint32_t features; + int err; + + if (!device_is_ready(config->syscon)) { + LOG_ERR("syscon device not ready"); + return -EINVAL; + } + + err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_FEATURES, &features); + if (err < 0) { + LOG_ERR("failed to determine implemented features (err %d)", err); + return -EIO; + } + + if ((features & NEORV32_SYSINFO_FEATURES_IO_GPIO) == 0) { + LOG_ERR("neorv32 gpio not supported"); + return -ENODEV; + } + + neorv32_gpio_write(dev, data->output); + + return 0; +} + +static const struct gpio_driver_api neorv32_gpio_driver_api = { + .pin_configure = neorv32_gpio_pin_configure, + .port_get_raw = neorv32_gpio_port_get_raw, + .port_set_masked_raw = neorv32_gpio_port_set_masked_raw, + .port_set_bits_raw = neorv32_gpio_port_set_bits_raw, + .port_clear_bits_raw = neorv32_gpio_port_clear_bits_raw, + .port_toggle_bits = neorv32_gpio_port_toggle_bits, + .pin_interrupt_configure = neorv32_gpio_pin_interrupt_configure, + .manage_callback = neorv32_gpio_manage_callback, + .get_pending_int = neorv32_gpio_get_pending_int, +}; + +#define NEORV32_GPIO_INIT(n) \ + static struct neorv32_gpio_data neorv32_gpio_##n##_data = { \ + .output = 0, \ + }; \ + \ + static const struct neorv32_gpio_config neorv32_gpio_##n##_config = { \ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n) \ + }, \ + .syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, syscon)), \ + .input = DT_INST_REG_ADDR_BY_NAME(n, input), \ + .output = DT_INST_REG_ADDR_BY_NAME(n, output), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + &neorv32_gpio_init, \ + NULL, \ + &neorv32_gpio_##n##_data, \ + &neorv32_gpio_##n##_config, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &neorv32_gpio_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(NEORV32_GPIO_INIT) diff --git a/dts/bindings/gpio/neorv32-gpio.yaml b/dts/bindings/gpio/neorv32-gpio.yaml new file mode 100644 index 00000000000..1cb175d7409 --- /dev/null +++ b/dts/bindings/gpio/neorv32-gpio.yaml @@ -0,0 +1,25 @@ +description: NEORV32 GPIO + +compatible: "neorv32-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + reg-names: + required: true + + syscon: + type: phandle + required: true + description: | + phandle to syscon (NEORV32 SYSINFO) node. + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags