From e8d0eb1db199e74180f122bd5d19e57ba691339a Mon Sep 17 00:00:00 2001 From: Robert Winkler Date: Fri, 2 Aug 2019 10:38:56 +0200 Subject: [PATCH] drivers: gpio: Add LiteX GPIO driver This commits adds GPIO driver for LiteX SoC builder. Due to the fact that GPIO in LiteX is unidirectional and can be configured with different pins amount per port, additional entries were added to the dts file. Signed-off-by: Robert Winkler Signed-off-by: Mateusz Holenko Signed-off-by: Piotr Zierhoffer --- CODEOWNERS | 1 + drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.litex | 11 ++ drivers/gpio/gpio_litex.c | 315 ++++++++++++++++++++++++++++++ dts/bindings/gpio/litex,gpio.yaml | 35 ++++ soc/riscv/litex-vexriscv/soc.h | 32 +++ 7 files changed, 397 insertions(+) create mode 100644 drivers/gpio/Kconfig.litex create mode 100644 drivers/gpio/gpio_litex.c create mode 100644 dts/bindings/gpio/litex,gpio.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 8d32c62520..ff710b28f5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -149,6 +149,7 @@ /drivers/gpio/*lmp90xxx* @henrikbrixandersen /drivers/gpio/*stm32* @erwango /drivers/gpio/*sx1509b* @pabigot +/drivers/gpio/*litex* @mateusz-holenko @kgugala @pgielda /drivers/hwinfo/ @alexanderwachter /drivers/i2c/*litex* @mateusz-holenko @kgugala @pgielda /drivers/i2s/i2s_ll_stm32* @avisconti diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index e4ddf723df..bf0ee29000 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_STELLARIS gpio_stellaris.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RV32M1 gpio_rv32m1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_HT16K33 gpio_ht16k33.c) zephyr_library_sources_ifdef(CONFIG_GPIO_LMP90XXX gpio_lmp90xxx.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_LITEX gpio_litex.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e9b6be26d2..bae2f2b374 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -67,4 +67,6 @@ source "drivers/gpio/Kconfig.ht16k33" source "drivers/gpio/Kconfig.lmp90xxx" +source "drivers/gpio/Kconfig.litex" + endif # GPIO diff --git a/drivers/gpio/Kconfig.litex b/drivers/gpio/Kconfig.litex new file mode 100644 index 0000000000..993c68324c --- /dev/null +++ b/drivers/gpio/Kconfig.litex @@ -0,0 +1,11 @@ +# Litex VexRiscV GPIO configuration options + +# Copyright (c) 2019 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_LITEX + bool "Litex GPIO driver" + depends on SOC_RISCV32_LITEX_VEXRISCV + select HAS_DTS_GPIO + help + Enable Litex GPIO driver. diff --git a/drivers/gpio/gpio_litex.c b/drivers/gpio/gpio_litex.c new file mode 100644 index 0000000000..113647f74f --- /dev/null +++ b/drivers/gpio/gpio_litex.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2019 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SUPPORTED_FLAGS (GPIO_INPUT | GPIO_OUTPUT | \ + GPIO_OUTPUT_INIT_LOW | GPIO_OUTPUT_INIT_HIGH | \ + GPIO_ACTIVE_LOW | GPIO_ACTIVE_HIGH) + +#define GPIO_LOW 0 +#define GPIO_HIGH 1 + +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL +LOG_MODULE_REGISTER(gpio_litex); + +static const char *LITEX_LOG_REG_SIZE_NGPIOS_MISMATCH = + "Cannot handle all of the gpios with the register of given size\n"; +static const char *LITEX_LOG_WRONG_DIR = + "Direction chosen in device tree do not match with the operation\n"; +static const char *LITEX_LOG_CANNOT_CHANGE_DIR = + "Cannot change port direction selected in device tree\n"; + +struct gpio_litex_cfg { + volatile u32_t *reg_addr; + int reg_size; + int nr_gpios; + bool port_is_output; +}; + +struct gpio_litex_data { + struct gpio_driver_data common; +}; + +/* Helper macros for GPIO */ + +#define DEV_GPIO_CFG(dev) \ + ((const struct gpio_litex_cfg *)(dev)->config->config_info) + +/* Helper functions for bit / port access */ + +static inline void set_bit(const struct gpio_litex_cfg *config, + u32_t bit, bool val) +{ + int regv, new_regv; + + regv = litex_read(config->reg_addr, config->reg_size); + new_regv = (regv & ~BIT(bit)) | (val << bit); + litex_write(config->reg_addr, config->reg_size, new_regv); +} + +static inline u32_t get_bit(const struct gpio_litex_cfg *config, u32_t bit) +{ + int regv = litex_read(config->reg_addr, config->reg_size); + + return !!(regv & BIT(bit)); +} + +static inline void set_port(const struct gpio_litex_cfg *config, u32_t value) +{ + litex_write(config->reg_addr, config->reg_size, value); +} + +static inline u32_t get_port(const struct gpio_litex_cfg *config) +{ + int regv = litex_read(config->reg_addr, config->reg_size); + + return (regv & BIT_MASK(config->nr_gpios)); +} + +/* Driver functions */ + +static int gpio_litex_init(struct device *dev) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + + /* each 4-byte register is able to handle 8 GPIO pins */ + if (gpio_config->nr_gpios > (gpio_config->reg_size / 4) * 8) { + LOG_ERR("%s", LITEX_LOG_REG_SIZE_NGPIOS_MISMATCH); + return -EINVAL; + } + + return 0; +} + +static int gpio_litex_configure(struct device *dev, int access_op, + u32_t pin, int flags) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + + if (access_op != GPIO_ACCESS_BY_PIN) { + return -ENOTSUP; + } + + if (flags & ~SUPPORTED_FLAGS) { + return -ENOTSUP; + } + + if ((flags & GPIO_OUTPUT) && (flags & GPIO_INPUT)) { + /* Pin cannot be configured as input and output */ + return -ENOTSUP; + } else if (!(flags & (GPIO_INPUT | GPIO_OUTPUT))) { + /* Pin has to be configuread as input or output */ + return -ENOTSUP; + } + + if (flags & GPIO_OUTPUT) { + if (!gpio_config->port_is_output) { + LOG_ERR("%s", LITEX_LOG_CANNOT_CHANGE_DIR); + return -EINVAL; + } + + if (flags & GPIO_OUTPUT_INIT_HIGH) { + set_bit(gpio_config, pin, GPIO_HIGH); + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + set_bit(gpio_config, pin, GPIO_LOW); + } + } else { + if (gpio_config->port_is_output) { + LOG_ERR("%s", LITEX_LOG_CANNOT_CHANGE_DIR); + return -EINVAL; + } + } + + return 0; +} + +static int gpio_litex_write(struct device *dev, int access_op, + u32_t pin, u32_t value) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + + if (!gpio_config->port_is_output) { + LOG_ERR("%s", LITEX_LOG_WRONG_DIR); + return -EINVAL; + } + + if (access_op != GPIO_ACCESS_BY_PIN) { + return -ENOTSUP; + } + + set_bit(gpio_config, pin, value); + + return 0; +} + +static int gpio_litex_read(struct device *dev, int access_op, + u32_t pin, u32_t *value) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + + if (access_op != GPIO_ACCESS_BY_PIN) { + return -ENOTSUP; + } + + *value = get_bit(gpio_config, pin); + + return 0; +} + +static int gpio_litex_port_get_raw(struct device *dev, gpio_port_value_t *value) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + + *value = get_port(gpio_config); + return 0; +} + +static int gpio_litex_port_set_masked_raw(struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + u32_t port_val; + + port_val = get_port(gpio_config); + port_val = (port_val & ~mask) | (value & mask); + set_port(gpio_config, port_val); + + return 0; +} + +static int gpio_litex_port_set_bits_raw(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + u32_t port_val; + + port_val = get_port(gpio_config); + port_val |= pins; + set_port(gpio_config, port_val); + + return 0; +} + +static int gpio_litex_port_clear_bits_raw(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + u32_t port_val; + + port_val = get_port(gpio_config); + port_val &= ~pins; + set_port(gpio_config, port_val); + + return 0; +} + +static int gpio_litex_port_toggle_bits(struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_litex_cfg *gpio_config = DEV_GPIO_CFG(dev); + u32_t port_val; + + port_val = get_port(gpio_config); + port_val ^= pins; + set_port(gpio_config, port_val); + + return 0; +} + +static int gpio_litex_pin_interrupt_configure(struct device *dev, + unsigned int pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + int ret = 0; + + if (mode != GPIO_INT_MODE_DISABLED) { + ret = -ENOTSUP; + } + return ret; +} + +static const struct gpio_driver_api gpio_litex_driver_api = { + .config = gpio_litex_configure, + .write = gpio_litex_write, + .read = gpio_litex_read, + .port_get_raw = gpio_litex_port_get_raw, + .port_set_masked_raw = gpio_litex_port_set_masked_raw, + .port_set_bits_raw = gpio_litex_port_set_bits_raw, + .port_clear_bits_raw = gpio_litex_port_clear_bits_raw, + .port_toggle_bits = gpio_litex_port_toggle_bits, + .pin_interrupt_configure = gpio_litex_pin_interrupt_configure, +}; + +/* Device Instantiation */ + +#define GPIO_LITEX_INIT(n) \ + BUILD_ASSERT_MSG(DT_INST_##n##_LITEX_GPIO_SIZE != 0 \ + && DT_INST_##n##_LITEX_GPIO_SIZE % 4 == 0, \ + "Register size must be a multiple of 4"); \ +\ + static const struct gpio_litex_cfg gpio_litex_cfg_##n = { \ + .reg_addr = \ + (volatile u32_t *) DT_INST_##n##_LITEX_GPIO_BASE_ADDRESS, \ + .reg_size = DT_INST_##n##_LITEX_GPIO_SIZE, \ + .nr_gpios = DT_INST_##n##_LITEX_GPIO_NGPIOS, \ + .port_is_output = DT_INST_##n##_LITEX_GPIO_PORT_IS_OUTPUT, \ + }; \ + static struct gpio_litex_data gpio_litex_data_##n; \ +\ + DEVICE_AND_API_INIT(litex_gpio_##n, \ + DT_INST_##n##_LITEX_GPIO_LABEL, \ + gpio_litex_init, \ + &gpio_litex_data_##n, \ + &gpio_litex_cfg_##n, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &gpio_litex_driver_api \ + ) + +#ifdef DT_INST_0_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(0); +#endif + +#ifdef DT_INST_1_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(1); +#endif + +#ifdef DT_INST_2_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(2); +#endif + +#ifdef DT_INST_3_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(3); +#endif + +#ifdef DT_INST_4_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(4); +#endif + +#ifdef DT_INST_5_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(5); +#endif + +#ifdef DT_INST_6_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(6); +#endif + +#ifdef DT_INST_7_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(7); +#endif + +#ifdef DT_INST_8_LITEX_GPIO_LABEL +GPIO_LITEX_INIT(8); +#endif diff --git a/dts/bindings/gpio/litex,gpio.yaml b/dts/bindings/gpio/litex,gpio.yaml new file mode 100644 index 0000000000..fedda8a249 --- /dev/null +++ b/dts/bindings/gpio/litex,gpio.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2019 Antmicro +# +# SPDX-License-Identifier: Apache-2.0 +# + +title: Litex GPIO + +description: > + This is a representation of the Litex GPIO nodes + +compatible: "litex,gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + port-is-output: + type: boolean + description: Indicates if the port is an output port + + reg: + required: true + + label: + required: true + + ngpios: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/soc/riscv/litex-vexriscv/soc.h b/soc/riscv/litex-vexriscv/soc.h index 5485175f94..02567237c7 100644 --- a/soc/riscv/litex-vexriscv/soc.h +++ b/soc/riscv/litex-vexriscv/soc.h @@ -54,6 +54,38 @@ static inline void litex_write32(unsigned int value, unsigned long addr) sys_write8(value >> 8, addr + 0x8); sys_write8(value, addr + 0xC); } + +/* `reg_size` is assumed to be a multiple of 4 */ +static inline void litex_write(volatile u32_t *reg, u32_t reg_size, u32_t val) +{ + u32_t shifted_data; + volatile u32_t *reg_addr; + u32_t subregs = reg_size / 4; + + for (int i = 0; i < subregs; ++i) { + shifted_data = val >> ((subregs - i - 1) * 8); + reg_addr = ((volatile u32_t *) reg) + i; + *(reg_addr) = shifted_data; + } +} + +/* `reg_size` is assumed to be a multiple of 4 */ +static inline u32_t litex_read(volatile u32_t *reg, u32_t reg_size) +{ + u32_t shifted_data; + volatile u32_t *reg_addr; + u32_t result = 0; + u32_t subregs = reg_size / 4; + + for (int i = 0; i < subregs; ++i) { + reg_addr = ((volatile u32_t *) reg) + i; + shifted_data = *(reg_addr); + result |= (shifted_data << ((subregs - i - 1) * 8)); + } + + return result; +} + #endif /* _ASMLANGUAGE */ #endif /* __RISCV32_LITEX_VEXRISCV_SOC_H_ */