diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index fe94c1ef79c..fd77bdbc186 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -44,6 +44,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_STMPE1600 gpio_stmpe1600.c) 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_SHELL gpio_shell.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 20237d4c196..3c7cbe9ac11 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -101,4 +101,6 @@ source "drivers/gpio/Kconfig.pca953x" source "drivers/gpio/Kconfig.fxl6408" +source "drivers/gpio/Kconfig.andes_atcgpio100" + endif # GPIO diff --git a/drivers/gpio/Kconfig.andes_atcgpio100 b/drivers/gpio/Kconfig.andes_atcgpio100 new file mode 100644 index 00000000000..2a6705fca6a --- /dev/null +++ b/drivers/gpio/Kconfig.andes_atcgpio100 @@ -0,0 +1,15 @@ +# Kconfig Andes ATCGPIO100 configuration option +# +# Copyright (c) 2021 Andes Technology Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# + +config GPIO_ANDES_ATCGPIO100 + bool "Andes ATCGPIO100 GPIO driver" + depends on SOC_SERIES_RISCV_ANDES_V5 + select HAS_DTS_GPIO + help + Enable driver for the Andes ATCGPIO100 GPIO controller. + + Says n if not sure. diff --git a/drivers/gpio/gpio_andes_atcgpio100.c b/drivers/gpio/gpio_andes_atcgpio100.c new file mode 100644 index 00000000000..0adc985e9de --- /dev/null +++ b/drivers/gpio/gpio_andes_atcgpio100.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2021 Andes Technology Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file GPIO driver for the AndesTech ATCGPIO100 controller + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio_utils.h" + +#define DT_DRV_COMPAT andestech_atcgpio100 + +/* Andes ATCGPIO100 register definition */ +#define REG_IDR 0x00 /* ID and Revision reg. */ +#define REG_CFG 0x10 /* Hardware configure reg. */ +#define REG_DIN 0x20 /* Data In reg. */ +#define REG_DOUT 0x24 /* Data Out reg. */ +#define REG_DIR 0x28 /* Channel direction reg. */ +#define REG_DCLR 0x2C /* Data out clear reg. */ +#define REG_DSET 0x30 /* Data out set reg. */ +#define REG_PUEN 0x40 /* Pull enable reg. */ +#define REG_PTYP 0x44 /* Pull type reg. */ +#define REG_INTE 0x50 /* Interrupt enable reg. */ +#define REG_IMD0 0x54 /* Interrupt mode 0 ~ 7 reg. */ +#define REG_IMD1 0x58 /* Interrupt mode 8 ~ 15 reg. */ +#define REG_IMD2 0x5C /* Interrupt mode 16 ~ 23 reg. */ +#define REG_IMD3 0x60 /* Interrupt mode 24 ~ 31 reg. */ +#define REG_ISTA 0x64 /* Interrupt status reg. */ +#define REG_DEBE 0x70 /* De-bounce enable reg. */ +#define REG_DEBC 0x74 /* De-Bounce control reg. */ + +#define INT_NO_OPERATION 0x0 +#define INT_HIGH_LEVEL 0x2 +#define INT_LOW_LEVEL 0x3 +#define INT_NEGATIVE_EDGE 0x5 +#define INT_POSITIVE_EDGE 0x6 +#define INT_DUAL_EDGE 0x7 + +#define PULL_CONFIGURED BIT(31) +#define DEBOUNCE_CONFIGURED BIT(29) +#define DF_DEBOUNCED_SETTING (0x80000003) + +#define DEV_CFG(dev) \ + ((const struct gpio_atcgpio100_config * const)(dev)->config) + +#define DEV_DATA(dev) \ + ((struct gpio_atcgpio100_data *)(dev)->data) + + +#define GPIO_CFG(dev) (DEV_CFG(dev)->base + REG_CFG) +#define GPIO_DIR(dev) (DEV_CFG(dev)->base + REG_DIR) +#define GPIO_DIN(dev) (DEV_CFG(dev)->base + REG_DIN) +#define GPIO_DOUT(dev) (DEV_CFG(dev)->base + REG_DOUT) +#define GPIO_DCLR(dev) (DEV_CFG(dev)->base + REG_DCLR) +#define GPIO_DSET(dev) (DEV_CFG(dev)->base + REG_DSET) +#define GPIO_PUEN(dev) (DEV_CFG(dev)->base + REG_PUEN) +#define GPIO_PTYP(dev) (DEV_CFG(dev)->base + REG_PTYP) +#define GPIO_INTE(dev) (DEV_CFG(dev)->base + REG_INTE) +#define GPIO_IMD(dev, idx) (DEV_CFG(dev)->base + REG_IMD0 + (idx * 4)) +#define GPIO_ISTA(dev) (DEV_CFG(dev)->base + REG_ISTA) +#define GPIO_DEBE(dev) (DEV_CFG(dev)->base + REG_DEBE) +#define GPIO_DEBC(dev) (DEV_CFG(dev)->base + REG_DEBC) + +#define INWORD(x) sys_read32(x) +#define OUTWORD(x, d) sys_write32(d, x) + +#define SET_GPIO_INT_MODE(cur_val, mode, ch_idx) \ + do { \ + cur_val &= ~(BIT_MASK(3) << (ch_idx * 4)); \ + cur_val |= (mode << (ch_idx * 4)); \ + } while (false) + + +typedef void (*atcgpio100_cfg_func_t)(void); + +struct gpio_atcgpio100_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + uint32_t base; + uint32_t irq_num; + atcgpio100_cfg_func_t cfg_func; +}; + +struct gpio_atcgpio100_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 int gpio_atcgpio100_config(const struct device *port, + gpio_pin_t pin, + gpio_flags_t flags) +{ + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + uint32_t port_value, pin_mask, io_flags; + k_spinlock_key_t key; + + /* Does not support disconnected pin, and + * not supporting both input/output at same time. + */ + io_flags = flags & (GPIO_INPUT | GPIO_OUTPUT); + if ((io_flags == GPIO_DISCONNECTED) + || (io_flags == (GPIO_INPUT | GPIO_OUTPUT))) { + return -ENOTSUP; + } + + pin_mask = BIT(pin); + + if (flags & GPIO_OUTPUT) { + + if (flags & GPIO_OUTPUT_INIT_HIGH) { + OUTWORD(GPIO_DSET(port), pin_mask); + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + OUTWORD(GPIO_DCLR(port), pin_mask); + } + + key = k_spin_lock(&data->lock); + + /* Set channel output */ + port_value = INWORD(GPIO_DIR(port)); + OUTWORD(GPIO_DIR(port), port_value | pin_mask); + + k_spin_unlock(&data->lock, key); + + } else if (flags & GPIO_INPUT) { + + if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) { + return -ENOTSUP; + } + + key = k_spin_lock(&data->lock); + + /* Set de-bounce */ + if (flags & GPIO_INT_DEBOUNCE) { + /* Default settings: Filter out pulses which are + * less than 4 de-bounce clock period + */ + OUTWORD(GPIO_DEBC(port), DF_DEBOUNCED_SETTING); + port_value = INWORD(GPIO_DEBE(port)); + OUTWORD(GPIO_DEBE(port), port_value | pin_mask); + } + + /* Set channel input */ + port_value = INWORD(GPIO_DIR(port)); + OUTWORD(GPIO_DIR(port), port_value & ~pin_mask); + + k_spin_unlock(&data->lock, key); + + } else { + return -ENOTSUP; + } + + return 0; +} + +static int gpio_atcgpio100_port_get_raw(const struct device *port, + gpio_port_value_t *value) +{ + *value = INWORD(GPIO_DIN(port)); + return 0; +} + +static int gpio_atcgpio100_set_masked_raw(const struct device *port, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + uint32_t port_value; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + port_value = INWORD(GPIO_DOUT(port)); + OUTWORD(GPIO_DOUT(port), (port_value & ~mask) | (value & mask)); + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int gpio_atcgpio100_set_bits_raw(const struct device *port, + gpio_port_pins_t pins) +{ + OUTWORD(GPIO_DSET(port), pins); + return 0; +} + +static int gpio_atcgpio100_clear_bits_raw(const struct device *port, + gpio_port_pins_t pins) +{ + OUTWORD(GPIO_DCLR(port), pins); + return 0; +} + +static int gpio_atcgpio100_toggle_bits(const struct device *port, + gpio_port_pins_t pins) +{ + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + uint32_t port_value; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + port_value = INWORD(GPIO_DOUT(port)); + OUTWORD(GPIO_DOUT(port), port_value ^ pins); + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int gpio_atcgpio100_pin_interrupt_configure( + const struct device *port, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + uint32_t port_value, int_mode, imr_idx, ch_idx; + k_spinlock_key_t key; + + switch (mode | trig) { + case GPIO_INT_EDGE_BOTH: + int_mode = INT_DUAL_EDGE; + break; + case GPIO_INT_EDGE_RISING: + int_mode = INT_POSITIVE_EDGE; + break; + case GPIO_INT_EDGE_FALLING: + int_mode = INT_NEGATIVE_EDGE; + break; + case GPIO_INT_LEVEL_LOW: + int_mode = INT_LOW_LEVEL; + break; + case GPIO_INT_LEVEL_HIGH: + int_mode = INT_HIGH_LEVEL; + break; + default: + int_mode = INT_NO_OPERATION; + break; + } + + imr_idx = (pin / 8); + ch_idx = (pin % 8); + + key = k_spin_lock(&data->lock); + + if (int_mode == INT_NO_OPERATION) { + /* Disable interrupt of pin */ + port_value = INWORD(GPIO_INTE(port)); + OUTWORD(GPIO_INTE(port), port_value & ~BIT(pin)); + + /* Clear the remain pending interrupt */ + port_value = INWORD(GPIO_ISTA(port)); + OUTWORD(GPIO_ISTA(port), port_value); + } else { + /* Set interrupt mode of pin */ + port_value = INWORD(GPIO_IMD(port, imr_idx)); + SET_GPIO_INT_MODE(port_value, int_mode, ch_idx); + OUTWORD(GPIO_IMD(port, imr_idx), port_value); + + /* Enable interrupt of pin */ + port_value = INWORD(GPIO_INTE(port)); + OUTWORD(GPIO_INTE(port), port_value | BIT(pin)); + } + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int gpio_atcgpio100_manage_callback(const struct device *port, + struct gpio_callback *callback, + bool set) +{ + + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + + return gpio_manage_callback(&data->cb, callback, set); +} + +static void gpio_atcgpio100_irq_handler(const struct device *port) +{ + struct gpio_atcgpio100_data * const data = DEV_DATA(port); + uint32_t port_value; + + port_value = INWORD(GPIO_ISTA(port)); + OUTWORD(GPIO_ISTA(port), port_value); + + gpio_fire_callbacks(&data->cb, port, port_value); + +} + +static const struct gpio_driver_api gpio_atcgpio100_api = { + .pin_configure = gpio_atcgpio100_config, + .port_get_raw = gpio_atcgpio100_port_get_raw, + .port_set_masked_raw = gpio_atcgpio100_set_masked_raw, + .port_set_bits_raw = gpio_atcgpio100_set_bits_raw, + .port_clear_bits_raw = gpio_atcgpio100_clear_bits_raw, + .port_toggle_bits = gpio_atcgpio100_toggle_bits, + .pin_interrupt_configure = gpio_atcgpio100_pin_interrupt_configure, + .manage_callback = gpio_atcgpio100_manage_callback +}; + +static int gpio_atcgpio100_init(const struct device *port) +{ + const struct gpio_atcgpio100_config * const dev_cfg = DEV_CFG(port); + + /* Disable all interrupts */ + OUTWORD(GPIO_INTE(port), BIT_MASK(0)); + + /* Write 1 to clear interrupt status */ + OUTWORD(GPIO_ISTA(port), (uint32_t) BIT64_MASK(32)); + + /* Configure GPIO device */ + dev_cfg->cfg_func(); + + /* Enable PLIC interrupt GPIO source */ + irq_enable(dev_cfg->irq_num); + + return 0; +} + +#define GPIO_ATCGPIO100_INIT(n) \ + static void gpio_atcgpio100_cfg_func_##n(void); \ + static struct gpio_atcgpio100_data gpio_atcgpio100_data_##n; \ + \ + static const struct gpio_atcgpio100_config \ + gpio_atcgpio100_config_##n = { \ + .common = { \ + .port_pin_mask = \ + GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ + }, \ + .base = DT_INST_REG_ADDR(n), \ + .irq_num = DT_INST_IRQN(n), \ + .cfg_func = gpio_atcgpio100_cfg_func_##n \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + gpio_atcgpio100_init, \ + NULL, \ + &gpio_atcgpio100_data_##n, \ + &gpio_atcgpio100_config_##n, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &gpio_atcgpio100_api); \ + \ + static void gpio_atcgpio100_cfg_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + gpio_atcgpio100_irq_handler, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + return; \ + } \ + + +DT_INST_FOREACH_STATUS_OKAY(GPIO_ATCGPIO100_INIT)