From 35dd66b6c458e206378c14cbf517b77de20a7d5e Mon Sep 17 00:00:00 2001 From: Wojciech Tatarski Date: Tue, 4 Feb 2020 11:23:26 +0100 Subject: [PATCH] drivers: gpio: add EOS S3 GPIO driver Add GPIO driver for QuickLogic EOS S3 SoC. Co-authored-by: Jan Kowalewski Signed-off-by: Wojciech Tatarski Signed-off-by: Jan Kowalewski --- CODEOWNERS | 1 + drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.eos_s3 | 11 + drivers/gpio/gpio_eos_s3.c | 408 ++++++++++++++++++++ soc/arm/quicklogic_eos_s3/Kconfig.defconfig | 7 + 6 files changed, 430 insertions(+) create mode 100644 drivers/gpio/Kconfig.eos_s3 create mode 100644 drivers/gpio/gpio_eos_s3.c diff --git a/CODEOWNERS b/CODEOWNERS index 4ba9ccf7629..75c4c64d83a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -207,6 +207,7 @@ /drivers/gpio/*lmp90xxx* @henrikbrixandersen /drivers/gpio/*stm32* @erwango /drivers/gpio/*sx1509b* @pabigot +/drivers/gpio/*eos_s3* @wtatarski @kowalewskijan @kgugala /drivers/hwinfo/ @alexanderwachter /drivers/i2s/i2s_ll_stm32* @avisconti /drivers/i2c/i2c_common.c @sjg20 diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 07b7e543497..9b302523ccc 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -36,6 +36,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NPCX gpio_npcx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_EMUL gpio_emul.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PSOC6 gpio_psoc6.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL6408A gpio_pcal6408a.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_EOS_S3 gpio_eos_s3.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 13a1a3b34b3..5217c061e9a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -87,4 +87,6 @@ source "drivers/gpio/Kconfig.psoc6" source "drivers/gpio/Kconfig.pcal6408a" +source "drivers/gpio/Kconfig.eos_s3" + endif # GPIO diff --git a/drivers/gpio/Kconfig.eos_s3 b/drivers/gpio/Kconfig.eos_s3 new file mode 100644 index 00000000000..372543d3666 --- /dev/null +++ b/drivers/gpio/Kconfig.eos_s3 @@ -0,0 +1,11 @@ +# EOS_S3 GPIO configuration options + +# Copyright (c) 2020 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_EOS_S3 + bool "EOS_S3 GPIO driver" + depends on EOS_S3_HAL + select HAS_DTS_GPIO + help + Enable the EOS S3 gpio driver. diff --git a/drivers/gpio/gpio_eos_s3.c b/drivers/gpio/gpio_eos_s3.c new file mode 100644 index 00000000000..a950fac7937 --- /dev/null +++ b/drivers/gpio/gpio_eos_s3.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2020, Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT quicklogic_eos_s3_gpio + +#include +#include +#include +#include +#include +#include + +#include "gpio_utils.h" + +#define MAX_GPIOS 8U +#define GPIOS_MASK (BIT(MAX_GPIOS) - 1) +#define DISABLED_GPIO_IRQ 0xFFU + +struct gpio_eos_s3_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + /* Pin configuration to determine whether use primary + * or secondary pin for a target GPIO. Secondary pin is used + * when the proper bit is set to 1. + * + * bit_index : primary_pin_number / secondary_pin_number + * + * 0 : 6 / 24 + * 1 : 9 / 26 + * 2 : 11 / 28 + * 3 : 14 / 30 + * 4 : 18 / 31 + * 5 : 21 / 36 + * 6 : 22 / 38 + * 7 : 23 / 45 + */ + uint8_t pin_secondary_config; +}; + +struct gpio_eos_s3_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + /* port ISR callback routine address */ + sys_slist_t callbacks; + /* array of interrupts mapped to the gpio number */ + uint8_t gpio_irqs[MAX_GPIOS]; +}; + +/* Connection table to configure GPIOs with pads */ +static const PadConfig pad_configs[] = { + {.ucPin = PAD_6, .ucFunc = PAD6_FUNC_SEL_GPIO_0}, + {.ucPin = PAD_9, .ucFunc = PAD9_FUNC_SEL_GPIO_1}, + {.ucPin = PAD_11, .ucFunc = PAD11_FUNC_SEL_GPIO_2}, + {.ucPin = PAD_14, .ucFunc = PAD14_FUNC_SEL_GPIO_3}, + {.ucPin = PAD_18, .ucFunc = PAD18_FUNC_SEL_GPIO_4}, + {.ucPin = PAD_21, .ucFunc = PAD21_FUNC_SEL_GPIO_5}, + {.ucPin = PAD_22, .ucFunc = PAD22_FUNC_SEL_GPIO_6}, + {.ucPin = PAD_23, .ucFunc = PAD23_FUNC_SEL_GPIO_7}, + {.ucPin = PAD_24, .ucFunc = PAD24_FUNC_SEL_GPIO_0}, + {.ucPin = PAD_26, .ucFunc = PAD26_FUNC_SEL_GPIO_1}, + {.ucPin = PAD_28, .ucFunc = PAD28_FUNC_SEL_GPIO_2}, + {.ucPin = PAD_30, .ucFunc = PAD30_FUNC_SEL_GPIO_3}, + {.ucPin = PAD_31, .ucFunc = PAD31_FUNC_SEL_GPIO_4}, + {.ucPin = PAD_36, .ucFunc = PAD36_FUNC_SEL_GPIO_5}, + {.ucPin = PAD_38, .ucFunc = PAD38_FUNC_SEL_GPIO_6}, + {.ucPin = PAD_45, .ucFunc = PAD45_FUNC_SEL_GPIO_7}, +}; + +static PadConfig gpio_eos_s3_pad_select(const struct device *dev, + uint8_t gpio_num) +{ + const struct gpio_eos_s3_config *config = dev->config; + uint8_t is_secondary = (config->pin_secondary_config >> gpio_num) & 1; + + return pad_configs[(MAX_GPIOS * is_secondary) + gpio_num]; +} + +/* This function maps pad number to IRQ number */ +static int gpio_eos_s3_get_irq_num(uint8_t pad) +{ + int gpio_irq_num; + + switch (pad) { + case PAD_6: + gpio_irq_num = 1; + break; + case PAD_9: + gpio_irq_num = 3; + break; + case PAD_11: + gpio_irq_num = 5; + break; + case PAD_14: + gpio_irq_num = 5; + break; + case PAD_18: + gpio_irq_num = 1; + break; + case PAD_21: + gpio_irq_num = 2; + break; + case PAD_22: + gpio_irq_num = 3; + break; + case PAD_23: + gpio_irq_num = 7; + break; + case PAD_24: + gpio_irq_num = 1; + break; + case PAD_26: + gpio_irq_num = 4; + break; + case PAD_28: + gpio_irq_num = 3; + break; + case PAD_30: + gpio_irq_num = 5; + break; + case PAD_31: + gpio_irq_num = 6; + break; + case PAD_36: + gpio_irq_num = 1; + break; + case PAD_38: + gpio_irq_num = 2; + break; + case PAD_45: + gpio_irq_num = 5; + break; + default: + return -EINVAL; + }; + + return gpio_irq_num; +} + +static int gpio_eos_s3_configure(const struct device *dev, + gpio_pin_t gpio_num, + gpio_flags_t flags) +{ + uint32_t *io_mux = (uint32_t *)IO_MUX; + GPIOCfgTypeDef gpio_cfg; + PadConfig pad_config = gpio_eos_s3_pad_select(dev, gpio_num); + + if (flags & GPIO_SINGLE_ENDED) { + return -ENOTSUP; + } + + gpio_cfg.ucGpioNum = gpio_num; + gpio_cfg.xPadConf = &pad_config; + + /* Configure PAD */ + if (flags & GPIO_PULL_UP) { + gpio_cfg.xPadConf->ucPull = PAD_PULLUP; + } else if (flags & GPIO_PULL_DOWN) { + gpio_cfg.xPadConf->ucPull = PAD_PULLDOWN; + } else { + /* High impedance */ + gpio_cfg.xPadConf->ucPull = PAD_NOPULL; + } + + if ((flags & GPIO_INPUT) != 0) { + gpio_cfg.xPadConf->ucMode = PAD_MODE_INPUT_EN; + gpio_cfg.xPadConf->ucSmtTrg = PAD_SMT_TRIG_EN; + } + + if ((flags & GPIO_OUTPUT) != 0) { + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { + MISC_CTRL->IO_OUTPUT |= (BIT(gpio_num) & GPIOS_MASK); + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { + MISC_CTRL->IO_OUTPUT &= ~(BIT(gpio_num) & GPIOS_MASK); + } + gpio_cfg.xPadConf->ucMode = PAD_MODE_OUTPUT_EN; + } + + if (flags == GPIO_DISCONNECTED) { + gpio_cfg.xPadConf->ucMode = PAD_MODE_INPUT_EN; + gpio_cfg.xPadConf->ucSmtTrg = PAD_SMT_TRIG_DIS; + } + + /* Initial PAD configuration */ + HAL_PAD_Config(gpio_cfg.xPadConf); + + /* Override direction setup to support bidirectional config */ + if ((flags & GPIO_DIR_MASK) == (GPIO_INPUT | GPIO_OUTPUT)) { + io_mux += gpio_cfg.xPadConf->ucPin; + *io_mux &= ~PAD_OEN_DISABLE; + *io_mux |= PAD_REN_ENABLE; + } + + return 0; +} + +static int gpio_eos_s3_port_get_raw(const struct device *dev, + uint32_t *value) +{ + ARG_UNUSED(dev); + + *value = (MISC_CTRL->IO_INPUT & GPIOS_MASK); + + return 0; +} + +static int gpio_eos_s3_port_set_masked_raw(const struct device *dev, + uint32_t mask, + uint32_t value) +{ + ARG_UNUSED(dev); + uint32_t target_value; + uint32_t output_states = MISC_CTRL->IO_OUTPUT; + + target_value = ((output_states & ~mask) | (value & mask)); + MISC_CTRL->IO_OUTPUT = (target_value & GPIOS_MASK); + + return 0; +} + +static int gpio_eos_s3_port_set_bits_raw(const struct device *dev, + uint32_t mask) +{ + ARG_UNUSED(dev); + + MISC_CTRL->IO_OUTPUT |= (mask & GPIOS_MASK); + + return 0; +} + +static int gpio_eos_s3_port_clear_bits_raw(const struct device *dev, + uint32_t mask) +{ + ARG_UNUSED(dev); + + MISC_CTRL->IO_OUTPUT &= ~(mask & GPIOS_MASK); + + return 0; +} + +static int gpio_eos_s3_port_toggle_bits(const struct device *dev, + uint32_t mask) +{ + ARG_UNUSED(dev); + uint32_t target_value; + uint32_t output_states = MISC_CTRL->IO_OUTPUT; + + target_value = output_states ^ mask; + MISC_CTRL->IO_OUTPUT = (target_value & GPIOS_MASK); + + return 0; +} + +static int gpio_eos_s3_manage_callback(const struct device *dev, + struct gpio_callback *callback, bool set) +{ + struct gpio_eos_s3_data *data = dev->data; + + return gpio_manage_callback(&data->callbacks, callback, set); +} + +static int gpio_eos_s3_pin_interrupt_configure(const struct device *dev, + gpio_pin_t gpio_num, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + struct gpio_eos_s3_data *data = dev->data; + GPIOCfgTypeDef gpio_cfg; + PadConfig pad_config = gpio_eos_s3_pad_select(dev, gpio_num); + + gpio_cfg.ucGpioNum = gpio_num; + gpio_cfg.xPadConf = &pad_config; + + if (mode == GPIO_INT_MODE_DISABLED) { + /* Get IRQ number which should be disabled */ + int irq_num = gpio_eos_s3_get_irq_num(pad_config.ucPin); + + if (irq_num < 0) { + return -EINVAL; + } + + /* Disable IRQ */ + INTR_CTRL->GPIO_INTR_EN_M4 &= ~BIT((uint32_t)irq_num); + + /* Mark corresponding IRQ number as disabled */ + data->gpio_irqs[irq_num] = DISABLED_GPIO_IRQ; + + /* Clear configuration */ + INTR_CTRL->GPIO_INTR_TYPE &= ~((uint32_t)(BIT(irq_num))); + INTR_CTRL->GPIO_INTR_POL &= ~((uint32_t)(BIT(irq_num))); + } else { + /* Prepare configuration */ + if (mode == GPIO_INT_MODE_LEVEL) { + gpio_cfg.intr_type = LEVEL_TRIGGERED; + if (trig == GPIO_INT_TRIG_LOW) { + gpio_cfg.pol_type = FALL_LOW; + } else { + gpio_cfg.pol_type = RISE_HIGH; + } + } else { + gpio_cfg.intr_type = EDGE_TRIGGERED; + switch (trig) { + case GPIO_INT_TRIG_LOW: + gpio_cfg.pol_type = FALL_LOW; + break; + case GPIO_INT_TRIG_HIGH: + gpio_cfg.pol_type = RISE_HIGH; + break; + case GPIO_INT_TRIG_BOTH: + return -ENOTSUP; + } + } + + /* Set IRQ configuration */ + int irq_num = HAL_GPIO_IntrCfg(&gpio_cfg); + + if (irq_num < 0) { + return -EINVAL; + } + + /* Set corresponding IRQ number as enabled */ + data->gpio_irqs[irq_num] = gpio_num; + + /* Clear pending GPIO interrupts */ + INTR_CTRL->GPIO_INTR |= BIT((uint32_t)irq_num); + + /* Enable IRQ */ + INTR_CTRL->GPIO_INTR_EN_M4 |= BIT((uint32_t)irq_num); + } + + return 0; +} + +static void gpio_eos_s3_isr(const struct device *dev) +{ + struct gpio_eos_s3_data *data = dev->data; + /* Level interrupts can be only checked from read-only GPIO_INTR_RAW, + * we need to add it to the intr_status. + */ + uint32_t intr_status = (INTR_CTRL->GPIO_INTR | INTR_CTRL->GPIO_INTR_RAW); + + /* Clear pending GPIO interrupts */ + INTR_CTRL->GPIO_INTR |= intr_status; + + /* Fire callbacks */ + for (int irq_num = 0; irq_num < MAX_GPIOS; irq_num++) { + if (data->gpio_irqs[irq_num] != DISABLED_GPIO_IRQ) { + gpio_fire_callbacks(&data->callbacks, + dev, BIT(data->gpio_irqs[irq_num])); + } + } +} + +static const struct gpio_driver_api gpio_eos_s3_driver_api = { + .pin_configure = gpio_eos_s3_configure, + .port_get_raw = gpio_eos_s3_port_get_raw, + .port_set_masked_raw = gpio_eos_s3_port_set_masked_raw, + .port_set_bits_raw = gpio_eos_s3_port_set_bits_raw, + .port_clear_bits_raw = gpio_eos_s3_port_clear_bits_raw, + .port_toggle_bits = gpio_eos_s3_port_toggle_bits, + .pin_interrupt_configure = gpio_eos_s3_pin_interrupt_configure, + .manage_callback = gpio_eos_s3_manage_callback, +}; + +static int gpio_eos_s3_init(const struct device *dev) +{ + ARG_UNUSED(dev); + IRQ_CONNECT(DT_INST_IRQN(0), + DT_INST_IRQ(0, priority), + gpio_eos_s3_isr, + DEVICE_DT_INST_GET(0), + 0); + + irq_enable(DT_INST_IRQN(0)); + + return 0; +} + +const struct gpio_eos_s3_config gpio_eos_s3_config = { + .common = { + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0), + }, + .pin_secondary_config = DT_INST_PROP(0, pin_secondary_config), +}; + +static struct gpio_eos_s3_data gpio_eos_s3_data = { + .gpio_irqs = { + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ, + DISABLED_GPIO_IRQ + }, +}; + +DEVICE_DT_INST_DEFINE(0, + gpio_eos_s3_init, + device_pm_control_nop, + &gpio_eos_s3_data, + &gpio_eos_s3_config, + POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_eos_s3_driver_api); diff --git a/soc/arm/quicklogic_eos_s3/Kconfig.defconfig b/soc/arm/quicklogic_eos_s3/Kconfig.defconfig index 107c5525ce8..6f325585859 100644 --- a/soc/arm/quicklogic_eos_s3/Kconfig.defconfig +++ b/soc/arm/quicklogic_eos_s3/Kconfig.defconfig @@ -28,4 +28,11 @@ config UART_PL011_PORT1 endif # SERIAL +if GPIO + +config GPIO_EOS_S3 + default y + +endif # GPIO + endif # SOC_EOS_S3