| /* |
| * Copyright (c) 2019 Brett Witherspoon |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ti_cc13xx_cc26xx_gpio |
| |
| #include <zephyr/types.h> |
| #include <sys/__assert.h> |
| #include <device.h> |
| #include <errno.h> |
| #include <drivers/gpio.h> |
| |
| #include <driverlib/gpio.h> |
| #include <driverlib/interrupt.h> |
| #include <driverlib/ioc.h> |
| #include <driverlib/prcm.h> |
| |
| #include <inc/hw_aon_event.h> |
| |
| #include <ti/drivers/Power.h> |
| #include <ti/drivers/power/PowerCC26XX.h> |
| |
| #include "gpio_utils.h" |
| |
| /* bits 16-18 in iocfg registers correspond to interrupt settings */ |
| #define IOCFG_INT_MASK 0x00070000 |
| |
| /* the rest are for general (non-interrupt) config */ |
| #define IOCFG_GEN_MASK (~IOCFG_INT_MASK) |
| |
| struct gpio_cc13xx_cc26xx_data { |
| /* gpio_driver_data needs to be first */ |
| struct gpio_driver_data common; |
| sys_slist_t callbacks; |
| }; |
| |
| static struct gpio_cc13xx_cc26xx_data gpio_cc13xx_cc26xx_data_0; |
| |
| static const struct gpio_driver_config gpio_cc13xx_cc26xx_cfg_0 = { |
| .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0), |
| }; |
| |
| static int gpio_cc13xx_cc26xx_port_set_bits_raw(const struct device *port, |
| uint32_t mask); |
| static int gpio_cc13xx_cc26xx_port_clear_bits_raw(const struct device *port, |
| uint32_t mask); |
| |
| static int gpio_cc13xx_cc26xx_config(const struct device *port, |
| gpio_pin_t pin, |
| gpio_flags_t flags) |
| { |
| uint32_t config = 0; |
| |
| __ASSERT_NO_MSG(pin < NUM_IO_MAX); |
| |
| switch (flags & (GPIO_INPUT | GPIO_OUTPUT)) { |
| case GPIO_INPUT: |
| config = IOC_INPUT_ENABLE; |
| break; |
| case GPIO_OUTPUT: |
| config = IOC_INPUT_DISABLE; |
| break; |
| case 0: /* disconnected */ |
| IOCPortConfigureSet(pin, IOC_PORT_GPIO, IOC_NO_IOPULL); |
| GPIO_setOutputEnableDio(pin, GPIO_OUTPUT_DISABLE); |
| return 0; |
| default: |
| return -ENOTSUP; |
| } |
| |
| config |= IOC_SLEW_DISABLE | IOC_NO_WAKE_UP; |
| |
| config |= (flags & GPIO_INT_DEBOUNCE) ? IOC_HYST_ENABLE : |
| IOC_HYST_DISABLE; |
| |
| /* |
| * The GPIO_DS_ALT_HIGH and GPIO_DS_ALT_LOW flags are for setting |
| * the highest drive strength for a GPIO in the output HIGH and |
| * output LOW states, respectively. Since only 1 drive strength |
| * setting is available for a GPIO (irrespective of output state), |
| * require both flags to be set for highest drive strength, default |
| * to low/auto drive strength. |
| * Not all GPIO support 8ma, but setting that bit will use the highest |
| * supported drive strength. |
| */ |
| switch (flags & (GPIO_DS_ALT_HIGH | GPIO_DS_ALT_LOW)) { |
| case 0: |
| config |= IOC_CURRENT_2MA | IOC_STRENGTH_AUTO; |
| break; |
| case (GPIO_DS_ALT_HIGH | GPIO_DS_ALT_LOW): |
| config |= IOC_CURRENT_8MA | IOC_STRENGTH_MAX; |
| break; |
| case GPIO_DS_ALT_HIGH: |
| case GPIO_DS_ALT_LOW: |
| return -ENOTSUP; |
| } |
| |
| switch (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) { |
| case 0: |
| config |= IOC_NO_IOPULL; |
| break; |
| case GPIO_PULL_UP: |
| config |= IOC_IOPULL_UP; |
| break; |
| case GPIO_PULL_DOWN: |
| config |= IOC_IOPULL_DOWN; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| config |= IOCPortConfigureGet(pin) & IOCFG_INT_MASK; |
| IOCPortConfigureSet(pin, IOC_PORT_GPIO, config); |
| |
| if ((flags & GPIO_OUTPUT) != 0) { |
| if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { |
| gpio_cc13xx_cc26xx_port_set_bits_raw(port, BIT(pin)); |
| } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { |
| gpio_cc13xx_cc26xx_port_clear_bits_raw(port, BIT(pin)); |
| } |
| GPIO_setOutputEnableDio(pin, GPIO_OUTPUT_ENABLE); |
| } else { |
| GPIO_setOutputEnableDio(pin, GPIO_OUTPUT_DISABLE); |
| } |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_port_get_raw(const struct device *port, |
| uint32_t *value) |
| { |
| __ASSERT_NO_MSG(value != NULL); |
| |
| *value = GPIO_readMultiDio(GPIO_DIO_ALL_MASK); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_port_set_masked_raw(const struct device *port, |
| uint32_t mask, |
| uint32_t value) |
| { |
| GPIO_setMultiDio(mask & value); |
| GPIO_clearMultiDio(mask & ~value); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_port_set_bits_raw(const struct device *port, |
| uint32_t mask) |
| { |
| GPIO_setMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_port_clear_bits_raw(const struct device *port, |
| uint32_t mask) |
| { |
| GPIO_clearMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_port_toggle_bits(const struct device *port, |
| uint32_t mask) |
| { |
| GPIO_toggleMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_pin_interrupt_configure(const struct device *port, |
| gpio_pin_t pin, |
| enum gpio_int_mode mode, |
| enum gpio_int_trig trig) |
| { |
| uint32_t config = 0; |
| |
| if (mode != GPIO_INT_MODE_DISABLED) { |
| if (mode == GPIO_INT_MODE_EDGE) { |
| if (trig == GPIO_INT_TRIG_BOTH) { |
| config |= IOC_BOTH_EDGES; |
| } else if (trig == GPIO_INT_TRIG_HIGH) { |
| config |= IOC_RISING_EDGE; |
| } else { /* GPIO_INT_TRIG_LOW */ |
| config |= IOC_FALLING_EDGE; |
| } |
| } else { |
| return -ENOTSUP; |
| } |
| |
| config |= IOC_INT_ENABLE; |
| } else { |
| config |= IOC_INT_DISABLE | IOC_NO_EDGE; |
| } |
| |
| config |= IOCPortConfigureGet(pin) & IOCFG_GEN_MASK; |
| IOCPortConfigureSet(pin, IOC_PORT_GPIO, config); |
| |
| return 0; |
| } |
| |
| static int gpio_cc13xx_cc26xx_manage_callback(const struct device *port, |
| struct gpio_callback *callback, |
| bool set) |
| { |
| struct gpio_cc13xx_cc26xx_data *data = port->data; |
| |
| return gpio_manage_callback(&data->callbacks, callback, set); |
| } |
| |
| static uint32_t gpio_cc13xx_cc26xx_get_pending_int(const struct device *dev) |
| { |
| return GPIO_getEventMultiDio(GPIO_DIO_ALL_MASK); |
| } |
| |
| static void gpio_cc13xx_cc26xx_isr(const struct device *dev) |
| { |
| struct gpio_cc13xx_cc26xx_data *data = dev->data; |
| |
| uint32_t status = GPIO_getEventMultiDio(GPIO_DIO_ALL_MASK); |
| |
| GPIO_clearEventMultiDio(status); |
| |
| gpio_fire_callbacks(&data->callbacks, dev, status); |
| } |
| |
| static int gpio_cc13xx_cc26xx_init(const struct device *dev) |
| { |
| #ifdef CONFIG_PM |
| /* Set dependency on gpio resource to turn on power domains */ |
| Power_setDependency(PowerCC26XX_PERIPH_GPIO); |
| #else |
| /* Enable peripheral power domain */ |
| PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH); |
| |
| /* Enable GPIO peripheral */ |
| PRCMPeripheralRunEnable(PRCM_PERIPH_GPIO); |
| |
| /* Load PRCM settings */ |
| PRCMLoadSet(); |
| while (!PRCMLoadGet()) { |
| continue; |
| } |
| #endif |
| |
| /* Enable edge detection on any pad as a wakeup source */ |
| HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) = |
| (HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) & |
| (~AON_EVENT_MCUWUSEL_WU1_EV_M)) | |
| AON_EVENT_MCUWUSEL_WU1_EV_PAD; |
| |
| /* Enable IRQ */ |
| IRQ_CONNECT(DT_INST_IRQN(0), |
| DT_INST_IRQ(0, priority), |
| gpio_cc13xx_cc26xx_isr, DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQN(0)); |
| |
| /* Peripheral should not be accessed until power domain is on. */ |
| while (PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) != |
| PRCM_DOMAIN_POWER_ON) { |
| continue; |
| } |
| |
| return 0; |
| } |
| |
| static const struct gpio_driver_api gpio_cc13xx_cc26xx_driver_api = { |
| .pin_configure = gpio_cc13xx_cc26xx_config, |
| .port_get_raw = gpio_cc13xx_cc26xx_port_get_raw, |
| .port_set_masked_raw = gpio_cc13xx_cc26xx_port_set_masked_raw, |
| .port_set_bits_raw = gpio_cc13xx_cc26xx_port_set_bits_raw, |
| .port_clear_bits_raw = gpio_cc13xx_cc26xx_port_clear_bits_raw, |
| .port_toggle_bits = gpio_cc13xx_cc26xx_port_toggle_bits, |
| .pin_interrupt_configure = gpio_cc13xx_cc26xx_pin_interrupt_configure, |
| .manage_callback = gpio_cc13xx_cc26xx_manage_callback, |
| .get_pending_int = gpio_cc13xx_cc26xx_get_pending_int |
| }; |
| |
| DEVICE_DT_INST_DEFINE(0, gpio_cc13xx_cc26xx_init, |
| NULL, &gpio_cc13xx_cc26xx_data_0, |
| &gpio_cc13xx_cc26xx_cfg_0, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &gpio_cc13xx_cc26xx_driver_api); |