diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index b1cd06978bb..34bdaebae27 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -89,6 +89,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_ALTERA_PIO gpio_altera_pio.c) zephyr_library_sources_ifdef(CONFIG_GPIO_BCM2711 gpio_bcm2711.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RA gpio_ra.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RZT2M gpio_rzt2m.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_AMBIQ gpio_ambiq.c) if (CONFIG_GPIO_EMUL_SDL) zephyr_library_sources(gpio_emul_sdl.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c49e2aa1167..960c7eb540e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -236,4 +236,6 @@ source "drivers/gpio/Kconfig.ra" source "drivers/gpio/Kconfig.rzt2m" +source "drivers/gpio/Kconfig.ambiq" + endif # GPIO diff --git a/drivers/gpio/Kconfig.ambiq b/drivers/gpio/Kconfig.ambiq new file mode 100644 index 00000000000..924028e6f27 --- /dev/null +++ b/drivers/gpio/Kconfig.ambiq @@ -0,0 +1,14 @@ +# Ambiq SDK GPIO +# +# Copyright (c) 2023 Antmicro +# +# SPDX-License-Identifier: Apache-2.0 +# + +config GPIO_AMBIQ + bool "AMBIQ GPIO driver" + default y + depends on DT_HAS_AMBIQ_GPIO_ENABLED + select AMBIQ_HAL + help + Enable driver for Ambiq gpio. diff --git a/drivers/gpio/gpio_ambiq.c b/drivers/gpio/gpio_ambiq.c new file mode 100644 index 00000000000..ebf2176ff32 --- /dev/null +++ b/drivers/gpio/gpio_ambiq.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2023 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ambiq_gpio + +#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 pin_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->pin_offset; + + 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->pin_offset; + + 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; + + if (inputs != NULL) { + for (int i = 0; i < dev_cfg->ngpios; i++) { + if ((map >> i) & 1) { + am_hal_gpio_pinconfig_get(i + dev_cfg->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 + dev_cfg->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->pin_offset)); + + 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; + + for (int i = 0; i < dev_cfg->ngpios; i++) { + if ((mask >> i) & 1) { + am_hal_gpio_state_write(i + dev_cfg->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; + + for (int i = 0; i < dev_cfg->ngpios; i++) { + if ((pins >> i) & 1) { + am_hal_gpio_state_write(i + dev_cfg->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; + + for (int i = 0; i < dev_cfg->ngpios; i++) { + if ((pins >> i) & 1) { + am_hal_gpio_state_write(i + dev_cfg->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; + + for (int i = 0; i < dev_cfg->ngpios; i++) { + if ((pins >> i) & 1) { + am_hal_gpio_state_write(i + dev_cfg->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->pin_offset; + 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); + + irq_disable(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_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: + 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)), \ + .pin_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) diff --git a/dts/bindings/gpio/ambiq,gpio.yaml b/dts/bindings/gpio/ambiq,gpio.yaml new file mode 100644 index 00000000000..b6e090ad064 --- /dev/null +++ b/dts/bindings/gpio/ambiq,gpio.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +description: Ambiq GPIO + +compatible: "ambiq,gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + description: GPIO pin offset + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags