You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
6.8 KiB
281 lines
6.8 KiB
/* |
|
* 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);
|
|
|