diff --git a/CODEOWNERS b/CODEOWNERS index 8d32c625204..ff710b28f53 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 e4ddf723df8..bf0ee29000b 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 e9b6be26d2e..bae2f2b374d 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 00000000000..993c68324c4 --- /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 00000000000..113647f74fb --- /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 00000000000..fedda8a2499 --- /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 5485175f94a..02567237c7b 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_ */