diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index cc3048442f..b2df448ef7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -65,4 +65,6 @@ source "drivers/gpio/Kconfig.cc2650" source "drivers/gpio/Kconfig.esp32" +source "drivers/gpio/Kconfig.gecko" + endif # GPIO diff --git a/drivers/gpio/Kconfig.gecko b/drivers/gpio/Kconfig.gecko new file mode 100644 index 0000000000..88367a49ed --- /dev/null +++ b/drivers/gpio/Kconfig.gecko @@ -0,0 +1,95 @@ +# Kconfig.gecko - Gecko GPIO configuration options +# +# Copyright (c) 2017 Christian Taedcke +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig GPIO_GECKO + bool "Gecko GPIO driver" + depends on GPIO && HAS_SILABS_GECKO + default n + help + Enable the Gecko gpio driver. + +if GPIO_GECKO + +config GPIO_GECKO_COMMON_NAME + string "Common driver name" + default "GPIO_COMMON" + +config GPIO_GECKO_COMMON_INIT_PRIORITY + int "Common initialization priority" + default 39 + +config GPIO_GECKO_COMMON_PRI + int "Interrupt priority" + default 2 + +config GPIO_GECKO_PORTA + bool "Port A" + default n + help + Enable Port A. + +config GPIO_GECKO_PORTA_NAME + string "Port A driver name" + depends on GPIO_GECKO_PORTA + default "GPIO_0" + +config GPIO_GECKO_PORTB + bool "Port B" + default n + help + Enable Port B. + +config GPIO_GECKO_PORTB_NAME + string "Port B driver name" + depends on GPIO_GECKO_PORTB + default "GPIO_1" + +config GPIO_GECKO_PORTC + bool "Port C" + default n + help + Enable Port C. + +config GPIO_GECKO_PORTC_NAME + string "Port C driver name" + depends on GPIO_GECKO_PORTC + default "GPIO_2" + +config GPIO_GECKO_PORTD + bool "Port D" + default n + help + Enable Port D. + +config GPIO_GECKO_PORTD_NAME + string "Port D driver name" + depends on GPIO_GECKO_PORTD + default "GPIO_3" + +config GPIO_GECKO_PORTE + bool "Port E" + default n + help + Enable Port E. + +config GPIO_GECKO_PORTE_NAME + string "Port E driver name" + depends on GPIO_GECKO_PORTE + default "GPIO_4" + +config GPIO_GECKO_PORTF + bool "Port F" + default n + help + Enable Port F. + +config GPIO_GECKO_PORTF_NAME + string "Port F driver name" + depends on GPIO_GECKO_PORTF + default "GPIO_5" + +endif # GPIO_GECKO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3ebcf313b5..e259c90fc2 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_GPIO_PULPINO) += gpio_pulpino.o obj-$(CONFIG_GPIO_FE310) += gpio_fe310.o obj-$(CONFIG_GPIO_CC2650) += gpio_cc2650.o obj-$(CONFIG_GPIO_ESP32) += gpio_esp32.o +obj-$(CONFIG_GPIO_GECKO) += gpio_gecko.o diff --git a/drivers/gpio/gpio_gecko.c b/drivers/gpio/gpio_gecko.c new file mode 100644 index 0000000000..0574c4dcc6 --- /dev/null +++ b/drivers/gpio/gpio_gecko.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2017, Christian Taedcke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "gpio_utils.h" + +/* + * Macros to set the GPIO MODE registers + * + * See https://www.silabs.com/documents/public/reference-manuals/EFM32WG-RM.pdf + * pages 972 and 982. + */ +/** + * @brief Create the value to set the GPIO MODEL register + * @param[in] pin The index of the pin. Valid values are 0..7. + * @param[in] mode The mode that should be set. + * @return The value that can be set into the GPIO MODEL register. + */ +#define GECKO_GPIO_MODEL(pin, mode) (mode << (pin * 4)) + +/** + * @brief Create the value to set the GPIO MODEH register + * @param[in] pin The index of the pin. Valid values are 8..15. + * @param[in] mode The mode that should be set. + * @return The value that can be set into the GPIO MODEH register. + */ +#define GECKO_GPIO_MODEH(pin, mode) (mode << ((pin - 8) * 4)) + + +#define member_size(type, member) sizeof(((type *)0)->member) +#define NUMBER_OF_PORTS (member_size(GPIO_TypeDef, P) / \ + member_size(GPIO_TypeDef, P[0])) + +struct gpio_gecko_common_config { +}; + +struct gpio_gecko_common_data { + /* a list of all ports */ + struct device *ports[NUMBER_OF_PORTS]; + size_t count; +}; + +struct gpio_gecko_config { + GPIO_P_TypeDef *gpio_base; + GPIO_Port_TypeDef gpio_index; +}; + +struct gpio_gecko_data { + /* port ISR callback routine address */ + sys_slist_t callbacks; + /* pin callback routine enable flags, by pin number */ + u32_t pin_callback_enables; +}; + +static inline void gpio_gecko_add_port(struct gpio_gecko_common_data *data, + struct device *dev) +{ + __ASSERT(dev, "No port device!"); + data->ports[data->count++] = dev; +} + + +static int gpio_gecko_configure(struct device *dev, + int access_op, u32_t pin, int flags) +{ + const struct gpio_gecko_config *config = dev->config->config_info; + GPIO_P_TypeDef *gpio_base = config->gpio_base; + GPIO_Port_TypeDef gpio_index = config->gpio_index; + GPIO_Mode_TypeDef mode; + unsigned int out = 0; + + /* Check for an invalid pin configuration */ + if ((flags & GPIO_INT) && (flags & GPIO_DIR_OUT)) { + return -EINVAL; + } + + /* Interrupt on static level is not supported by the hardware */ + if ((flags & GPIO_INT) && !(flags & GPIO_INT_EDGE)) { + return -ENOTSUP; + } + + /* Setting interrupt flags for a complete port is not implemented */ + if ((flags & GPIO_INT) && (access_op == GPIO_ACCESS_BY_PORT)) { + return -ENOTSUP; + } + + if ((flags & GPIO_DIR_MASK) == GPIO_DIR_IN) { + + if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) { + mode = gpioModeInputPull; + out = 1; /* pull-up*/ + } else if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_DOWN) { + mode = gpioModeInputPull; + /* out = 0 means pull-down*/ + } + + mode = gpioModeInput; + } else { /* GPIO_DIR_OUT */ + mode = gpioModePushPull; + } + /* The flags contain options that require touching registers in the + * GPIO module and the corresponding PORT module. + * + * Start with the GPIO module and set up the pin direction register. + * 0 - pin is input, 1 - pin is output + */ + + if (access_op == GPIO_ACCESS_BY_PIN) { + GPIO_PinModeSet(gpio_index, pin, mode, out); + } else { /* GPIO_ACCESS_BY_PORT */ + gpio_base->MODEL = GECKO_GPIO_MODEL(7, mode) + | GECKO_GPIO_MODEL(6, mode) | GECKO_GPIO_MODEL(5, mode) + | GECKO_GPIO_MODEL(4, mode) | GECKO_GPIO_MODEL(3, mode) + | GECKO_GPIO_MODEL(2, mode) | GECKO_GPIO_MODEL(1, mode) + | GECKO_GPIO_MODEL(0, mode); + gpio_base->MODEH = GECKO_GPIO_MODEH(15, mode) + | GECKO_GPIO_MODEH(14, mode) + | GECKO_GPIO_MODEH(13, mode) + | GECKO_GPIO_MODEH(12, mode) + | GECKO_GPIO_MODEH(11, mode) + | GECKO_GPIO_MODEH(10, mode) + | GECKO_GPIO_MODEH(9, mode) + | GECKO_GPIO_MODEH(8, mode); + gpio_base->DOUT = (out ? 0xFFFF : 0x0000); + } + + if (access_op == GPIO_ACCESS_BY_PIN) { + GPIO_IntConfig(gpio_index, pin, + (flags & GPIO_INT_ACTIVE_HIGH) + || (flags & GPIO_INT_DOUBLE_EDGE), + !(flags & GPIO_INT_ACTIVE_HIGH) + || (flags & GPIO_INT_DOUBLE_EDGE), + !!(flags & GPIO_INT)); + } + + return 0; +} + +static int gpio_gecko_write(struct device *dev, + int access_op, u32_t pin, u32_t value) +{ + const struct gpio_gecko_config *config = dev->config->config_info; + GPIO_P_TypeDef *gpio_base = config->gpio_base; + + if (access_op == GPIO_ACCESS_BY_PIN) { + if (value) { + /* Set the data output for the corresponding pin. + * Writing zeros to the other bits leaves the data + * output unchanged for the other pins. + */ + gpio_base->DOUTSET = BIT(pin); + } else { + /* Clear the data output for the corresponding pin. + * Writing zeros to the other bits leaves the data + * output unchanged for the other pins. + */ + gpio_base->DOUTCLR = BIT(pin); + } + } else { /* GPIO_ACCESS_BY_PORT */ + /* Write the data output for all the pins */ + gpio_base->DOUT = value; + } + + return 0; +} + +static int gpio_gecko_read(struct device *dev, + int access_op, u32_t pin, u32_t *value) +{ + const struct gpio_gecko_config *config = dev->config->config_info; + GPIO_P_TypeDef *gpio_base = config->gpio_base; + + *value = gpio_base->DIN; + + if (access_op == GPIO_ACCESS_BY_PIN) { + *value = (*value & BIT(pin)) >> pin; + } + + /* nothing more to do for GPIO_ACCESS_BY_PORT */ + + return 0; +} + +static int gpio_gecko_manage_callback(struct device *dev, + struct gpio_callback *callback, bool set) +{ + struct gpio_gecko_data *data = dev->driver_data; + + _gpio_manage_callback(&data->callbacks, callback, set); + + return 0; +} + +static int gpio_gecko_enable_callback(struct device *dev, + int access_op, u32_t pin) +{ + struct gpio_gecko_data *data = dev->driver_data; + + if (access_op == GPIO_ACCESS_BY_PORT) { + return -ENOTSUP; + } + + data->pin_callback_enables |= BIT(pin); + GPIO->IEN |= BIT(pin); + + return 0; +} + +static int gpio_gecko_disable_callback(struct device *dev, + int access_op, u32_t pin) +{ + struct gpio_gecko_data *data = dev->driver_data; + + if (access_op == GPIO_ACCESS_BY_PORT) { + return -ENOTSUP; + } + + data->pin_callback_enables &= ~BIT(pin); + GPIO->IEN &= ~BIT(pin); + + return 0; +} + +/** + * Handler for both odd and even pin interrupts + */ +static void gpio_gecko_common_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct gpio_gecko_common_data *data = dev->driver_data; + u32_t enabled_int, int_status; + struct device *port_dev; + struct gpio_gecko_data *port_data; + + int_status = GPIO->IF; + + for (unsigned int i = 0; int_status && (i < data->count); i++) { + port_dev = data->ports[i]; + port_data = port_dev->driver_data; + enabled_int = int_status & port_data->pin_callback_enables; + int_status &= ~enabled_int; + + _gpio_fire_callbacks(&port_data->callbacks, port_dev, + enabled_int); + } + /* Clear the pending interrupts */ + GPIO->IFC = 0xFFFF; +} + + +static const struct gpio_driver_api gpio_gecko_driver_api = { + .config = gpio_gecko_configure, + .write = gpio_gecko_write, + .read = gpio_gecko_read, + .manage_callback = gpio_gecko_manage_callback, + .enable_callback = gpio_gecko_enable_callback, + .disable_callback = gpio_gecko_disable_callback, +}; + +static const struct gpio_driver_api gpio_gecko_common_driver_api = { + .manage_callback = gpio_gecko_manage_callback, + .enable_callback = gpio_gecko_enable_callback, + .disable_callback = gpio_gecko_disable_callback, +}; + +#ifdef CONFIG_GPIO_GECKO +static int gpio_gecko_common_init(struct device *dev); + +static const struct gpio_gecko_common_config gpio_gecko_common_config = { +}; + +static struct gpio_gecko_common_data gpio_gecko_common_data; + +DEVICE_AND_API_INIT(gpio_gecko_common, CONFIG_GPIO_GECKO_COMMON_NAME, + gpio_gecko_common_init, + &gpio_gecko_common_data, &gpio_gecko_common_config, + POST_KERNEL, CONFIG_GPIO_GECKO_COMMON_INIT_PRIORITY, + &gpio_gecko_common_driver_api); + +static int gpio_gecko_common_init(struct device *dev) +{ + gpio_gecko_common_data.count = 0; + IRQ_CONNECT(GPIO_EVEN_IRQn, CONFIG_GPIO_GECKO_COMMON_PRI, + gpio_gecko_common_isr, DEVICE_GET(gpio_gecko_common), 0); + + IRQ_CONNECT(GPIO_ODD_IRQn, CONFIG_GPIO_GECKO_COMMON_PRI, + gpio_gecko_common_isr, DEVICE_GET(gpio_gecko_common), 0); + + irq_enable(GPIO_EVEN_IRQn); + irq_enable(GPIO_ODD_IRQn); + + return 0; +} +#endif /* CONFIG_GPIO_GECKO */ + + +#ifdef CONFIG_GPIO_GECKO_PORTA +static int gpio_gecko_porta_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_porta_config = { + .gpio_base = &GPIO->P[gpioPortA], + .gpio_index = gpioPortA, +}; + +static struct gpio_gecko_data gpio_gecko_porta_data; + +DEVICE_AND_API_INIT(gpio_gecko_porta, CONFIG_GPIO_GECKO_PORTA_NAME, + gpio_gecko_porta_init, + &gpio_gecko_porta_data, &gpio_gecko_porta_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_porta_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTA */ + +#ifdef CONFIG_GPIO_GECKO_PORTB +static int gpio_gecko_portb_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_portb_config = { + .gpio_base = &GPIO->P[gpioPortB], + .gpio_index = gpioPortB, +}; + +static struct gpio_gecko_data gpio_gecko_portb_data; + +DEVICE_AND_API_INIT(gpio_gecko_portb, CONFIG_GPIO_GECKO_PORTB_NAME, + gpio_gecko_portb_init, + &gpio_gecko_portb_data, &gpio_gecko_portb_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_portb_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTB */ + +#ifdef CONFIG_GPIO_GECKO_PORTC +static int gpio_gecko_portc_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_portc_config = { + .gpio_base = &GPIO->P[gpioPortC], + .gpio_index = gpioPortC, +}; + +static struct gpio_gecko_data gpio_gecko_portc_data; + +DEVICE_AND_API_INIT(gpio_gecko_portc, CONFIG_GPIO_GECKO_PORTC_NAME, + gpio_gecko_portc_init, + &gpio_gecko_portc_data, &gpio_gecko_portc_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_portc_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTC */ + +#ifdef CONFIG_GPIO_GECKO_PORTD +static int gpio_gecko_portd_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_portd_config = { + .gpio_base = &GPIO->P[gpioPortD], + .gpio_index = gpioPortD, +}; + +static struct gpio_gecko_data gpio_gecko_portd_data; + +DEVICE_AND_API_INIT(gpio_gecko_portd, CONFIG_GPIO_GECKO_PORTD_NAME, + gpio_gecko_portd_init, + &gpio_gecko_portd_data, &gpio_gecko_portd_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_portd_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTD */ + +#ifdef CONFIG_GPIO_GECKO_PORTE +static int gpio_gecko_porte_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_porte_config = { + .gpio_base = &GPIO->P[gpioPortE], + .gpio_index = gpioPortE, +}; + +static struct gpio_gecko_data gpio_gecko_porte_data; + +DEVICE_AND_API_INIT(gpio_gecko_porte, CONFIG_GPIO_GECKO_PORTE_NAME, + gpio_gecko_porte_init, + &gpio_gecko_porte_data, &gpio_gecko_porte_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_porte_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTE */ + +#ifdef CONFIG_GPIO_GECKO_PORTF +static int gpio_gecko_portf_init(struct device *dev); + +static const struct gpio_gecko_config gpio_gecko_portf_config = { + .gpio_base = &GPIO->P[gpioPortF], + .gpio_index = gpioPortF, +}; + +static struct gpio_gecko_data gpio_gecko_portf_data; + +DEVICE_AND_API_INIT(gpio_gecko_portf, CONFIG_GPIO_GECKO_PORTF_NAME, + gpio_gecko_portf_init, + &gpio_gecko_portf_data, &gpio_gecko_portf_config, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &gpio_gecko_driver_api); + +static int gpio_gecko_portf_init(struct device *dev) +{ + gpio_gecko_add_port(&gpio_gecko_common_data, dev); + return 0; +} +#endif /* CONFIG_GPIO_GECKO_PORTF */ diff --git a/ext/hal/silabs/gecko/Kbuild b/ext/hal/silabs/gecko/Kbuild index f761d4db15..eb0c45d9ac 100644 --- a/ext/hal/silabs/gecko/Kbuild +++ b/ext/hal/silabs/gecko/Kbuild @@ -1,4 +1,5 @@ obj-$(CONFIG_HAS_CMU) += emlib/src/em_cmu.o obj-y += emlib/src/em_system.o +obj-$(CONFIG_GPIO_GECKO) += emlib/src/em_gpio.o obj-$(CONFIG_SOC_SERIES_EFM32WG) += Device/SiliconLabs/EFM32WG/Source/system_efm32wg.o