/* * Copyright (c) 2017, Christian Taedcke * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT silabs_gecko_gpio_port #include #include #include #include #include #ifdef CONFIG_SOC_GECKO_DEV_INIT #include #endif #include #if CONFIG_GPIO_GECKO_COMMON_INIT_PRIORITY >= CONFIG_GPIO_INIT_PRIORITY #error CONFIG_GPIO_GECKO_COMMON_INIT_PRIORITY must be less than \ CONFIG_GPIO_INIT_PRIORITY. #endif #if DT_NODE_HAS_PROP(id, peripheral_id) #define GET_GECKO_GPIO_INDEX(id) DT_INST_PROP(id, peripheral_id) #else #if defined(CONFIG_SOC_SERIES_EFR32BG22) || \ defined(CONFIG_SOC_SERIES_EFR32BG27) || \ defined(CONFIG_SOC_SERIES_EFR32MG21) || \ defined(CONFIG_SOC_SERIES_EFR32MG24) #define GECKO_GPIO_PORT_ADDR_SPACE_SIZE sizeof(GPIO_PORT_TypeDef) #else #define GECKO_GPIO_PORT_ADDR_SPACE_SIZE sizeof(GPIO_P_TypeDef) #endif /* Assumption for calculating gpio index: * 1. Address space of the first GPIO port is the address space for GPIO port A */ #define GET_GECKO_GPIO_INDEX(id) (DT_INST_REG_ADDR(id) - DT_REG_ADDR(DT_NODELABEL(gpioa))) \ / GECKO_GPIO_PORT_ADDR_SPACE_SIZE #endif /* DT_NODE_HAS_PROP(id, peripheral_id) */ /* * 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 */ const struct device *ports[NUMBER_OF_PORTS]; size_t count; }; struct gpio_gecko_config { /* gpio_driver_config needs to be first */ struct gpio_driver_config common; GPIO_Port_TypeDef gpio_index; }; struct gpio_gecko_data { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; /* port ISR callback routine address */ sys_slist_t callbacks; /* mask of pins on which interrupt is enabled */ uint32_t int_enabled_mask; }; static inline void gpio_gecko_add_port(struct gpio_gecko_common_data *data, const struct device *dev) { __ASSERT(dev, "No port device!"); data->ports[data->count++] = dev; } static int gpio_gecko_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_Mode_TypeDef mode; unsigned int out = 0U; if (flags & GPIO_OUTPUT) { /* Following modes enable both output and input */ if (flags & GPIO_SINGLE_ENDED) { if (flags & GPIO_LINE_OPEN_DRAIN) { mode = gpioModeWiredAnd; } else { mode = gpioModeWiredOr; } } else { mode = gpioModePushPull; } if (flags & GPIO_OUTPUT_INIT_HIGH) { out = 1U; } else if (flags & GPIO_OUTPUT_INIT_LOW) { out = 0U; } else { out = GPIO_PinOutGet(gpio_index, pin); } } else if (flags & GPIO_INPUT) { if (flags & GPIO_PULL_UP) { mode = gpioModeInputPull; out = 1U; /* pull-up*/ } else if (flags & GPIO_PULL_DOWN) { mode = gpioModeInputPull; /* out = 0 means pull-down*/ } else { mode = gpioModeInput; } } else { /* Neither input nor output mode is selected */ mode = gpioModeDisabled; } /* 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 */ GPIO_PinModeSet(gpio_index, pin, mode, out); return 0; } #ifdef CONFIG_GPIO_GET_CONFIG static int gpio_gecko_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_Mode_TypeDef mode; unsigned int out; gpio_flags_t flags = 0; mode = GPIO_PinModeGet(gpio_index, pin); out = GPIO_PinOutGet(gpio_index, pin); switch (mode) { case gpioModeWiredAnd: flags = GPIO_OUTPUT | GPIO_OPEN_DRAIN; if (out) { flags |= GPIO_OUTPUT_HIGH; } else { flags |= GPIO_OUTPUT_LOW; } break; case gpioModeWiredOr: flags = GPIO_OUTPUT | GPIO_OPEN_SOURCE; if (out) { flags |= GPIO_OUTPUT_HIGH; } else { flags |= GPIO_OUTPUT_LOW; } break; case gpioModePushPull: flags = GPIO_OUTPUT | GPIO_PUSH_PULL; if (out) { flags |= GPIO_OUTPUT_HIGH; } else { flags |= GPIO_OUTPUT_LOW; } break; case gpioModeInputPull: flags = GPIO_INPUT; if (out) { flags |= GPIO_PULL_UP; } else { flags |= GPIO_PULL_DOWN; } break; case gpioModeInput: flags = GPIO_INPUT; break; case gpioModeDisabled: flags = GPIO_DISCONNECTED; break; default: break; } *out_flags = flags; return 0; } #endif static int gpio_gecko_port_get_raw(const struct device *dev, uint32_t *value) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; *value = GPIO_PortInGet(gpio_index); return 0; } static int gpio_gecko_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_PortOutSetVal(gpio_index, value, mask); return 0; } static int gpio_gecko_port_set_bits_raw(const struct device *dev, uint32_t mask) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_PortOutSet(gpio_index, mask); return 0; } static int gpio_gecko_port_clear_bits_raw(const struct device *dev, uint32_t mask) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_PortOutClear(gpio_index, mask); return 0; } static int gpio_gecko_port_toggle_bits(const struct device *dev, uint32_t mask) { const struct gpio_gecko_config *config = dev->config; GPIO_Port_TypeDef gpio_index = config->gpio_index; GPIO_PortOutToggle(gpio_index, mask); return 0; } static int gpio_gecko_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { const struct gpio_gecko_config *config = dev->config; struct gpio_gecko_data *data = dev->data; /* Interrupt on static level is not supported by the hardware */ if (mode == GPIO_INT_MODE_LEVEL) { return -ENOTSUP; } if (mode == GPIO_INT_MODE_DISABLED) { GPIO_IntDisable(BIT(pin)); } else { /* Interrupt line is already in use */ if ((GPIO->IEN & BIT(pin)) != 0) { /* TODO: Return an error only if request is done for * a pin from a different port. */ return -EBUSY; } bool rising_edge = true; bool falling_edge = true; if (trig == GPIO_INT_TRIG_LOW) { rising_edge = false; falling_edge = true; } else if (trig == GPIO_INT_TRIG_HIGH) { rising_edge = true; falling_edge = false; } /* default is GPIO_INT_TRIG_BOTH */ GPIO_ExtIntConfig(config->gpio_index, pin, pin, rising_edge, falling_edge, true); } WRITE_BIT(data->int_enabled_mask, pin, mode != GPIO_INT_DISABLE); return 0; } static int gpio_gecko_manage_callback(const struct device *dev, struct gpio_callback *callback, bool set) { struct gpio_gecko_data *data = dev->data; return gpio_manage_callback(&data->callbacks, callback, set); } /** * Handler for both odd and even pin interrupts */ static void gpio_gecko_common_isr(const struct device *dev) { struct gpio_gecko_common_data *data = dev->data; uint32_t enabled_int, int_status; const 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->data; enabled_int = int_status & port_data->int_enabled_mask; if (enabled_int != 0) { int_status &= ~enabled_int; #if defined(_SILICON_LABS_32B_SERIES_2) GPIO->IF_CLR = enabled_int; #else GPIO->IFC = enabled_int; #endif gpio_fire_callbacks(&port_data->callbacks, port_dev, enabled_int); } } } static const struct gpio_driver_api gpio_gecko_driver_api = { .pin_configure = gpio_gecko_configure, #ifdef CONFIG_GPIO_GET_CONFIG .pin_get_config = gpio_gecko_get_config, #endif .port_get_raw = gpio_gecko_port_get_raw, .port_set_masked_raw = gpio_gecko_port_set_masked_raw, .port_set_bits_raw = gpio_gecko_port_set_bits_raw, .port_clear_bits_raw = gpio_gecko_port_clear_bits_raw, .port_toggle_bits = gpio_gecko_port_toggle_bits, .pin_interrupt_configure = gpio_gecko_pin_interrupt_configure, .manage_callback = gpio_gecko_manage_callback, }; static const struct gpio_driver_api gpio_gecko_common_driver_api = { .manage_callback = gpio_gecko_manage_callback, }; static int gpio_gecko_common_init(const 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_DT_DEFINE(DT_INST(0, silabs_gecko_gpio), gpio_gecko_common_init, NULL, &gpio_gecko_common_data, &gpio_gecko_common_config, PRE_KERNEL_1, CONFIG_GPIO_GECKO_COMMON_INIT_PRIORITY, &gpio_gecko_common_driver_api); static int gpio_gecko_common_init(const struct device *dev) { #ifdef CONFIG_SOC_GECKO_DEV_INIT CMU_ClockEnable(cmuClock_GPIO, true); #endif gpio_gecko_common_data.count = 0; IRQ_CONNECT(GPIO_EVEN_IRQn, DT_IRQ_BY_NAME(DT_INST(0, silabs_gecko_gpio), gpio_even, priority), gpio_gecko_common_isr, DEVICE_DT_GET(DT_INST(0, silabs_gecko_gpio)), 0); IRQ_CONNECT(GPIO_ODD_IRQn, DT_IRQ_BY_NAME(DT_INST(0, silabs_gecko_gpio), gpio_odd, priority), gpio_gecko_common_isr, DEVICE_DT_GET(DT_INST(0, silabs_gecko_gpio)), 0); irq_enable(GPIO_EVEN_IRQn); irq_enable(GPIO_ODD_IRQn); return 0; } #define GPIO_PORT_INIT(idx) \ static int gpio_gecko_port##idx##_init(const struct device *dev); \ \ static const struct gpio_gecko_config gpio_gecko_port##idx##_config = { \ .common = { \ .port_pin_mask = (gpio_port_pins_t)(-1), \ }, \ .gpio_index = GET_GECKO_GPIO_INDEX(idx), \ }; \ \ static struct gpio_gecko_data gpio_gecko_port##idx##_data; \ \ DEVICE_DT_INST_DEFINE(idx, \ gpio_gecko_port##idx##_init, \ NULL, \ &gpio_gecko_port##idx##_data, \ &gpio_gecko_port##idx##_config, \ POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ &gpio_gecko_driver_api); \ \ static int gpio_gecko_port##idx##_init(const struct device *dev) \ { \ gpio_gecko_add_port(&gpio_gecko_common_data, dev); \ return 0; \ } DT_INST_FOREACH_STATUS_OKAY(GPIO_PORT_INIT)