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.
319 lines
7.9 KiB
319 lines
7.9 KiB
/* |
|
* Copyright (c) 2019 Brett Witherspoon |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT ti_cc13xx_cc26xx_gpio |
|
|
|
#include <zephyr/types.h> |
|
#include <zephyr/sys/__assert.h> |
|
#include <zephyr/device.h> |
|
#include <errno.h> |
|
#include <zephyr/drivers/gpio.h> |
|
#include <zephyr/dt-bindings/gpio/ti-cc13xx-cc26xx-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 <zephyr/irq.h> |
|
|
|
#include <zephyr/drivers/gpio/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 & CC13XX_CC26XX_GPIO_DEBOUNCE) ? |
|
IOC_HYST_ENABLE : IOC_HYST_DISABLE; |
|
|
|
switch (flags & CC13XX_CC26XX_GPIO_DS_MASK) { |
|
case CC13XX_CC26XX_GPIO_DS_DFLT: |
|
config |= IOC_CURRENT_2MA | IOC_STRENGTH_AUTO; |
|
break; |
|
case CC13XX_CC26XX_GPIO_DS_ALT: |
|
/* |
|
* Not all GPIO support 8ma, but setting that bit will use the |
|
* highest supported drive strength. |
|
*/ |
|
config |= IOC_CURRENT_8MA | IOC_STRENGTH_MAX; |
|
break; |
|
default: |
|
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 (PRCMPowerDomainsAllOn(PRCM_DOMAIN_PERIPH) != |
|
PRCM_DOMAIN_POWER_ON) { |
|
continue; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#ifdef CONFIG_GPIO_GET_DIRECTION |
|
static int gpio_cc13xx_cc26xx_port_get_direction(const struct device *port, gpio_port_pins_t map, |
|
gpio_port_pins_t *inputs, |
|
gpio_port_pins_t *outputs) |
|
{ |
|
uint32_t pin; |
|
gpio_port_pins_t ip = 0; |
|
gpio_port_pins_t op = 0; |
|
const struct gpio_driver_config *cfg = port->config; |
|
|
|
map &= cfg->port_pin_mask; |
|
|
|
if (inputs != NULL) { |
|
for (pin = find_lsb_set(map) - 1; map; |
|
map &= ~BIT(pin), pin = find_lsb_set(map) - 1) { |
|
ip |= !!(IOCPortConfigureGet(pin) & IOC_INPUT_ENABLE) * BIT(pin); |
|
} |
|
|
|
*inputs = ip; |
|
} |
|
|
|
if (outputs != NULL) { |
|
for (pin = find_lsb_set(map) - 1; map; |
|
map &= ~BIT(pin), pin = find_lsb_set(map) - 1) { |
|
op |= GPIO_getOutputEnableDio(pin) * BIT(pin); |
|
} |
|
|
|
*outputs = op; |
|
} |
|
|
|
return 0; |
|
} |
|
#endif /* CONFIG_GPIO_GET_DIRECTION */ |
|
|
|
static DEVICE_API(gpio, 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, |
|
#ifdef CONFIG_GPIO_GET_DIRECTION |
|
.port_get_direction = gpio_cc13xx_cc26xx_port_get_direction, |
|
#endif /* CONFIG_GPIO_GET_DIRECTION */ |
|
}; |
|
|
|
DEVICE_DT_INST_DEFINE(0, gpio_cc13xx_cc26xx_init, |
|
NULL, &gpio_cc13xx_cc26xx_data_0, |
|
&gpio_cc13xx_cc26xx_cfg_0, |
|
PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, |
|
&gpio_cc13xx_cc26xx_driver_api);
|
|
|