/* * Copyright (c) 2023 Antmicro * Copyright (c) 2023 Ambiq Micro Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ambiq_gpio_bank #include #include #include #include #include #include #include typedef void (*ambiq_gpio_cfg_func_t)(void); struct ambiq_gpio_config { struct gpio_driver_config common; uint32_t base; uint32_t offset; uint32_t irq_num; ambiq_gpio_cfg_func_t cfg_func; uint8_t ngpios; }; struct ambiq_gpio_data { struct gpio_driver_data common; sys_slist_t cb; struct k_spinlock lock; }; static int ambiq_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { const struct ambiq_gpio_config *const dev_cfg = dev->config; pin += (dev_cfg->offset >> 2); am_hal_gpio_pincfg_t pincfg = am_hal_gpio_pincfg_default; if (flags & GPIO_INPUT) { pincfg = am_hal_gpio_pincfg_input; if (flags & GPIO_PULL_UP) { pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLUP_50K; } else if (flags & GPIO_PULL_DOWN) { pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLDOWN_50K; } } if (flags & GPIO_OUTPUT) { if (flags & GPIO_SINGLE_ENDED) { if (flags & GPIO_LINE_OPEN_DRAIN) { pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN; } } else { pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL; } } if (flags & GPIO_DISCONNECTED) { pincfg = am_hal_gpio_pincfg_disabled; } if (flags & GPIO_OUTPUT_INIT_HIGH) { pincfg.GP.cfg_b.eCEpol = AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH; am_hal_gpio_state_write(pin, AM_HAL_GPIO_OUTPUT_SET); } else if (flags & GPIO_OUTPUT_INIT_LOW) { pincfg.GP.cfg_b.eCEpol = AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW; am_hal_gpio_state_write(pin, AM_HAL_GPIO_OUTPUT_CLEAR); } am_hal_gpio_pinconfig(pin, pincfg); return 0; } #ifdef CONFIG_GPIO_GET_CONFIG static int ambiq_gpio_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags) { const struct ambiq_gpio_config *const dev_cfg = dev->config; am_hal_gpio_pincfg_t pincfg; pin += (dev_cfg->offset >> 2); am_hal_gpio_pinconfig_get(pin, &pincfg); if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_DISABLE && pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_NONE) { *out_flags = GPIO_DISCONNECTED; } if (pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) { *out_flags = GPIO_INPUT; if (pincfg.GP.cfg_b.ePullup == AM_HAL_GPIO_PIN_PULLUP_50K) { *out_flags |= GPIO_PULL_UP; } else if (pincfg.GP.cfg_b.ePullup == AM_HAL_GPIO_PIN_PULLDOWN_50K) { *out_flags |= GPIO_PULL_DOWN; } } if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL) { *out_flags = GPIO_OUTPUT | GPIO_PUSH_PULL; if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH) { *out_flags |= GPIO_OUTPUT_HIGH; } else if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW) { *out_flags |= GPIO_OUTPUT_LOW; } } if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN) { *out_flags = GPIO_OUTPUT | GPIO_OPEN_DRAIN; if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH) { *out_flags |= GPIO_OUTPUT_HIGH; } else if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW) { *out_flags |= GPIO_OUTPUT_LOW; } } return 0; } #endif #ifdef CONFIG_GPIO_GET_DIRECTION static int ambiq_gpio_port_get_direction(const struct device *dev, gpio_port_pins_t map, gpio_port_pins_t *inputs, gpio_port_pins_t *outputs) { const struct ambiq_gpio_config *const dev_cfg = dev->config; am_hal_gpio_pincfg_t pincfg; gpio_port_pins_t ip = 0; gpio_port_pins_t op = 0; uint32_t pin_offset = dev_cfg->offset >> 2; if (inputs != NULL) { for (int i = 0; i < dev_cfg->ngpios; i++) { if ((map >> i) & 1) { am_hal_gpio_pinconfig_get(i + pin_offset, &pincfg); if (pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) { ip |= BIT(i); } } } *inputs = ip; } if (outputs != NULL) { for (int i = 0; i < dev_cfg->ngpios; i++) { if ((map >> i) & 1) { am_hal_gpio_pinconfig_get(i + pin_offset, &pincfg); if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL || pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN) { op |= BIT(i); } } } *outputs = op; } return 0; } #endif static int ambiq_gpio_port_get_raw(const struct device *dev, gpio_port_value_t *value) { const struct ambiq_gpio_config *const dev_cfg = dev->config; *value = (*AM_HAL_GPIO_RDn(dev_cfg->offset >> 2)); return 0; } static int ambiq_gpio_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, gpio_port_value_t value) { const struct ambiq_gpio_config *const dev_cfg = dev->config; uint32_t pin_offset = dev_cfg->offset >> 2; for (int i = 0; i < dev_cfg->ngpios; i++) { if ((mask >> i) & 1) { am_hal_gpio_state_write(i + pin_offset, ((value >> i) & 1)); } } return 0; } static int ambiq_gpio_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) { const struct ambiq_gpio_config *const dev_cfg = dev->config; uint32_t pin_offset = dev_cfg->offset >> 2; for (int i = 0; i < dev_cfg->ngpios; i++) { if ((pins >> i) & 1) { am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_SET); } } return 0; } static int ambiq_gpio_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) { const struct ambiq_gpio_config *const dev_cfg = dev->config; uint32_t pin_offset = dev_cfg->offset >> 2; for (int i = 0; i < dev_cfg->ngpios; i++) { if ((pins >> i) & 1) { am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_CLEAR); } } return 0; } static int ambiq_gpio_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) { const struct ambiq_gpio_config *const dev_cfg = dev->config; uint32_t pin_offset = dev_cfg->offset >> 2; for (int i = 0; i < dev_cfg->ngpios; i++) { if ((pins >> i) & 1) { am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_TOGGLE); } } return 0; } static void ambiq_gpio_isr(const struct device *dev) { struct ambiq_gpio_data *const data = dev->data; const struct ambiq_gpio_config *const dev_cfg = dev->config; uint32_t int_status; am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status); am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status); gpio_fire_callbacks(&data->cb, dev, int_status); } static int ambiq_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { const struct ambiq_gpio_config *const dev_cfg = dev->config; struct ambiq_gpio_data *const data = dev->data; am_hal_gpio_pincfg_t pincfg; int gpio_pin = pin + (dev_cfg->offset >> 2); uint32_t int_status; int ret; ret = am_hal_gpio_pinconfig_get(gpio_pin, &pincfg); if (mode == GPIO_INT_MODE_DISABLED) { pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_NONE; ret = am_hal_gpio_pinconfig(gpio_pin, pincfg); k_spinlock_key_t key = k_spin_lock(&data->lock); ret = am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status); ret = am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status); ret = am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0, AM_HAL_GPIO_INT_CTRL_INDV_DISABLE, (void *)&gpio_pin); k_spin_unlock(&data->lock, key); } else { if (mode == GPIO_INT_MODE_LEVEL) { return -ENOTSUP; } switch (trig) { case GPIO_INT_TRIG_LOW: pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_HI2LO; break; case GPIO_INT_TRIG_HIGH: pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI; break; case GPIO_INT_TRIG_BOTH: /* * GPIO_INT_TRIG_BOTH is not supported on Ambiq Apollo4 Plus Platform * ERR008: GPIO: Dual-edge interrupts are not vectoring */ return -ENOTSUP; } ret = am_hal_gpio_pinconfig(gpio_pin, pincfg); irq_enable(dev_cfg->irq_num); k_spinlock_key_t key = k_spin_lock(&data->lock); ret = am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status); ret = am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status); ret = am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0, AM_HAL_GPIO_INT_CTRL_INDV_ENABLE, (void *)&gpio_pin); k_spin_unlock(&data->lock, key); } return ret; } static int ambiq_gpio_manage_callback(const struct device *dev, struct gpio_callback *callback, bool set) { struct ambiq_gpio_data *const data = dev->data; return gpio_manage_callback(&data->cb, callback, set); } static int ambiq_gpio_init(const struct device *port) { const struct ambiq_gpio_config *const dev_cfg = port->config; NVIC_ClearPendingIRQ(dev_cfg->irq_num); dev_cfg->cfg_func(); return 0; } static const struct gpio_driver_api ambiq_gpio_drv_api = { .pin_configure = ambiq_gpio_pin_configure, #ifdef CONFIG_GPIO_GET_CONFIG .pin_get_config = ambiq_gpio_get_config, #endif .port_get_raw = ambiq_gpio_port_get_raw, .port_set_masked_raw = ambiq_gpio_port_set_masked_raw, .port_set_bits_raw = ambiq_gpio_port_set_bits_raw, .port_clear_bits_raw = ambiq_gpio_port_clear_bits_raw, .port_toggle_bits = ambiq_gpio_port_toggle_bits, .pin_interrupt_configure = ambiq_gpio_pin_interrupt_configure, .manage_callback = ambiq_gpio_manage_callback, #ifdef CONFIG_GPIO_GET_DIRECTION .port_get_direction = ambiq_gpio_port_get_direction, #endif }; #define AMBIQ_GPIO_DEFINE(n) \ static struct ambiq_gpio_data ambiq_gpio_data_##n; \ static void ambiq_gpio_cfg_func_##n(void); \ \ static const struct ambiq_gpio_config ambiq_gpio_config_##n = { \ .common = \ { \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ }, \ .base = DT_REG_ADDR(DT_INST_PARENT(n)), \ .offset = DT_INST_REG_ADDR(n), \ .ngpios = DT_INST_PROP(n, ngpios), \ .irq_num = DT_INST_IRQN(n), \ .cfg_func = ambiq_gpio_cfg_func_##n}; \ static void ambiq_gpio_cfg_func_##n(void) \ { \ \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ambiq_gpio_isr, \ DEVICE_DT_INST_GET(n), 0); \ \ return; \ }; \ \ DEVICE_DT_INST_DEFINE(n, &ambiq_gpio_init, NULL, &ambiq_gpio_data_##n, \ &ambiq_gpio_config_##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \ &ambiq_gpio_drv_api); DT_INST_FOREACH_STATUS_OKAY(AMBIQ_GPIO_DEFINE)