From f18cced2c197fd391535594a0730c3fc7da795db Mon Sep 17 00:00:00 2001 From: Gil Pitney Date: Wed, 23 Nov 2016 15:47:20 -0800 Subject: [PATCH] cc3200: Add a GPIO driver for the TI CC3200 LaunchXL The pinmux configuration is done during board initialization. This was validated using the following Zephyr apps: - samples/basic/blinky - samples/basic/disco - samples/basic/button All 4 GPIO ports are supported. Change-Id: If8599a23c1d56cfd678a6e2e5339f7e093c6061a Signed-off-by: Gil Pitney --- .../cc32xx/Kconfig.defconfig.cc3200 | 19 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.cc32xx | 93 +++++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio_cc32xx.c | 335 ++++++++++++++++++ 5 files changed, 450 insertions(+) create mode 100644 drivers/gpio/Kconfig.cc32xx create mode 100644 drivers/gpio/gpio_cc32xx.c diff --git a/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 b/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 index 0c332140116..f9b16082a17 100644 --- a/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 +++ b/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 @@ -34,4 +34,23 @@ config UART_CC32XX_IRQ_PRI default 3 endif # UART_CC32XX +if GPIO + +config GPIO_CC32XX + def_bool y + +config GPIO_CC32XX_A0 + default n + +config GPIO_CC32XX_A1 + default y + +config GPIO_CC32XX_A2 + default y + +config GPIO_CC32XX_A3 + default n + +endif # GPIO + endif # SOC_CC3200 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index dedff24489a..149b9c74420 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -63,4 +63,6 @@ source "drivers/gpio/Kconfig.nrf5" source "drivers/gpio/Kconfig.cmsdk_ahb" +source "drivers/gpio/Kconfig.cc32xx" + endif # GPIO diff --git a/drivers/gpio/Kconfig.cc32xx b/drivers/gpio/Kconfig.cc32xx new file mode 100644 index 00000000000..0e6441d0bb7 --- /dev/null +++ b/drivers/gpio/Kconfig.cc32xx @@ -0,0 +1,93 @@ +# Kconfig.cc32xx- CC32XX GPIO configuration options +# + +menuconfig GPIO_CC32XX + bool "TI CC32XX GPIO driver" + depends on GPIO && SOC_FAMILY_TISIMPLELINK + default n + help + Enable the GPIO driver on TI SimpleLink CC32xx boards + +if GPIO_CC32XX + +config GPIO_CC32XX_A0 + bool "GPIO block A0" + depends on GPIO_CC32XX + default n + help + Include support for the GPIO port A0. + +config GPIO_CC32XX_A0_NAME + string "Driver name" + depends on GPIO_CC32XX_A0 + default "GPIO_A0" + +config GPIO_CC32XX_A0_IRQ_PRI + int "GPIO A0 interrupt priority" + depends on GPIO_CC32XX_A0 + range 0 5 + default 1 + help + CC32XX GPIO A0 IRQ priority. + +config GPIO_CC32XX_A1 + bool "GPIO block A1" + depends on GPIO_CC32XX + default n + help + Include support for the GPIO port A1. + +config GPIO_CC32XX_A1_NAME + string "Driver name" + depends on GPIO_CC32XX_A1 + default "GPIO_A1" + +config GPIO_CC32XX_A1_IRQ_PRI + int "GPIO A1 interrupt priority" + depends on GPIO_CC32XX_A1 + range 0 5 + default 1 + help + CC32XX GPIO A1 IRQ priority. + +config GPIO_CC32XX_A2 + bool "GPIO block A2" + depends on GPIO_CC32XX + default n + help + Include support for the GPIO port A2. + +config GPIO_CC32XX_A2_NAME + string "Driver name" + depends on GPIO_CC32XX_A2 + default "GPIO_A2" + +config GPIO_CC32XX_A2_IRQ_PRI + int "GPIO A2 interrupt priority" + depends on GPIO_CC32XX_A2 + range 0 5 + default 1 + help + CC32XX GPIO A2 IRQ priority. + +config GPIO_CC32XX_A3 + bool "GPIO block A3" + depends on GPIO_CC32XX + default n + help + Include support for the GPIO port A3. + +config GPIO_CC32XX_A3_NAME + string "Driver name" + depends on GPIO_CC32XX_A3 + default "GPIO_A3" + +config GPIO_CC32XX_A3_IRQ_PRI + int "GPIO A3 interrupt priority" + depends on GPIO_CC32XX_A3 + range 0 5 + default 1 + help + CC32XX GPIO A3 IRQ priority. + +endif # GPIO_CC32XX diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9ccfe9cf228..3c8c51ec0ac 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_GPIO_K64) += gpio_k64.o obj-$(CONFIG_GPIO_STM32) += gpio_stm32.o obj-$(CONFIG_GPIO_NRF5) += gpio_nrf5.o obj-$(CONFIG_GPIO_CMSDK_AHB) += gpio_cmsdk_ahb.o +obj-$(CONFIG_GPIO_CC32XX) += gpio_cc32xx.o diff --git a/drivers/gpio/gpio_cc32xx.c b/drivers/gpio/gpio_cc32xx.c new file mode 100644 index 00000000000..c8e0c67e9ad --- /dev/null +++ b/drivers/gpio/gpio_cc32xx.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2016, Texas Instruments Incorporated + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include +#include + +/* Driverlib includes */ +#include +#include +#include +#include +#include +#include +#undef __GPIO_H__ /* Zephyr and CC3200SDK gpio.h conflict */ +#include +#include +#include + +#include "gpio_utils.h" + +/* Note: Zephyr uses exception numbers, vs the IRQ #s used by the CC3200 SDK */ +#define EXCEPTION_GPIOA0 0 /* (INT_GPIOA0 - 16) = (16-16) */ +#define EXCEPTION_GPIOA1 1 /* (INT_GPIOA1 - 16) = (17-16) */ +#define EXCEPTION_GPIOA2 2 /* (INT_GPIOA2 - 16) = (18-16) */ +#define EXCEPTION_GPIOA3 3 /* (INT_GPIOA3 - 16) = (19-16) */ + +struct gpio_cc32xx_config { + /* base address of GPIO port */ + unsigned long port_base; + /* GPIO IRQ number */ + unsigned long irq_num; +}; + +struct gpio_cc32xx_data { + /* list of registered callbacks */ + sys_slist_t callbacks; + /* callback enable pin bitmask */ + uint32_t pin_callback_enables; +}; + +#define DEV_CFG(dev) \ + ((const struct gpio_cc32xx_config *)(dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct gpio_cc32xx_data *)(dev)->driver_data) + +static inline int gpio_cc32xx_config(struct device *port, + int access_op, uint32_t pin, int flags) +{ + const struct gpio_cc32xx_config *gpio_config = DEV_CFG(port); + unsigned long port_base = gpio_config->port_base; + unsigned long int_type; + + /* + * See pinmux_initialize(): which leverages TI's recommended + * method of using the PinMux utility for most pin configuration. + */ + + if (access_op == GPIO_ACCESS_BY_PIN) { + /* Just handle runtime interrupt type config here: */ + if (flags & GPIO_INT) { + if (flags & GPIO_INT_EDGE) { + if (flags & GPIO_INT_ACTIVE_HIGH) { + int_type = GPIO_RISING_EDGE; + } else if (flags & GPIO_INT_DOUBLE_EDGE) { + int_type = GPIO_BOTH_EDGES; + } else { + int_type = GPIO_FALLING_EDGE; + } + } else { /* GPIO_INT_LEVEL */ + if (flags & GPIO_INT_ACTIVE_HIGH) { + int_type = GPIO_HIGH_LEVEL; + } else { + int_type = GPIO_LOW_LEVEL; + } + } + MAP_GPIOIntTypeSet(port_base, (1 << pin), int_type); + MAP_GPIOIntClear(port_base, (1 << pin)); + MAP_GPIOIntEnable(port_base, (1 << pin)); + } + } else { + return -ENOTSUP; + } + + return 0; +} + +static inline int gpio_cc32xx_write(struct device *port, + int access_op, uint32_t pin, uint32_t value) +{ + const struct gpio_cc32xx_config *gpio_config = DEV_CFG(port); + unsigned long port_base = gpio_config->port_base; + + if (access_op == GPIO_ACCESS_BY_PIN) { + value = value << pin; + /* Bitpack external GPIO pin number for GPIOPinWrite API: */ + pin = 1 << pin; + + MAP_GPIOPinWrite(port_base, (unsigned char)pin, value); + } else { + return -ENOTSUP; + } + + return 0; +} + +static inline int gpio_cc32xx_read(struct device *port, + int access_op, uint32_t pin, uint32_t *value) +{ + const struct gpio_cc32xx_config *gpio_config = DEV_CFG(port); + unsigned long port_base = gpio_config->port_base; + long status; + unsigned char pin_packed; + + if (access_op == GPIO_ACCESS_BY_PIN) { + /* Bitpack external GPIO pin number for GPIOPinRead API: */ + pin_packed = 1 << pin; + status = MAP_GPIOPinRead(port_base, pin_packed); + *value = status >> pin; + } else { + return -ENOTSUP; + } + + return 0; +} + +static int gpio_cc32xx_manage_callback(struct device *dev, + struct gpio_callback *callback, bool set) +{ + struct gpio_cc32xx_data *data = DEV_DATA(dev); + + _gpio_manage_callback(&data->callbacks, callback, set); + + return 0; +} + + +static int gpio_cc32xx_enable_callback(struct device *dev, + int access_op, uint32_t pin) +{ + struct gpio_cc32xx_data *data = DEV_DATA(dev); + + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables |= (1 << pin); + } else { + data->pin_callback_enables = 0xFFFFFFFF; + } + + return 0; +} + + +static int gpio_cc32xx_disable_callback(struct device *dev, + int access_op, uint32_t pin) +{ + struct gpio_cc32xx_data *data = DEV_DATA(dev); + + if (access_op == GPIO_ACCESS_BY_PIN) { + data->pin_callback_enables &= ~(1 << pin); + } else { + data->pin_callback_enables = 0; + } + + return 0; +} + +static void gpio_cc32xx_port_isr(void *arg) +{ + struct device *dev = arg; + const struct gpio_cc32xx_config *config = DEV_CFG(dev); + struct gpio_cc32xx_data *data = DEV_DATA(dev); + uint32_t enabled_int, int_status; + + /* See which interrupts triggered: */ + int_status = (uint32_t)MAP_GPIOIntStatus(config->port_base, 1); + + enabled_int = int_status & data->pin_callback_enables; + + /* Clear and Disable GPIO Interrupt */ + MAP_GPIOIntDisable(config->port_base, int_status); + MAP_GPIOIntClear(config->port_base, int_status); + + /* Call the registered callbacks */ + _gpio_fire_callbacks(&data->callbacks, (struct device *)dev, + enabled_int); + + /* Re-enable the interrupts */ + MAP_GPIOIntEnable(config->port_base, int_status); +} + +static const struct gpio_driver_api api_funcs = { + .config = gpio_cc32xx_config, + .write = gpio_cc32xx_write, + .read = gpio_cc32xx_read, + .manage_callback = gpio_cc32xx_manage_callback, + .enable_callback = gpio_cc32xx_enable_callback, + .disable_callback = gpio_cc32xx_disable_callback, + +}; + +#ifdef CONFIG_GPIO_CC32XX_A0 +static const struct gpio_cc32xx_config gpio_cc32xx_a0_config = { + .port_base = GPIOA0_BASE, + .irq_num = INT_GPIOA0, +}; + +static struct device DEVICE_NAME_GET(gpio_cc32xx_a0); +static struct gpio_cc32xx_data gpio_cc32xx_a0_data; + +static int gpio_cc32xx_a0_init(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(EXCEPTION_GPIOA0, CONFIG_GPIO_CC32XX_A0_IRQ_PRI, + gpio_cc32xx_port_isr, DEVICE_GET(gpio_cc32xx_a0), 0); + + MAP_IntPendClear(INT_GPIOA0); + irq_enable(EXCEPTION_GPIOA0); + + return 0; +} + +DEVICE_AND_API_INIT(gpio_cc32xx_a0, CONFIG_GPIO_CC32XX_A0_NAME, + &gpio_cc32xx_a0_init, &gpio_cc32xx_a0_data, + &gpio_cc32xx_a0_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &api_funcs); + +#endif + +#ifdef CONFIG_GPIO_CC32XX_A1 +static const struct gpio_cc32xx_config gpio_cc32xx_a1_config = { + .port_base = GPIOA1_BASE, + .irq_num = INT_GPIOA1, +}; + +static struct device DEVICE_NAME_GET(gpio_cc32xx_a1); +static struct gpio_cc32xx_data gpio_cc32xx_a1_data; + +static int gpio_cc32xx_a1_init(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(EXCEPTION_GPIOA1, CONFIG_GPIO_CC32XX_A1_IRQ_PRI, + gpio_cc32xx_port_isr, DEVICE_GET(gpio_cc32xx_a1), 0); + + MAP_IntPendClear(INT_GPIOA1); + irq_enable(EXCEPTION_GPIOA1); + + return 0; +} + +DEVICE_AND_API_INIT(gpio_cc32xx_a1, CONFIG_GPIO_CC32XX_A1_NAME, + &gpio_cc32xx_a1_init, &gpio_cc32xx_a1_data, + &gpio_cc32xx_a1_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &api_funcs); + +#endif /* CONFIG_GPIO_CC32XX_A1 */ + +#ifdef CONFIG_GPIO_CC32XX_A2 +static const struct gpio_cc32xx_config gpio_cc32xx_a2_config = { + .port_base = GPIOA2_BASE, + .irq_num = INT_GPIOA2, +}; + +static struct device DEVICE_NAME_GET(gpio_cc32xx_a2); +static struct gpio_cc32xx_data gpio_cc32xx_a2_data; + +static int gpio_cc32xx_a2_init(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(EXCEPTION_GPIOA2, CONFIG_GPIO_CC32XX_A2_IRQ_PRI, + gpio_cc32xx_port_isr, DEVICE_GET(gpio_cc32xx_a2), 0); + + MAP_IntPendClear(INT_GPIOA2); + irq_enable(EXCEPTION_GPIOA2); + + return 0; +} + +DEVICE_AND_API_INIT(gpio_cc32xx_a2, CONFIG_GPIO_CC32XX_A2_NAME, + &gpio_cc32xx_a2_init, &gpio_cc32xx_a2_data, + &gpio_cc32xx_a2_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &api_funcs); + +#endif + +#ifdef CONFIG_GPIO_CC32XX_A3 +static const struct gpio_cc32xx_config gpio_cc32xx_a3_config = { + .port_base = GPIOA3_BASE, + .irq_num = INT_GPIOA3, +}; + +static struct device DEVICE_NAME_GET(gpio_cc32xx_a3); +static struct gpio_cc32xx_data gpio_cc32xx_a3_data; + +static int gpio_cc32xx_a3_init(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(EXCEPTION_GPIOA3, CONFIG_GPIO_CC32XX_A3_IRQ_PRI, + gpio_cc32xx_port_isr, DEVICE_GET(gpio_cc32xx_a3), 0); + + MAP_IntPendClear(INT_GPIOA3); + irq_enable(EXCEPTION_GPIOA3); + + return 0; +} + +DEVICE_AND_API_INIT(gpio_cc32xx_a3, CONFIG_GPIO_CC32XX_A3_NAME, + &gpio_cc32xx_a3_init, &gpio_cc32xx_a3_data, + &gpio_cc32xx_a3_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &api_funcs); + +#endif