diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index be77e3c79e3..e84ae06e755 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -61,4 +61,6 @@ source "drivers/gpio/Kconfig.pulpino" source "drivers/gpio/Kconfig.fe310" +source "drivers/gpio/Kconfig.cc2650" + endif # GPIO diff --git a/drivers/gpio/Kconfig.cc2650 b/drivers/gpio/Kconfig.cc2650 new file mode 100644 index 00000000000..d091c6165d7 --- /dev/null +++ b/drivers/gpio/Kconfig.cc2650 @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# GPIO configuration options for the CC2650 SoC (Texas Instruments). + +menuconfig GPIO_CC2650 + bool "TI CC2650 GPIO driver" + depends on GPIO && SOC_SERIES_CC2650 + default n + help + Enable the GPIO driver on boards equipped with TI CC2650. + +if GPIO_CC2650 + +# A single block of GPIO exist. + +config GPIO_CC2650_NAME + string + default "GPIO_0" + prompt "GPIO driver name." + +config GPIO_CC2650_INIT_PRIO + int + default 40 + prompt "GPIO driver initialization priority." + +endif # GPIO_CC2650 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 142eae1eada..ada9000fcd7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_GPIO_CMSDK_AHB) += gpio_cmsdk_ahb.o obj-$(CONFIG_GPIO_CC32XX) += gpio_cc32xx.o obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o +obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o diff --git a/drivers/gpio/gpio_cc2650.c b/drivers/gpio/gpio_cc2650.c new file mode 100644 index 00000000000..de5cd896807 --- /dev/null +++ b/drivers/gpio/gpio_cc2650.c @@ -0,0 +1,348 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * GPIO driver for the CC2650 SOC from Texas Instruments. + */ + +#include +#include +#include +#include +#include +#include + +#include "gpio_utils.h" + + +struct gpio_cc2650_data { + u32_t pin_callback_enables; + sys_slist_t callbacks; +}; + +/* Pre-declarations */ +static int gpio_cc2650_init(struct device *dev); +static int gpio_cc2650_config(struct device *port, int access_op, + u32_t pin, int flags); +static int gpio_cc2650_write(struct device *port, int access_op, + u32_t pin, u32_t value); + +static int gpio_cc2650_read(struct device *port, int access_op, + u32_t pin, u32_t *value); +static int gpio_cc2650_manage_callback(struct device *port, + struct gpio_callback *callback, + bool set); +static int gpio_cc2650_enable_callback(struct device *port, + int access_op, + u32_t pin); +static int gpio_cc2650_disable_callback(struct device *port, + int access_op, + u32_t pin); +static u32_t gpio_cc2650_get_pending_int(struct device *dev); + +/* GPIO registers */ +static const u32_t doutset31_0 = + REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS, + CC2650_GPIO_DOUTSET31_0); +static const u32_t doutclr31_0 = + REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS, + CC2650_GPIO_DOUTCLR31_0); +static const u32_t din31_0 = + REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS, + CC2650_GPIO_DIN31_0); +static const u32_t doe31_0 = + REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS, + CC2650_GPIO_DOE31_0); +static const u32_t evflags31_0 = + REG_ADDR(TI_CC2650_GPIO_40022000_BASE_ADDRESS, + CC2650_GPIO_EVFLAGS31_0); + +static struct gpio_cc2650_data gpio_cc2650_data = { + .pin_callback_enables = 0 +}; + +static const struct gpio_driver_api gpio_cc2650_funcs = { + .config = gpio_cc2650_config, + .write = gpio_cc2650_write, + .read = gpio_cc2650_read, + .manage_callback = gpio_cc2650_manage_callback, + .enable_callback = gpio_cc2650_enable_callback, + .disable_callback = gpio_cc2650_disable_callback, + .get_pending_int = gpio_cc2650_get_pending_int +}; + +DEVICE_AND_API_INIT(gpio_cc2650_0, CONFIG_GPIO_CC2650_NAME, + gpio_cc2650_init, &gpio_cc2650_data, NULL, + PRE_KERNEL_1, CONFIG_GPIO_CC2650_INIT_PRIO, + &gpio_cc2650_funcs); +static void disconnect(const int pin, u32_t *gpiodoe31_0, + u32_t *iocfg) +{ + *gpiodoe31_0 &= ~BIT(pin); + + *iocfg &= ~(CC2650_IOC_IOCFGX_PULL_CTL_MASK | + CC2650_IOC_IOCFGX_IE_MASK); + *iocfg |= CC2650_IOC_INPUT_DISABLED | + CC2650_IOC_NO_PULL; +} + +/* Configure a single pin. + * If any asked option is not implementable, rollback entirely to + * previous configuration. + * + * Note: For pin drive strength, the CC2650 devices only support + * symmetric sink/source capabilities. + * Thus, you may ONLY determine the common drive strength with + * GPIO *low output state* flags. Flags for *high output state* + * will be ignored. + */ +static int gpio_cc2650_config_pin(int pin, int flags) +{ + const u32_t iocfg = REG_ADDR(TI_CC2650_PINMUX_40081000_BASE_ADDRESS, + CC2650_IOC_IOCFG0 + 0x4 * pin); + u32_t iocfg_config = sys_read32(iocfg); + u32_t gpio_doe31_0_config = sys_read32(doe31_0); + + /* Reset all configurable fields to 0 */ + iocfg_config &= ~(CC2650_IOC_IOCFGX_IOSTR_MASK | + CC2650_IOC_IOCFGX_PULL_CTL_MASK | + CC2650_IOC_IOCFGX_EDGE_DET_MASK | + CC2650_IOC_IOCFGX_EDGE_IRQ_EN_MASK | + CC2650_IOC_IOCFGX_IOMODE_MASK | + CC2650_IOC_IOCFGX_IE_MASK | + CC2650_IOC_IOCFGX_HYST_EN_MASK); + + if (flags & GPIO_PIN_DISABLE) { + disconnect(pin, &gpio_doe31_0_config, &iocfg_config); + goto commit_config; + } + + if (flags & GPIO_DIR_OUT) { + gpio_doe31_0_config |= BIT(pin); + iocfg_config |= CC2650_IOC_INPUT_DISABLED; + } else { + gpio_doe31_0_config &= ~BIT(pin); + iocfg_config |= CC2650_IOC_INPUT_ENABLED; + } + + if (flags & GPIO_INT) { + if (!(flags & GPIO_INT_EDGE) && + !(flags & GPIO_INT_DOUBLE_EDGE)) { + /* Can't do level-based interrupt */ + /* Don't commit changes */ + return -ENOTSUP; + } + + iocfg_config |= BIT(CC2650_IOC_IOCFGX_EDGE_IRQ_EN_POS); + + if (flags & GPIO_INT_EDGE) { + if (flags & GPIO_INT_ACTIVE_HIGH) { + iocfg_config |= CC2650_IOC_POS_EDGE_DET; + } else { + iocfg_config |= CC2650_IOC_NEG_EDGE_DET; + } + } else if (flags & GPIO_INT_DOUBLE_EDGE) { + iocfg_config |= CC2650_IOC_NEG_AND_POS_EDGE_DET; + } + + if (flags & GPIO_INT_CLOCK_SYNC) { + /* Don't commit changes */ + return -ENOTSUP; + } + + if (flags & GPIO_INT_DEBOUNCE) { + iocfg_config |= CC2650_IOC_HYSTERESIS_ENABLED; + } else { + iocfg_config |= CC2650_IOC_HYSTERESIS_DISABLED; + } + } + + if (flags & GPIO_POL_INV) { + iocfg_config |= CC2650_IOC_INVERTED_IO; + } else { + iocfg_config |= CC2650_IOC_NORMAL_IO; + } + + if (flags & GPIO_PUD_NORMAL) { + iocfg_config |= CC2650_IOC_NO_PULL; + } else if (flags & GPIO_PUD_PULL_UP) { + iocfg_config |= CC2650_IOC_PULL_UP; + } else if (flags & GPIO_PUD_PULL_DOWN) { + iocfg_config |= CC2650_IOC_PULL_DOWN; + } + + /* Remember, we only look at GPIO_DS_*_LOW ! */ + if (flags & GPIO_DS_DISCONNECT_LOW) { + disconnect(pin, &gpio_doe31_0_config, &iocfg_config); + } + if (flags & GPIO_DS_DFLT_LOW) { + iocfg_config |= CC2650_IOC_MIN_DRIVE_STRENGTH; + } else { + iocfg_config |= CC2650_IOC_MAX_DRIVE_STRENGTH; + } + + /* Commit changes */ +commit_config: + sys_write32(iocfg_config, iocfg); + sys_write32(gpio_doe31_0_config, doe31_0); + return 0; +} + +static inline void gpio_cc2650_write_pin(int pin, u32_t value) +{ + value ? sys_write32(BIT(pin), doutset31_0) : + sys_write32(BIT(pin), doutclr31_0); +} + +static inline void gpio_cc2650_read_pin(int pin, u32_t *value) +{ + *value = sys_read32(din31_0) & BIT(pin); +} + +static void gpio_cc2650_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct gpio_cc2650_data *data = dev->driver_data; + + const u32_t events = sys_read32(evflags31_0); + const u32_t call_mask = events & data->pin_callback_enables; + + /* Clear GPIO trigger events */ + u32_t evflags = sys_read32(evflags31_0); + + sys_write32(evflags | call_mask, evflags31_0); + + _gpio_fire_callbacks(&data->callbacks, dev, call_mask); +} + + +static int gpio_cc2650_init(struct device *dev) +{ + ARG_UNUSED(dev); + + /* ISR setup */ + IRQ_CONNECT(TI_CC2650_GPIO_40022000_IRQ_0, + TI_CC2650_GPIO_40022000_IRQ_0_PRIORITY, + gpio_cc2650_isr, DEVICE_GET(gpio_cc2650_0), + 0); + irq_enable(TI_CC2650_GPIO_40022000_IRQ_0); + + return 0; +} + +static int gpio_cc2650_config(struct device *port, int access_op, + u32_t pin, int flags) +{ + ARG_UNUSED(port); + + if (access_op == GPIO_ACCESS_BY_PIN) { + return gpio_cc2650_config_pin(pin, flags); + } + + const u32_t nb_pins = 32; + + for (u8_t i = 0; i < nb_pins; ++i) { + if (pin & 0x1 && + gpio_cc2650_config_pin(i, flags) == -ENOTSUP) { + /* The flags being treated the same for + * every pin, if we get here then it's + * necessarily the first pin on which we act. + * + * We expect gpio_cc2650_config_pin() to + * NOT commit its changes if any problem + * arises, thus we do nothing special here + * to implement rollback to previous + * configuration. + */ + return -ENOTSUP; + } + pin >>= 1; + } + return 0; +} + +static int gpio_cc2650_write(struct device *port, int access_op, + u32_t pin, u32_t value) +{ + ARG_UNUSED(port); + + if (access_op == GPIO_ACCESS_BY_PIN) { + gpio_cc2650_write_pin(pin, value); + } else { + const u32_t nb_pins = 32; + + for (u32_t i = 0; i < nb_pins; ++i) { + if (pin & 0x1) { + gpio_cc2650_write_pin(i, value); + } + pin >>= 1; + } + } + + return 0; +} + +static int gpio_cc2650_read(struct device *port, int access_op, + u32_t pin, u32_t *value) +{ + ARG_UNUSED(port); + + if (access_op == GPIO_ACCESS_BY_PIN) { + gpio_cc2650_read_pin(pin, value); + *value >>= pin; + } else { + const u32_t nb_pins = 32; + + for (u32_t i = 0; i < nb_pins; ++i) { + if (pin & 0x1) { + gpio_cc2650_read_pin(i, value); + } + pin >>= 1; + } + } + return 0; +} + +static int gpio_cc2650_manage_callback(struct device *port, + struct gpio_callback *callback, + bool set) +{ + struct gpio_cc2650_data *data = port->driver_data; + + _gpio_manage_callback(&data->callbacks, callback, set); + return 0; +} + +static int gpio_cc2650_enable_callback(struct device *port, + int access_op, + u32_t pin) +{ + struct gpio_cc2650_data *data = port->driver_data; + + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables |= BIT(pin); + } else { + data->pin_callback_enables |= pin; + } + return 0; +} + +static int gpio_cc2650_disable_callback(struct device *port, + int access_op, + u32_t pin) +{ + struct gpio_cc2650_data *data = port->driver_data; + + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables &= ~BIT(pin); + } else { + data->pin_callback_enables &= ~pin; + } + return 0; +} + +static u32_t gpio_cc2650_get_pending_int(struct device *dev) +{ + ARG_UNUSED(dev); + + return sys_read32(evflags31_0); +} diff --git a/dts/arm/yaml/ti,cc2650-gpio.yaml b/dts/arm/yaml/ti,cc2650-gpio.yaml new file mode 100644 index 00000000000..f24f3bda966 --- /dev/null +++ b/dts/arm/yaml/ti,cc2650-gpio.yaml @@ -0,0 +1,27 @@ +--- +# SPDX-License-Identifier: Apache-2.0 +title: TI CC2650 GPIO +version: 0.1 + +description: > + This is a representation of the TI CC2650 GPIO node + +properties: + - compatible: + type: string + category: required + description: compatible strings + constraint: "ti,cc2650-gpio" + + - reg: + type: int + description: mmio register space + generation: define + category: required + + - interrupts: + type: compound + category: required + description: required interrupts + generation: define +...