| /* |
| * Copyright (c) 2022, Renesas Electronics Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT renesas_smartbond_gpio |
| |
| #include <zephyr/drivers/gpio/gpio_utils.h> |
| |
| #include <stdint.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/irq.h> |
| |
| #include <DA1469xAB.h> |
| |
| #define GPIO_MODE_RESET 0x200 |
| |
| #define GPIO_PUPD_INPUT 0 |
| #define GPIO_PUPD_INPUT_PU 1 |
| #define GPIO_PUPD_INPUT_PD 2 |
| #define GPIO_PUPD_OUTPUT 3 |
| |
| /* GPIO P0 and P1 share single GPIO and WKUP peripheral instance with separate |
| * set registers for P0 and P1 interleaved. The starting registers for direct |
| * data access, bit access, mode, latch and wake-up controller are defined in |
| * device tree. |
| */ |
| |
| struct gpio_smartbond_data_regs { |
| uint32_t data; |
| uint32_t _reserved0; |
| uint32_t set; |
| uint32_t _reserved1; |
| uint32_t reset; |
| }; |
| |
| struct gpio_smartbond_latch_regs { |
| uint32_t latch; |
| uint32_t set; |
| uint32_t reset; |
| }; |
| |
| struct gpio_smartbond_wkup_regs { |
| uint32_t select; |
| uint32_t _reserved0[4]; |
| uint32_t pol; |
| uint32_t _reserved1[4]; |
| uint32_t status; |
| uint32_t _reserved2[2]; |
| uint32_t clear; |
| uint32_t _reserved3[2]; |
| uint32_t sel; |
| }; |
| |
| struct gpio_smartbond_data { |
| /* gpio_driver_data needs to be first */ |
| struct gpio_driver_data common; |
| sys_slist_t callbacks; |
| }; |
| |
| struct gpio_smartbond_config { |
| /* gpio_driver_config needs to be first */ |
| struct gpio_driver_config common; |
| volatile struct gpio_smartbond_data_regs *data_regs; |
| volatile uint32_t *mode_regs; |
| volatile struct gpio_smartbond_latch_regs *latch_regs; |
| volatile struct gpio_smartbond_wkup_regs *wkup_regs; |
| }; |
| |
| static void gpio_smartbond_wkup_init(void) |
| { |
| static bool wkup_init; |
| |
| /* Wakeup controller is shared for both GPIO ports and should |
| * be initialized only once. |
| */ |
| if (!wkup_init) { |
| WAKEUP->WKUP_CTRL_REG = 0; |
| WAKEUP->WKUP_CLEAR_P0_REG = 0xffffffff; |
| WAKEUP->WKUP_CLEAR_P1_REG = 0xffffffff; |
| WAKEUP->WKUP_SELECT_P0_REG = 0; |
| WAKEUP->WKUP_SELECT_P1_REG = 0; |
| WAKEUP->WKUP_SEL_GPIO_P0_REG = 0; |
| WAKEUP->WKUP_SEL_GPIO_P1_REG = 0; |
| WAKEUP->WKUP_RESET_IRQ_REG = 0; |
| |
| CRG_TOP->CLK_TMR_REG |= CRG_TOP_CLK_TMR_REG_WAKEUPCT_ENABLE_Msk; |
| |
| WAKEUP->WKUP_CTRL_REG = WAKEUP_WKUP_CTRL_REG_WKUP_ENABLE_IRQ_Msk; |
| |
| wkup_init = true; |
| } |
| } |
| |
| static int gpio_smartbond_pin_configure(const struct device *dev, |
| gpio_pin_t pin, gpio_flags_t flags) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| if (flags == GPIO_DISCONNECTED) { |
| /* Reset to default value */ |
| config->mode_regs[pin] = GPIO_MODE_RESET; |
| return 0; |
| } |
| |
| if ((flags & GPIO_INPUT) && (flags & GPIO_OUTPUT)) { |
| /* Simultaneous in/out is not supported */ |
| return -ENOTSUP; |
| } |
| |
| if (flags & GPIO_OUTPUT) { |
| config->mode_regs[pin] = GPIO_PUPD_OUTPUT << GPIO_P0_00_MODE_REG_PUPD_Pos; |
| |
| if (flags & GPIO_OUTPUT_INIT_LOW) { |
| config->data_regs->reset = BIT(pin); |
| } else if (flags & GPIO_OUTPUT_INIT_HIGH) { |
| config->data_regs->set = BIT(pin); |
| } |
| |
| return 0; |
| } |
| |
| if (flags & GPIO_PULL_DOWN) { |
| config->mode_regs[pin] = GPIO_PUPD_INPUT_PD << GPIO_P0_00_MODE_REG_PUPD_Pos; |
| } else if (flags & GPIO_PULL_UP) { |
| config->mode_regs[pin] = GPIO_PUPD_INPUT_PU << GPIO_P0_00_MODE_REG_PUPD_Pos; |
| } else { |
| config->mode_regs[pin] = GPIO_PUPD_INPUT << GPIO_P0_00_MODE_REG_PUPD_Pos; |
| } |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_port_get_raw(const struct device *dev, |
| gpio_port_value_t *value) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| *value = config->data_regs->data; |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_port_set_masked_raw(const struct device *dev, |
| gpio_port_pins_t mask, |
| gpio_port_value_t value) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| config->data_regs->data = value & mask; |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_port_set_bits_raw(const struct device *dev, |
| gpio_port_pins_t pins) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| config->data_regs->set = pins; |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_port_clear_bits_raw(const struct device *dev, |
| gpio_port_pins_t pins) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| config->data_regs->reset = pins; |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_port_toggle_bits(const struct device *dev, |
| gpio_port_pins_t mask) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| volatile uint32_t *reg = &config->data_regs->data; |
| |
| *reg = *reg ^ mask; |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_pin_interrupt_configure(const struct device *dev, |
| gpio_pin_t pin, |
| enum gpio_int_mode mode, |
| enum gpio_int_trig trig) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| |
| /* Not supported by hardware */ |
| if (mode == GPIO_INT_MODE_LEVEL) { |
| return -ENOTSUP; |
| } |
| |
| if (mode == GPIO_INT_MODE_DISABLED) { |
| config->wkup_regs->sel &= ~BIT(pin); |
| config->wkup_regs->clear = BIT(pin); |
| } else { |
| if (trig == GPIO_INT_TRIG_BOTH) { |
| /* Not supported by hardware */ |
| return -ENOTSUP; |
| } |
| |
| if (trig == GPIO_INT_TRIG_HIGH) { |
| config->wkup_regs->pol &= ~BIT(pin); |
| } else { |
| config->wkup_regs->pol |= BIT(pin); |
| } |
| |
| config->wkup_regs->sel |= BIT(pin); |
| } |
| |
| return 0; |
| } |
| |
| static int gpio_smartbond_manage_callback(const struct device *dev, |
| struct gpio_callback *callback, bool set) |
| { |
| struct gpio_smartbond_data *data = dev->data; |
| |
| return gpio_manage_callback(&data->callbacks, callback, set); |
| } |
| |
| static void gpio_smartbond_isr(const struct device *dev) |
| { |
| const struct gpio_smartbond_config *config = dev->config; |
| struct gpio_smartbond_data *data = dev->data; |
| uint32_t stat; |
| |
| WAKEUP->WKUP_RESET_IRQ_REG = WAKEUP_WKUP_RESET_IRQ_REG_WKUP_IRQ_RST_Msk; |
| |
| stat = config->wkup_regs->status; |
| config->wkup_regs->clear = stat; |
| |
| gpio_fire_callbacks(&data->callbacks, dev, stat); |
| } |
| |
| /* GPIO driver registration */ |
| static const struct gpio_driver_api gpio_smartbond_drv_api_funcs = { |
| .pin_configure = gpio_smartbond_pin_configure, |
| .port_get_raw = gpio_smartbond_port_get_raw, |
| .port_set_masked_raw = gpio_smartbond_port_set_masked_raw, |
| .port_set_bits_raw = gpio_smartbond_port_set_bits_raw, |
| .port_clear_bits_raw = gpio_smartbond_port_clear_bits_raw, |
| .port_toggle_bits = gpio_smartbond_port_toggle_bits, |
| .pin_interrupt_configure = gpio_smartbond_pin_interrupt_configure, |
| .manage_callback = gpio_smartbond_manage_callback, |
| }; |
| |
| #define GPIO_SMARTBOND_DEVICE(id) \ |
| static const struct gpio_smartbond_config gpio_smartbond_p##id##_config = { \ |
| .common = { \ |
| .port_pin_mask = \ |
| GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \ |
| }, \ |
| .data_regs = (volatile struct gpio_smartbond_data_regs *) \ |
| DT_INST_REG_ADDR_BY_NAME(id, data), \ |
| .mode_regs = (volatile uint32_t *)DT_INST_REG_ADDR_BY_NAME(id, mode), \ |
| .latch_regs = (volatile struct gpio_smartbond_latch_regs *) \ |
| DT_INST_REG_ADDR_BY_NAME(id, latch), \ |
| .wkup_regs = (volatile struct gpio_smartbond_wkup_regs *) \ |
| DT_INST_REG_ADDR_BY_NAME(id, wkup), \ |
| }; \ |
| \ |
| static struct gpio_smartbond_data gpio_smartbond_p##id##_data; \ |
| \ |
| static int gpio_smartbond_##id##_init(const struct device *dev) \ |
| { \ |
| gpio_smartbond_wkup_init(); \ |
| IRQ_CONNECT(DT_INST_IRQN(id), \ |
| DT_INST_IRQ(id, priority), \ |
| gpio_smartbond_isr, \ |
| DEVICE_DT_INST_GET(id), 0); \ |
| irq_enable(DT_INST_IRQN(id)); \ |
| return 0; \ |
| } \ |
| \ |
| DEVICE_DT_INST_DEFINE(id, gpio_smartbond_##id##_init, \ |
| NULL, \ |
| &gpio_smartbond_p##id##_data, \ |
| &gpio_smartbond_p##id##_config, \ |
| POST_KERNEL, \ |
| CONFIG_GPIO_INIT_PRIORITY, \ |
| &gpio_smartbond_drv_api_funcs); |
| |
| DT_INST_FOREACH_STATUS_OKAY(GPIO_SMARTBOND_DEVICE) |