/* * 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 #include #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; default: return -EINVAL; } } /* 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])); } } } #ifdef CONFIG_GPIO_GET_DIRECTION static int gpio_eos_s3_port_get_direction(const struct device *port, gpio_port_pins_t map, gpio_port_pins_t *inputs, gpio_port_pins_t *outputs) { uint32_t pin; PadConfig pad_config; gpio_port_pins_t ip = 0; gpio_port_pins_t op = 0; const struct gpio_eos_s3_config *config = dev->config; map &= config->common.port_pin_mask; if (inputs != NULL) { for (pin = find_lsb_set(pins) - 1; pins; pins &= ~BIT(pin), pin = find_lsb_set(pins) - 1) { pad_config = gpio_eos_s3_pad_select(port, pin); ip |= (pad_config.ucMode == PAD_MODE_INPUT_EN && pad_config.ucSmtTrg == PAD_SMT_TRIG_EN) * BIT(pin); } *inputs = ip; } if (outputs != NULL) { for (pin = find_lsb_set(pins) - 1; pins; pins &= ~BIT(pin), pin = find_lsb_set(pins) - 1) { pad_config = gpio_eos_s3_pad_select(port, pin); op |= (pad_config.ucMode == PAD_MODE_OUTPUT_EN) * BIT(pin); } *outputs = op; } return 0; } #endif /* CONFIG_GPIO_GET_DIRECTION */ 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, #ifdef CONFIG_GPIO_GET_DIRECTION .port_get_direction = gpio_eos_s3_port_get_direction, #endif /* CONFIG_GPIO_GET_DIRECTION */ }; 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, NULL, &gpio_eos_s3_data, &gpio_eos_s3_config, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, &gpio_eos_s3_driver_api);