diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 6bd3243ef37..5b42a99c65a 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -12,6 +12,7 @@ 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_KSCAN_ITE_IT8XXX2 gpio_kscan_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23S17 gpio_mcp23s17.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23XXX gpio_mcp23xxx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23SXX gpio_mcp23sxx.c) diff --git a/drivers/gpio/Kconfig.it8xxx2 b/drivers/gpio/Kconfig.it8xxx2 index ff288446fca..4bf21961ce1 100644 --- a/drivers/gpio/Kconfig.it8xxx2 +++ b/drivers/gpio/Kconfig.it8xxx2 @@ -7,3 +7,16 @@ config GPIO_ITE_IT8XXX2 depends on DT_HAS_ITE_IT8XXX2_GPIO_ENABLED help Enable driver for the ite GPIO controller. + +config GPIO_KSCAN_ITE_IT8XXX2 + bool "ITE IT8XXX2 GPIO KSCAN driver" + default y + depends on DT_HAS_ITE_IT8XXX2_GPIOKSCAN_ENABLED + help + Enable GPIO KSCAN driver for ITE chip it8xxx2. + IT8XXX2 support 8 KSI pins and 18 KSO pins, all of them can be configured + to GPIO mode. + KSI[7:0] and KSO[15:0] pins are configured to GPIO mode by + this GPIO_KSCAN_ITE_IT8XXX2 driver. + KSO[17:16] corresponds to GPC5 and GPC3 pin, so they can be configured to + gpio mode by original GPIO_ITE_IT8XXX2 driver. diff --git a/drivers/gpio/gpio_kscan_ite_it8xxx2.c b/drivers/gpio/gpio_kscan_ite_it8xxx2.c new file mode 100644 index 00000000000..31fa3d2ae3a --- /dev/null +++ b/drivers/gpio/gpio_kscan_ite_it8xxx2.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2022 ITE Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it8xxx2_gpiokscan + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_kscan_cfg { + /* The gpio_driver_config needs to be first */ + struct gpio_driver_config common; + /* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio output enable register (bit mapping to pin) */ + volatile uint8_t *reg_ksi_kso_goen; + /* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio control register (bit mapping to pin) */ + volatile uint8_t *reg_ksi_kso_gctrl; + /* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio data register (bit mapping to pin) */ + volatile uint8_t *reg_ksi_kso_gdat; + /* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio data mirror register (bit mapping to pin) */ + volatile uint8_t *reg_ksi_kso_gdmr; + /* KSI[7:0]/KSO[15:8]/KSO[7:0] port gpio open drain register (bit mapping to pin) */ + volatile uint8_t *reg_ksi_kso_gpod; +}; + +struct gpio_kscan_data { + /* The gpio_driver_data needs to be first */ + struct gpio_driver_data common; +}; + +static int gpio_kscan_it8xxx2_configure(const struct device *dev, + gpio_pin_t pin, + gpio_flags_t flags) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gctrl = config->reg_ksi_kso_gctrl; + volatile uint8_t *reg_ksi_kso_goen = config->reg_ksi_kso_goen; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + volatile uint8_t *reg_ksi_kso_gpod = config->reg_ksi_kso_gpod; + uint8_t mask = BIT(pin); + + /* KSI[7:0]/KSO[15:8]/KSO[7:0] pins don't support open source, 1.8V and 5.0V mode */ + if ((((flags & GPIO_SINGLE_ENDED) != 0) && ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) || + ((flags & IT8XXX2_GPIO_VOLTAGE_MASK) == IT8XXX2_GPIO_VOLTAGE_1P8) || + ((flags & IT8XXX2_GPIO_VOLTAGE_MASK) == IT8XXX2_GPIO_VOLTAGE_5P0)) { + return -ENOTSUP; + } + + /* Set GPIO mode */ + *reg_ksi_kso_gctrl |= mask; + + if (flags & GPIO_OUTPUT) { + /* + * Select open drain first, so that we don't glitch the signal + * when changing the line to an output. + */ + if (flags & GPIO_OPEN_DRAIN) { + /* Set open-drain and enable internal pullup */ + *reg_ksi_kso_gpod |= mask; + } else { + /* Set push-pull and disable internal pullup */ + *reg_ksi_kso_gpod &= ~mask; + } + + /* Set level before change to output */ + if (flags & GPIO_OUTPUT_INIT_HIGH) { + *reg_ksi_kso_gdat |= mask; + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + *reg_ksi_kso_gdat &= ~mask; + } + + /* Set output mode */ + *reg_ksi_kso_goen |= mask; + } else { + /* Set input mode */ + *reg_ksi_kso_goen &= ~mask; + + if (flags & GPIO_PULL_UP) { + /* Enable internal pullup */ + *reg_ksi_kso_gpod |= mask; + } else { + /* No internal pullup and pulldown */ + *reg_ksi_kso_gpod &= ~mask; + } + } + + return 0; +} + +#ifdef CONFIG_GPIO_GET_CONFIG +static int gpio_kscan_it8xxx2_get_config(const struct device *dev, + gpio_pin_t pin, + gpio_flags_t *out_flags) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_goen = config->reg_ksi_kso_goen; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + volatile uint8_t *reg_ksi_kso_gpod = config->reg_ksi_kso_gpod; + uint8_t mask = BIT(pin); + gpio_flags_t flags = 0; + + /* KSI[7:0]/KSO[15:8]/KSO[7:0] pins only support 3.3V */ + flags |= IT8XXX2_GPIO_VOLTAGE_3P3; + + /* Input or output */ + if (*reg_ksi_kso_goen & mask) { + flags |= GPIO_OUTPUT; + + /* Open-drain or push-pull */ + if (*reg_ksi_kso_gpod & mask) { + flags |= GPIO_OPEN_DRAIN; + } + + /* High or low */ + if (*reg_ksi_kso_gdat & mask) { + flags |= GPIO_OUTPUT_HIGH; + } else { + flags |= GPIO_OUTPUT_LOW; + } + } else { + flags |= GPIO_INPUT; + + /* pullup or no pull */ + if (*reg_ksi_kso_gpod & mask) { + flags |= GPIO_PULL_UP; + } + } + + *out_flags = flags; + + return 0; +} +#endif + +static int gpio_kscan_it8xxx2_port_get_raw(const struct device *dev, + gpio_port_value_t *value) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gdmr = config->reg_ksi_kso_gdmr; + + /* Get physical level from all pins of the port */ + *value = *reg_ksi_kso_gdmr; + + return 0; +} + +static int gpio_kscan_it8xxx2_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + uint8_t out = *reg_ksi_kso_gdat; + + /* Set high/low level to mask pins of the port */ + *reg_ksi_kso_gdat = ((out & ~mask) | (value & mask)); + + return 0; +} + +static int gpio_kscan_it8xxx2_port_set_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + + /* Set high level to pins of the port */ + *reg_ksi_kso_gdat |= pins; + + return 0; +} + +static int gpio_kscan_it8xxx2_port_clear_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + + /* Set low level to pins of the port */ + *reg_ksi_kso_gdat &= ~pins; + + return 0; +} + +static int gpio_kscan_it8xxx2_port_toggle_bits(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_kscan_cfg *const config = dev->config; + volatile uint8_t *reg_ksi_kso_gdat = config->reg_ksi_kso_gdat; + + /* Toggle output level to pins of the port */ + *reg_ksi_kso_gdat ^= pins; + + return 0; +} + +static int gpio_kscan_it8xxx2_init(const struct device *dev) +{ + return 0; +} + +static const struct gpio_driver_api gpio_kscan_it8xxx2_driver_api = { + .pin_configure = gpio_kscan_it8xxx2_configure, +#ifdef CONFIG_GPIO_GET_CONFIG + .pin_get_config = gpio_kscan_it8xxx2_get_config, +#endif + .port_get_raw = gpio_kscan_it8xxx2_port_get_raw, + .port_set_masked_raw = gpio_kscan_it8xxx2_port_set_masked_raw, + .port_set_bits_raw = gpio_kscan_it8xxx2_port_set_bits_raw, + .port_clear_bits_raw = gpio_kscan_it8xxx2_port_clear_bits_raw, + .port_toggle_bits = gpio_kscan_it8xxx2_port_toggle_bits, +}; + +#define GPIO_KSCAN_IT8XXX2_INIT(inst) \ +static const struct gpio_kscan_cfg gpio_kscan_it8xxx2_cfg_##inst = { \ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_NGPIOS( \ + DT_INST_PROP(inst, ngpios)) \ + }, \ + .reg_ksi_kso_goen = (uint8_t *)DT_INST_REG_ADDR_BY_NAME(inst, goen), \ + .reg_ksi_kso_gctrl = (uint8_t *)DT_INST_REG_ADDR_BY_NAME(inst, gctrl), \ + .reg_ksi_kso_gdat = (uint8_t *)DT_INST_REG_ADDR_BY_NAME(inst, gdat), \ + .reg_ksi_kso_gdmr = (uint8_t *)DT_INST_REG_ADDR_BY_NAME(inst, gdmr), \ + .reg_ksi_kso_gpod = (uint8_t *)DT_INST_REG_ADDR_BY_NAME(inst, gpod), \ +}; \ + \ +static struct gpio_kscan_data gpio_kscan_it8xxx2_data_##inst; \ + \ +DEVICE_DT_INST_DEFINE(inst, \ + gpio_kscan_it8xxx2_init, \ + NULL, \ + &gpio_kscan_it8xxx2_data_##inst, \ + &gpio_kscan_it8xxx2_cfg_##inst, \ + PRE_KERNEL_1, \ + CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_kscan_it8xxx2_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_KSCAN_IT8XXX2_INIT) diff --git a/dts/bindings/gpio/ite,it8xxx2-gpiokscan.yaml b/dts/bindings/gpio/ite,it8xxx2-gpiokscan.yaml new file mode 100644 index 00000000000..bca75c534aa --- /dev/null +++ b/dts/bindings/gpio/ite,it8xxx2-gpiokscan.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2022 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: This binding gives a base representation of the ITE kscan pins as gpio + +compatible: "ite,it8xxx2-gpiokscan" + +include: [base.yaml, gpio-controller.yaml] + +properties: + reg: + required: true + +gpio-cells: + - pin + - flags diff --git a/dts/riscv/ite/it81xx2.dtsi b/dts/riscv/ite/it81xx2.dtsi index ad17f3ee6cb..67c040b4531 100644 --- a/dts/riscv/ite/it81xx2.dtsi +++ b/dts/riscv/ite/it81xx2.dtsi @@ -13,6 +13,45 @@ reg = <0x00f01600 0x100>; }; + gpioksi: gpiokscan@f01d07 { + compatible = "ite,it8xxx2-gpiokscan"; + reg = <0x00f01d07 1 + 0x00f01d06 1 + 0x00f01d08 1 + 0x00f01d09 1 + 0x00f01d26 1>; + reg-names = "goen", "gctrl", "gdat", "gdmr", "gpod"; + ngpios = <8>; + gpio-controller; + #gpio-cells = <2>; + }; + + gpioksoh: gpiokscan@f01d0b { + compatible = "ite,it8xxx2-gpiokscan"; + reg = <0x00f01d0b 1 + 0x00f01d0a 1 + 0x00f01d01 1 + 0x00f01d0c 1 + 0x00f01d27 1>; + reg-names = "goen", "gctrl", "gdat", "gdmr", "gpod"; + ngpios = <8>; + gpio-controller; + #gpio-cells = <2>; + }; + + gpioksol: gpiokscan@f01d0e { + compatible = "ite,it8xxx2-gpiokscan"; + reg = <0x00f01d0e 1 + 0x00f01d0d 1 + 0x00f01d00 1 + 0x00f01d0f 1 + 0x00f01d28 1>; + reg-names = "goen", "gctrl", "gdat", "gdmr", "gpod"; + ngpios = <8>; + gpio-controller; + #gpio-cells = <2>; + }; + pinctrl: pin-controller { compatible = "ite,it8xxx2-pinctrl"; #address-cells = <1>;