| /* |
| * Copyright (c) 2024 Texas Instruments Incorporated |
| * Copyright (c) 2024 BayLibre, SAS |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ti_cc23x0_gpio |
| |
| #include <zephyr/types.h> |
| #include <zephyr/device.h> |
| #include <zephyr/irq.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/gpio/gpio_utils.h> |
| |
| #include <driverlib/clkctl.h> |
| #include <driverlib/gpio.h> |
| #include <inc/hw_ioc.h> |
| |
| #define IOC_ADDR(index) (IOC_BASE + IOC_O_IOC0 + (sizeof(uint32_t) * (index))) |
| |
| struct gpio_cc23x0_config { |
| /* gpio_driver_config needs to be first */ |
| struct gpio_driver_config common; |
| }; |
| |
| struct gpio_cc23x0_data { |
| /* gpio_driver_data needs to be first */ |
| struct gpio_driver_data common; |
| sys_slist_t callbacks; |
| }; |
| |
| static void set_pin_mask_non_atomic(uint8_t index, uint32_t registerBaseAddress) |
| { |
| GPIOSetConfigDio(GPIO_BASE + registerBaseAddress, BIT(index)); |
| } |
| |
| static int gpio_cc23x0_config(const struct device *port, gpio_pin_t pin, gpio_flags_t flags) |
| { |
| uint32_t config = 0; |
| uint32_t iocfg_reg = IOC_ADDR(pin); |
| gpio_flags_t direction = flags & GPIO_DIR_MASK; |
| |
| if (flags & GPIO_PULL_UP) { |
| config |= IOC_IOC0_PULLCTL_PULL_UP; |
| } else if (flags & GPIO_PULL_DOWN) { |
| config |= IOC_IOC0_PULLCTL_PULL_DOWN; |
| } else { |
| config |= IOC_IOC0_PULLCTL_PULL_DIS; |
| } |
| |
| if (!(flags & GPIO_SINGLE_ENDED)) { |
| config |= IOC_IOC0_IOMODE_NORMAL; |
| } else { |
| if (flags & GPIO_LINE_OPEN_DRAIN) { |
| config |= IOC_IOC0_IOMODE_OPEND; |
| } else { |
| config |= IOC_IOC0_IOMODE_OPENS; |
| } |
| } |
| if (direction & GPIO_INPUT) { |
| config |= IOC_IOC0_INPEN_EN | IOC_IOC0_HYSTEN_EN; |
| } |
| |
| GPIOSetConfigDio(iocfg_reg, config); |
| |
| if (flags & GPIO_OUTPUT) { |
| if (flags & GPIO_OUTPUT_INIT_HIGH) { |
| GPIOSetDio(pin); |
| } else if (flags & GPIO_OUTPUT_INIT_LOW) { |
| GPIOClearDio(pin); |
| } |
| GPIOSetOutputEnableDio(pin, GPIO_OUTPUT_ENABLE); |
| } else { |
| GPIOSetOutputEnableDio(pin, GPIO_OUTPUT_DISABLE); |
| } |
| return 0; |
| } |
| |
| #ifdef CONFIG_GPIO_GET_CONFIG |
| static int gpio_cc23x0_get_config(const struct device *port, gpio_pin_t pin, gpio_flags_t *flags) |
| { |
| uint32_t out_flag = 0; |
| uint32_t iocfg_reg = IOC_ADDR(pin); |
| uint32_t config = GPIOGetConfigDio(iocfg_reg); |
| |
| /* GPIO input/output configuration flags */ |
| if (config & IOC_IOC0_INPEN_EN) { |
| out_flag |= GPIO_INPUT; |
| } |
| |
| if (GPIOGetOutputEnableDio(pin)) { |
| out_flag |= GPIO_OUTPUT; |
| |
| if (GPIOReadDio(pin)) { |
| out_flag |= GPIO_OUTPUT_INIT_HIGH; |
| } else { |
| /* This is the default value. If not explicitly set, |
| * the returned config will not be symmetric |
| */ |
| out_flag |= GPIO_OUTPUT_INIT_LOW; |
| } |
| } |
| |
| /* GPIO interrupt configuration flags */ |
| if ((config & IOC_IOC0_EDGEDET_M) != IOC_IOC0_EDGEDET_EDGE_DIS) { |
| if (config & IOC_IOC0_EDGEDET_EDGE_POS) { |
| out_flag |= GPIO_INT_EDGE_RISING; |
| } |
| |
| if (config & IOC_IOC0_EDGEDET_EDGE_NEG) { |
| out_flag |= GPIO_INT_EDGE_FALLING; |
| } |
| } else { |
| /* This is the default value. If not explicitly set, |
| * the returned config will not be symmetric |
| */ |
| out_flag |= GPIO_INT_DISABLE; |
| } |
| |
| /* GPIO pin drive flags */ |
| if (config & IOC_IOC0_IOMODE_OPENS) { |
| out_flag |= GPIO_OPEN_SOURCE; |
| } |
| |
| if (config & IOC_IOC0_IOMODE_OPEND) { |
| out_flag |= IOC_IOC0_IOMODE_OPEND; |
| } |
| |
| if (config & IOC_IOC0_PULLCTL_PULL_UP) { |
| out_flag |= GPIO_PULL_UP; |
| } |
| |
| if (config & IOC_IOC0_PULLCTL_PULL_DOWN) { |
| out_flag |= GPIO_PULL_DOWN; |
| } |
| |
| *flags = out_flag; |
| |
| return 0; |
| } |
| #endif |
| |
| static int gpio_cc23x0_port_get_raw(const struct device *port, uint32_t *value) |
| { |
| *value = GPIOReadMultiDio(GPIO_DIO_ALL_MASK); |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0_port_set_masked_raw(const struct device *port, uint32_t mask, uint32_t value) |
| { |
| GPIOWriteMultiDio(mask, value); |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0_port_set_bits_raw(const struct device *port, uint32_t mask) |
| { |
| GPIOSetMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0_port_clear_bits_raw(const struct device *port, uint32_t mask) |
| { |
| GPIOClearMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0_port_toggle_bits(const struct device *port, uint32_t mask) |
| { |
| GPIOToggleMultiDio(mask); |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0xx_pin_interrupt_configure(const struct device *port, gpio_pin_t pin, |
| enum gpio_int_mode mode, enum gpio_int_trig trig) |
| { |
| if (mode == GPIO_INT_MODE_LEVEL) { |
| return -ENOTSUP; |
| } |
| |
| uint32_t config = GPIOGetConfigDio(IOC_ADDR(pin)) & ~IOC_IOC0_EDGEDET_M; |
| |
| if (mode == GPIO_INT_MODE_DISABLED) { |
| config |= IOC_IOC1_EDGEDET_EDGE_DIS; |
| |
| GPIOSetConfigDio(IOC_ADDR(pin), config); |
| |
| /* Disable interrupt mask */ |
| set_pin_mask_non_atomic(pin, GPIO_O_IMCLR); |
| |
| } else if (mode == GPIO_INT_MODE_EDGE) { |
| switch (trig) { |
| case GPIO_INT_TRIG_LOW: |
| config |= IOC_IOC1_EDGEDET_EDGE_NEG; |
| break; |
| case GPIO_INT_TRIG_HIGH: |
| config |= IOC_IOC1_EDGEDET_EDGE_POS; |
| break; |
| case GPIO_INT_TRIG_BOTH: |
| config |= IOC_IOC1_EDGEDET_EDGE_BOTH; |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| GPIOSetConfigDio(IOC_ADDR(pin), config); |
| |
| /* Enable interrupt mask */ |
| set_pin_mask_non_atomic(pin, GPIO_O_ICLR); |
| set_pin_mask_non_atomic(pin, GPIO_O_IMSET); |
| } |
| |
| return 0; |
| } |
| |
| static int gpio_cc23x0_manage_callback(const struct device *port, struct gpio_callback *callback, |
| bool set) |
| { |
| struct gpio_cc23x0_data *data = port->data; |
| |
| return gpio_manage_callback(&data->callbacks, callback, set); |
| } |
| |
| static uint32_t gpio_cc23x0_get_pending_int(const struct device *dev) |
| { |
| return GPIOGetEventMultiDio(GPIO_DIO_ALL_MASK); |
| } |
| |
| static void gpio_cc23x0_isr(const struct device *dev) |
| { |
| struct gpio_cc23x0_data *data = dev->data; |
| |
| uint32_t status = GPIOGetEventMultiDio(GPIO_DIO_ALL_MASK); |
| |
| GPIOClearEventMultiDio(status); |
| |
| gpio_fire_callbacks(&data->callbacks, dev, status); |
| } |
| |
| static int gpio_cc23x0_init(const struct device *dev) |
| { |
| /* Enable GPIO domain clock */ |
| CLKCTLEnable(CLKCTL_BASE, CLKCTL_GPIO); |
| |
| /* Enable IRQ */ |
| IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), gpio_cc23x0_isr, |
| DEVICE_DT_INST_GET(0), 0); |
| |
| irq_enable(DT_INST_IRQN(0)); |
| |
| return 0; |
| } |
| |
| static DEVICE_API(gpio, gpio_cc23x0_driver_api) = { |
| .pin_configure = gpio_cc23x0_config, |
| #ifdef CONFIG_GPIO_GET_CONFIG |
| .pin_get_config = gpio_cc23x0_get_config, |
| #endif |
| .port_get_raw = gpio_cc23x0_port_get_raw, |
| .port_set_masked_raw = gpio_cc23x0_port_set_masked_raw, |
| .port_set_bits_raw = gpio_cc23x0_port_set_bits_raw, |
| .port_clear_bits_raw = gpio_cc23x0_port_clear_bits_raw, |
| .port_toggle_bits = gpio_cc23x0_port_toggle_bits, |
| .pin_interrupt_configure = gpio_cc23x0xx_pin_interrupt_configure, |
| .manage_callback = gpio_cc23x0_manage_callback, |
| .get_pending_int = gpio_cc23x0_get_pending_int, |
| }; |
| |
| static const struct gpio_cc23x0_config gpio_cc23x0_config_0 = { |
| .common = {/* Read ngpios from DT */ |
| .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0)}}; |
| |
| static struct gpio_cc23x0_data gpio_cc23x0_data_0; |
| |
| DEVICE_DT_INST_DEFINE(0, gpio_cc23x0_init, NULL, &gpio_cc23x0_data_0, &gpio_cc23x0_config_0, |
| PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, &gpio_cc23x0_driver_api); |