From 6804e8ff40a7c2baf8fcdb903d3d825cf5e1daeb Mon Sep 17 00:00:00 2001 From: Cheryl Su Date: Thu, 5 Nov 2020 17:23:35 +0800 Subject: [PATCH] drivers/gpio: it8xxx2 platform gpio driver This commit is about platform it8xxx2 gpio. The devicetree use key and led as example. Users can change it to meet their needs. Signed-off-by: Cheryl Su --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.it8xxx2 | 8 + drivers/gpio/gpio_ite_it8xxx2.c | 395 ++++++++++++++++++++++++++++++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/gpio/Kconfig.it8xxx2 create mode 100644 drivers/gpio/gpio_ite_it8xxx2.c diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 55dba3dbc78..a93b64a8409 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_ESP32 gpio_esp32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SIFIVE gpio_sifive.c) zephyr_library_sources_ifdef(CONFIG_GPIO_GECKO gpio_gecko.c) zephyr_library_sources_ifdef(CONFIG_GPIO_IMX gpio_imx.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_ITE_IT8XXX2 gpio_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23S17 gpio_mcp23s17.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX gpio_mcux.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCUX_IGPIO gpio_mcux_igpio.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 81d86851b3b..af015ba5b51 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -59,6 +59,8 @@ source "drivers/gpio/Kconfig.sx1509b" source "drivers/gpio/Kconfig.imx" +source "drivers/gpio/Kconfig.it8xxx2" + source "drivers/gpio/Kconfig.intel" source "drivers/gpio/Kconfig.xec" diff --git a/drivers/gpio/Kconfig.it8xxx2 b/drivers/gpio/Kconfig.it8xxx2 new file mode 100644 index 00000000000..78812d7523c --- /dev/null +++ b/drivers/gpio/Kconfig.it8xxx2 @@ -0,0 +1,8 @@ +# Copyright (c) 2020 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_ITE_IT8XXX2 + bool "ITE IT8XXX2 GPIO driver" + select HAS_DTS_GPIO + help + Enable driver for the ite GPIO controller. diff --git a/drivers/gpio/gpio_ite_it8xxx2.c b/drivers/gpio/gpio_ite_it8xxx2.c new file mode 100644 index 00000000000..f83aecad39e --- /dev/null +++ b/drivers/gpio/gpio_ite_it8xxx2.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2020 ITE Corporation. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "gpio_utils.h" + +#define DT_DRV_COMPAT ite_it8xxx2_gpio +#define GPIO_LOW 0 +#define GPIO_HIGH 1 +#define NUM_IO_MAX 8 +#define CURR_SUPP_GPIO_SET 2 + +/* + * this two function be used to enable/disable specific irq interrupt + */ +extern void ite_intc_irq_enable(unsigned int irq); +extern void ite_intc_irq_disable(unsigned int irq); + +#define GPIO_GPDRB (DT_REG_ADDR_BY_IDX(DT_NODELABEL(gpiob), 0)) +#define GPIO_GPDRF (DT_REG_ADDR_BY_IDX(DT_NODELABEL(gpiof), 0)) +#define GPIO_GPDRM (DT_REG_ADDR_BY_IDX(DT_NODELABEL(gpiom), 0)) +#define GPCR_OFFSET 0x10 +#define GPIO_DIR_INPUT 0x80 +#define GPIO_DIR_OUTPUT 0x40 + +struct gpio_ite_wui { + volatile uint8_t *reg_addr; + volatile uint8_t *clear_addr; + volatile uint8_t *bothedge_addr; + uint32_t pin; + uint32_t irq; +}; + +static const struct gpio_ite_wui GPIO_GPDRB_wui[NUM_IO_MAX] = { + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(5), 106 }, /* gpb0, */ + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(6), 107 }, /* gpb1, */ + { &WUEMR8, &WUESR8, &WUBEMR8, BIT(4), 92 }, /* gpb2, */ + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(7), 108 }, /* gpb3, */ + { &WUEMR9, &WUESR9, &WUBEMR9, BIT(6), 99 }, /* gpb4, */ + { &WUEMR11, &WUESR11, &WUBEMR11, BIT(0), 109 }, /* gpb5, */ + { &WUEMR11, &WUESR11, &WUBEMR11, BIT(1), 110 }, /* gpb6, */ + { &WUEMR11, &WUESR11, &WUBEMR11, BIT(2), 111 }, /* gpb7, */ + +}; + +static const struct gpio_ite_wui GPIO_GPDRF_wui[NUM_IO_MAX] = { + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(0), 101 }, /* gpf0, */ + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(1), 102 }, /* gpf1, */ + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(2), 103 }, /* gpf2, */ + { &WUEMR10, &WUESR10, &WUBEMR10, BIT(3), 104 }, /* gpf3, */ + { &WUEMR6, &WUESR6, &WUBEMR6, BIT(4), 52 }, /* gpf4, */ + { &WUEMR6, &WUESR6, &WUBEMR6, BIT(5), 53 }, /* gpf5, */ + { &WUEMR6, &WUESR6, &WUBEMR6, BIT(6), 54 }, /* gpf6, */ + { &WUEMR6, &WUESR6, &WUBEMR6, BIT(7), 55 }, /* gpf7, */ +}; + +/* struct gpio_ite_reg_table is record different gpio port's register set + * base_addr: base address of gpio set + * wui[]: wui register group + */ +struct gpio_ite_reg_table { + uint32_t base_addr; + const struct gpio_ite_wui *wui; +}; + +struct gpio_ite_reg_table gpiox_reg[CURR_SUPP_GPIO_SET] = { + {GPIO_GPDRB, GPIO_GPDRB_wui}, /* GPIO GROPU B */ + {GPIO_GPDRF, GPIO_GPDRF_wui}, /* GPIO GROPU F */ +}; + +/* edge/level trigger register */ +static volatile uint8_t *const reg_ielmr[] = { + &IELMR0, &IELMR1, &IELMR2, &IELMR3, + &IELMR4, &IELMR5, &IELMR6, &IELMR7, + &IELMR8, &IELMR9, &IELMR10, &IELMR11, + &IELMR12, &IELMR13, &IELMR14, &IELMR15, + &IELMR16, &IELMR17, &IELMR18, &IELMR19, + &IELMR20 +}; + +/* high/low trigger register */ +static volatile uint8_t *const reg_ipolr[] = { + &IPOLR0, &IPOLR1, &IPOLR2, &IPOLR3, + &IPOLR4, &IPOLR5, &IPOLR6, &IPOLR7, + &IPOLR8, &IPOLR9, &IPOLR10, &IPOLR11, + &IPOLR12, &IPOLR13, &IPOLR14, &IPOLR15, + &IPOLR16, &IPOLR17, &IPOLR18, &IPOLR19, + &IPOLR20 +}; + +/* + * Strcture gpio_ite_cfg is about the setting of gpio + * this config will be used at initial time + */ +struct gpio_ite_cfg { + uint32_t reg_addr; /* gpio register base address */ + uint8_t gpio_irq[8]; /* gpio's irq */ +}; + +/* + * Strcture gpio_ite_data is about callback function + */ +struct gpio_ite_data { + struct gpio_driver_data common; + sys_slist_t callbacks; + uint32_t pin_callback_enables; +}; + +/* dev macros for GPIO */ +#define DEV_GPIO_DATA(dev) \ + ((struct gpio_ite_data *)(dev)->data) + +#define DEV_GPIO_CFG(dev) \ + ((const struct gpio_ite_cfg *)(dev)->config) + +/** + * functions for bit / port access + */ +static inline void set_bit(const struct gpio_ite_cfg *config, + uint8_t bit, bool val) +{ + uint8_t regv, new_regv; + + regv = ite_read(config->reg_addr, 1); + new_regv = (regv & ~BIT(bit)) | (val << bit); + ite_write(config->reg_addr, 1, new_regv); +} + +static inline uint8_t get_bit(const struct gpio_ite_cfg *config, uint8_t bit) +{ + uint8_t regv = ite_read(config->reg_addr, 1); + + return !!(regv & BIT(bit)); +} + +static inline void set_port(const struct gpio_ite_cfg *config, uint8_t value) +{ + ite_write(config->reg_addr, 1, value); +} + +static inline uint8_t get_port(const struct gpio_ite_cfg *config) +{ + uint8_t regv = ite_read(config->reg_addr, 1); + + return regv; +} + +/** + * Driver functions + */ +static int gpio_ite_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + unsigned int gpcr_offset = GPCR_OFFSET; + uint32_t gpcr_reg; + uint32_t gpcr_reg_addr; + + /* counting the gpio control register's base address */ + gpcr_reg = ((gpio_config->reg_addr & 0xff) - 1) * NUM_IO_MAX + + gpcr_offset; + gpcr_reg_addr = gpcr_reg | (gpio_config->reg_addr & 0xffffff00); + if (!(flags & GPIO_SINGLE_ENDED)) { + /* Pin open-source/open-drain is not supported */ + 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 (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); + } + ite_write(gpcr_reg_addr + pin, 1, GPIO_DIR_OUTPUT); + } else { + if ((flags & GPIO_PULL_DOWN) || (flags & GPIO_PULL_UP)) { + return -ENOTSUP; + } + ite_write(gpcr_reg_addr + pin, 1, GPIO_DIR_INPUT); + } + return 0; +} + +static int gpio_ite_port_get_raw(const struct device *dev, + gpio_port_value_t *value) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + + *value = ite_read(gpio_config->reg_addr, 1); + return 0; +} + +static int gpio_ite_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, gpio_port_value_t value) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + uint32_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_ite_port_set_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + uint32_t port_val; + + port_val = get_port(gpio_config); + port_val |= pins; + set_port(gpio_config, port_val); + return 0; +} + +static int gpio_ite_port_clear_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + uint32_t port_val; + + port_val = get_port(gpio_config); + port_val &= ~pins; + set_port(gpio_config, port_val); + return 0; +} + +static int gpio_ite_port_toggle_bits(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + uint32_t port_val; + + port_val = get_port(gpio_config); + port_val ^= pins; + set_port(gpio_config, port_val); + return 0; +} + +static int gpio_ite_manage_callback(const struct device *dev, + struct gpio_callback *callback, + bool set) +{ + struct gpio_ite_data *data = DEV_GPIO_DATA(dev); + + return gpio_manage_callback(&data->callbacks, callback, set); +} + +static void gpio_ite_isr(const void *arg) +{ + int irq_index = 0; + int gpio_index = 0; + const struct device *dev = arg; + struct gpio_ite_wui *wui_local; + struct gpio_ite_reg_table *gpio_tab_local; + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + struct gpio_ite_data *data = DEV_GPIO_DATA(dev); + + + for (gpio_index = 0; gpio_index < CURR_SUPP_GPIO_SET; gpio_index++) { + if (gpio_config->reg_addr == gpiox_reg[gpio_index].base_addr) { + gpio_tab_local = &gpiox_reg[gpio_index]; + } + } + for (irq_index = 0; irq_index < NUM_IO_MAX; irq_index++) { + wui_local = (struct gpio_ite_wui *)& + gpio_tab_local->wui[irq_index]; + if ((*wui_local->clear_addr)&wui_local->pin) { + SET_MASK(*wui_local->clear_addr, wui_local->pin); + gpio_fire_callbacks(&data->callbacks, dev, + BIT(wui_local->pin)); + } + } +} + +static int gpio_ite_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + int ret = 0; + int gpio_index = 0; + uint32_t g, i; + uint8_t both_tri_en = 0; + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + struct gpio_ite_wui *wui_local; + volatile uint8_t *trig_mode; + volatile uint8_t *hl_trig; + + g = gpio_config->gpio_irq[pin] / NUM_IO_MAX; + i = gpio_config->gpio_irq[pin] % NUM_IO_MAX; + trig_mode = reg_ielmr[g]; + hl_trig = reg_ipolr[g]; + + if (mode & GPIO_INT_MODE_DISABLED) { + /* Disables interrupt for a pin. */ + ite_intc_irq_disable(gpio_config->gpio_irq[pin]); + return ret; + } else if (mode & GPIO_INT_MODE_EDGE) { + /* edge trigger */ + SET_MASK(*trig_mode, BIT(i)); + } else { + /* level trigger */ + CLEAR_MASK(*trig_mode, BIT(i)); + } + /* both */ + if ((trig & GPIO_INT_TRIG_LOW) && (trig & GPIO_INT_TRIG_HIGH)) { + both_tri_en = 1; + } else { + if (trig & GPIO_INT_TRIG_LOW) { + SET_MASK(*hl_trig, BIT(i)); + } else if (trig & GPIO_INT_TRIG_HIGH) { + CLEAR_MASK(*hl_trig, BIT(i)); + } + } + + /* set wui , only gpiob gpiof, currently */ + for (gpio_index = 0; gpio_index < CURR_SUPP_GPIO_SET; gpio_index++) { + if (gpio_config->reg_addr == + gpiox_reg[gpio_index].base_addr) { + wui_local = (struct gpio_ite_wui *) + &(gpiox_reg[gpio_index].wui[pin]); + SET_MASK(*wui_local->reg_addr, wui_local->pin); + SET_MASK(*wui_local->clear_addr, wui_local->pin); + if (both_tri_en == 1) { + SET_MASK(*wui_local->reg_addr, wui_local->pin); + } + } + } + + /* Enable IRQ */ + ret = irq_connect_dynamic( + gpio_config->gpio_irq[pin], 0, gpio_ite_isr, dev, 0); + ite_intc_irq_enable(gpio_config->gpio_irq[pin]); + return ret; +} + +static const struct gpio_driver_api gpio_ite_driver_api = { + .pin_configure = gpio_ite_configure, + .port_get_raw = gpio_ite_port_get_raw, + .port_set_masked_raw = gpio_ite_port_set_masked_raw, + .port_set_bits_raw = gpio_ite_port_set_bits_raw, + .port_clear_bits_raw = gpio_ite_port_clear_bits_raw, + .port_toggle_bits = gpio_ite_port_toggle_bits, + .pin_interrupt_configure = gpio_ite_pin_interrupt_configure, + .manage_callback = gpio_ite_manage_callback, +}; + +static int gpio_ite_init(const struct device *dev) +{ + const struct gpio_ite_cfg *gpio_config = DEV_GPIO_CFG(dev); + int i; + + for (i = 0; i < NUM_IO_MAX; i++) { + /* init disable intc */ + ite_intc_irq_disable(gpio_config->gpio_irq[i]); + } + + return 0; +} + +#define GPIO_ITE_DEV_CFG_DATA(n, idx) \ +static struct gpio_ite_data gpio_ite_data_##n##_##idx; \ +static const struct gpio_ite_cfg gpio_ite_cfg_##n##_##idx = { \ + .reg_addr = DT_INST_REG_ADDR(idx),\ + .gpio_irq[0] = DT_INST_IRQ_BY_IDX(idx, 0, irq), \ + }; \ +DEVICE_AND_API_INIT(gpio_it8xxx2_dev_##n##_##idx, \ + DT_PROP(DT_NODELABEL(n), label), \ + gpio_ite_init, \ + &gpio_ite_data_##n##_##idx, \ + &gpio_ite_cfg_##n##_##idx, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &gpio_ite_driver_api) +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpiob), okay) +GPIO_ITE_DEV_CFG_DATA(gpiob, 0); +#endif /* gpiob */ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpiof), okay) +GPIO_ITE_DEV_CFG_DATA(gpiof, 1); +#endif /* gpiof */