| /* |
| * Copyright (c) 2023 TOKITA Hiroshi <tokita.hiroshi@fujitsu.com> |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT renesas_ra_gpio |
| |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/init.h> |
| #include <zephyr/irq.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/gpio/gpio_utils.h> |
| #include <zephyr/drivers/interrupt_controller/intc_ra_icu.h> |
| #include <zephyr/drivers/pinctrl.h> |
| |
| enum { |
| PCNTR1_OFFSET = 0x0, |
| PCNTR2_OFFSET = 0x4, |
| PCNTR3_OFFSET = 0x8, |
| PCNTR4_OFFSET = 0xc |
| }; |
| |
| enum { |
| PCNTR1_PDR0_OFFSET = 0, |
| PCNTR1_PODR0_OFFSET = 16, |
| }; |
| |
| enum { |
| PCNTR2_PIDR0_OFFSET = 0, |
| PCNTR2_EIDR0_OFFSET = 16, |
| }; |
| |
| enum { |
| PCNTR3_POSR0_OFFSET = 0, |
| PCNTR3_PORR0_OFFSET = 16, |
| }; |
| |
| enum { |
| PCNTR4_EOSR0_OFFSET = 0, |
| PCNTR4_EORR0_OFFSET = 16, |
| }; |
| |
| struct gpio_ra_irq_info { |
| const uint8_t *const pins; |
| size_t num; |
| int port_irq; |
| int irq; |
| uint32_t priority; |
| uint32_t flags; |
| ra_isr_handler isr; |
| }; |
| |
| struct gpio_ra_pin_irq_info { |
| const struct gpio_ra_irq_info *info; |
| uint8_t pin; |
| }; |
| |
| struct gpio_ra_config { |
| struct gpio_driver_config common; |
| mem_addr_t regs; |
| struct gpio_ra_irq_info *irq_info; |
| uint32_t irq_info_size; |
| uint16_t port; |
| }; |
| |
| struct gpio_ra_data { |
| struct gpio_driver_data common; |
| struct gpio_ra_pin_irq_info port_irq_info[16]; |
| sys_slist_t callbacks; |
| }; |
| |
| static inline uint32_t gpio_ra_irq_info_event(const struct gpio_ra_irq_info *info) |
| { |
| return ((info->flags & RA_ICU_FLAG_EVENT_MASK) >> RA_ICU_FLAG_EVENT_OFFSET); |
| } |
| |
| static void gpio_ra_isr(const struct device *dev, uint32_t port_irq) |
| { |
| struct gpio_ra_data *data = dev->data; |
| const struct gpio_ra_pin_irq_info *pin_irq = &data->port_irq_info[port_irq]; |
| const int irq = ra_icu_query_exists_irq(gpio_ra_irq_info_event(pin_irq->info)); |
| |
| if (irq >= 0) { |
| gpio_fire_callbacks(&data->callbacks, dev, BIT(pin_irq->pin)); |
| ra_icu_clear_int_flag(irq); |
| } |
| } |
| |
| static const struct gpio_ra_irq_info *query_irq_info(const struct device *dev, uint32_t pin) |
| { |
| const struct gpio_ra_config *config = dev->config; |
| |
| for (int i = 0; i < config->irq_info_size; i++) { |
| const struct gpio_ra_irq_info *info = &config->irq_info[i]; |
| |
| for (int j = 0; j < info->num; j++) { |
| if (info->pins[j] == pin) { |
| return info; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static inline uint32_t reg_read(const struct device *dev, size_t offset) |
| { |
| const struct gpio_ra_config *config = dev->config; |
| |
| return sys_read32(config->regs + offset); |
| } |
| |
| static inline void reg_write(const struct device *dev, size_t offset, uint32_t value) |
| { |
| const struct gpio_ra_config *config = dev->config; |
| |
| sys_write32(value, config->regs + offset); |
| } |
| |
| static inline uint32_t port_read(const struct device *dev) |
| { |
| return reg_read(dev, PCNTR2_OFFSET) & UINT16_MAX; |
| } |
| |
| static int port_write(const struct device *dev, uint16_t value, uint16_t mask) |
| { |
| const uint16_t set = value & mask; |
| const uint16_t clr = (~value) & mask; |
| |
| reg_write(dev, PCNTR3_OFFSET, (clr << PCNTR3_PORR0_OFFSET) | set << PCNTR3_POSR0_OFFSET); |
| |
| return 0; |
| } |
| |
| static int gpio_ra_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) |
| { |
| const enum gpio_int_mode mode = |
| flags & (GPIO_INT_EDGE | GPIO_INT_DISABLE | GPIO_INT_ENABLE); |
| const enum gpio_int_trig trig = flags & (GPIO_INT_LOW_0 | GPIO_INT_HIGH_1); |
| const struct gpio_ra_config *config = dev->config; |
| struct gpio_ra_data *data = dev->data; |
| struct pinctrl_ra_pin pincfg = {0}; |
| |
| if ((flags & GPIO_OUTPUT) && (flags & GPIO_INPUT)) { |
| /* Pin cannot be configured as input and output */ |
| return -ENOTSUP; |
| } else if (!(flags & (GPIO_INPUT | GPIO_OUTPUT))) { |
| /* Pin has to be configured as input or output */ |
| return -ENOTSUP; |
| } |
| |
| if (flags & GPIO_OUTPUT) { |
| pincfg.config |= BIT(PmnPFS_PDR_POS); |
| } |
| |
| if (flags & GPIO_PULL_UP) { |
| pincfg.config |= BIT(PmnPFS_PCR_POS); |
| } |
| |
| if ((flags & GPIO_SINGLE_ENDED) && (flags & GPIO_LINE_OPEN_DRAIN)) { |
| pincfg.config |= BIT(PmnPFS_NCODR_POS); |
| } |
| |
| if (flags & GPIO_INT_ENABLE) { |
| pincfg.config |= BIT(PmnPFS_ISEL_POS); |
| } |
| |
| pincfg.config &= ~BIT(PmnPFS_PMR_POS); |
| |
| pincfg.pin = pin; |
| pincfg.port = config->port; |
| |
| if (flags & GPIO_INT_ENABLE) { |
| const struct gpio_ra_irq_info *irq_info; |
| uint32_t intcfg; |
| int irqn; |
| |
| if (mode == GPIO_INT_MODE_LEVEL) { |
| if (trig != GPIO_INT_TRIG_LOW) { |
| return -ENOTSUP; |
| } |
| |
| intcfg = ICU_LOW_LEVEL; |
| } else if (mode == GPIO_INT_MODE_EDGE) { |
| switch (trig) { |
| case GPIO_INT_TRIG_LOW: |
| intcfg = ICU_FALLING; |
| break; |
| case GPIO_INT_TRIG_HIGH: |
| intcfg = ICU_RISING; |
| break; |
| case GPIO_INT_TRIG_BOTH: |
| intcfg = ICU_BOTH_EDGE; |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| } else { |
| return -ENOTSUP; |
| } |
| |
| irq_info = query_irq_info(dev, pin); |
| if (irq_info == NULL) { |
| return -EINVAL; |
| } |
| |
| irqn = ra_icu_irq_connect_dynamic( |
| irq_info->irq, irq_info->priority, irq_info->isr, dev, |
| (intcfg << RA_ICU_FLAG_INTCFG_OFFSET) | irq_info->flags); |
| if (irqn < 0) { |
| return irqn; |
| } |
| |
| data->port_irq_info[irq_info->port_irq].pin = pin; |
| data->port_irq_info[irq_info->port_irq].info = irq_info; |
| |
| irq_enable(irqn); |
| } |
| |
| return pinctrl_configure_pins(&pincfg, 1, PINCTRL_REG_NONE); |
| } |
| |
| #ifdef CONFIG_GPIO_GET_CONFIG |
| static int gpio_ra_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *flags) |
| { |
| const struct gpio_ra_config *config = dev->config; |
| const struct gpio_ra_irq_info *irq_info; |
| struct pinctrl_ra_pin pincfg; |
| ra_isr_handler cb; |
| const void *cbarg; |
| uint32_t intcfg; |
| int irqn; |
| int err; |
| |
| memset(flags, 0, sizeof(gpio_flags_t)); |
| |
| err = pinctrl_ra_query_config(config->port, pin, &pincfg); |
| if (err < 0) { |
| return err; |
| } |
| |
| if (pincfg.config & BIT(PmnPFS_PDR_POS)) { |
| *flags |= GPIO_OUTPUT; |
| } else { |
| *flags |= GPIO_INPUT; |
| } |
| |
| if (pincfg.config & BIT(PmnPFS_ISEL_POS)) { |
| *flags |= GPIO_INT_ENABLE; |
| } |
| |
| if (pincfg.config & BIT(PmnPFS_PCR_POS)) { |
| *flags |= GPIO_PULL_UP; |
| } |
| |
| irq_info = query_irq_info(dev, pin); |
| if (irq_info == NULL) { |
| return 0; |
| } |
| |
| irqn = ra_icu_query_exists_irq(gpio_ra_irq_info_event(irq_info)); |
| if (irqn < 0) { |
| return 0; |
| } |
| |
| ra_icu_query_irq_config(irqn, &intcfg, &cb, &cbarg); |
| |
| if (cbarg != dev) { |
| return 0; |
| } |
| |
| if (intcfg == ICU_FALLING) { |
| *flags |= GPIO_INT_TRIG_LOW; |
| *flags |= GPIO_INT_MODE_EDGE; |
| } else if (intcfg == ICU_RISING) { |
| *flags |= GPIO_INT_TRIG_HIGH; |
| *flags |= GPIO_INT_MODE_EDGE; |
| } else if (intcfg == ICU_BOTH_EDGE) { |
| *flags |= GPIO_INT_TRIG_BOTH; |
| *flags |= GPIO_INT_MODE_EDGE; |
| } else if (intcfg == ICU_LOW_LEVEL) { |
| *flags |= GPIO_INT_TRIG_LOW; |
| *flags |= GPIO_INT_MODE_LEVEL; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int gpio_ra_port_get_raw(const struct device *dev, gpio_port_value_t *value) |
| { |
| *value = port_read(dev); |
| |
| return 0; |
| } |
| |
| static int gpio_ra_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, |
| gpio_port_value_t value) |
| { |
| uint16_t port_val; |
| |
| port_val = port_read(dev); |
| port_val = (port_val & ~mask) | (value & mask); |
| return port_write(dev, port_val, UINT16_MAX); |
| } |
| |
| static int gpio_ra_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
| { |
| uint16_t port_val; |
| |
| port_val = port_read(dev); |
| port_val |= pins; |
| return port_write(dev, port_val, UINT16_MAX); |
| } |
| |
| static int gpio_ra_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
| { |
| uint16_t port_val; |
| |
| port_val = port_read(dev); |
| port_val &= ~pins; |
| return port_write(dev, port_val, UINT16_MAX); |
| } |
| |
| static int gpio_ra_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) |
| { |
| uint16_t port_val; |
| |
| port_val = port_read(dev); |
| port_val ^= pins; |
| return port_write(dev, port_val, UINT16_MAX); |
| } |
| |
| static int gpio_ra_manage_callback(const struct device *dev, struct gpio_callback *callback, |
| bool set) |
| { |
| struct gpio_ra_data *data = dev->data; |
| |
| return gpio_manage_callback(&data->callbacks, callback, set); |
| } |
| |
| static int gpio_ra_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, |
| enum gpio_int_mode mode, enum gpio_int_trig trig) |
| { |
| gpio_flags_t pincfg; |
| int err; |
| |
| err = gpio_ra_pin_get_config(dev, pin, &pincfg); |
| if (err < 0) { |
| return err; |
| } |
| |
| return gpio_ra_pin_configure(dev, pin, pincfg | mode | trig); |
| } |
| |
| static const struct gpio_driver_api gpio_ra_driver_api = { |
| .pin_configure = gpio_ra_pin_configure, |
| #ifdef CONFIG_GPIO_GET_CONFIG |
| .pin_get_config = gpio_ra_pin_get_config, |
| #endif |
| .port_get_raw = gpio_ra_port_get_raw, |
| .port_set_masked_raw = gpio_ra_port_set_masked_raw, |
| .port_set_bits_raw = gpio_ra_port_set_bits_raw, |
| .port_clear_bits_raw = gpio_ra_port_clear_bits_raw, |
| .port_toggle_bits = gpio_ra_port_toggle_bits, |
| .pin_interrupt_configure = gpio_ra_pin_interrupt_configure, |
| .manage_callback = gpio_ra_manage_callback, |
| }; |
| |
| #define RA_NUM_PORT_IRQ0 0 |
| #define RA_NUM_PORT_IRQ1 1 |
| #define RA_NUM_PORT_IRQ2 2 |
| #define RA_NUM_PORT_IRQ3 3 |
| #define RA_NUM_PORT_IRQ4 4 |
| #define RA_NUM_PORT_IRQ5 5 |
| #define RA_NUM_PORT_IRQ6 6 |
| #define RA_NUM_PORT_IRQ7 7 |
| #define RA_NUM_PORT_IRQ8 8 |
| #define RA_NUM_PORT_IRQ9 9 |
| #define RA_NUM_PORT_IRQ10 10 |
| #define RA_NUM_PORT_IRQ11 11 |
| #define RA_NUM_PORT_IRQ12 12 |
| #define RA_NUM_PORT_IRQ13 13 |
| #define RA_NUM_PORT_IRQ14 14 |
| #define RA_NUM_PORT_IRQ15 15 |
| |
| #define GPIO_RA_DECL_PINS(n, p, i) \ |
| const uint8_t _CONCAT(n, ___pins##i[]) = {DT_FOREACH_PROP_ELEM_SEP( \ |
| n, _CONCAT(DT_STRING_TOKEN_BY_IDX(n, p, i), _pins), DT_PROP_BY_IDX, (,))}; |
| |
| #define GPIO_RA_IRQ_INFO(n, p, i) \ |
| { \ |
| .port_irq = _CONCAT(RA_NUM_, DT_STRING_UPPER_TOKEN_BY_IDX(n, p, i)), \ |
| .irq = DT_IRQ_BY_IDX(n, i, irq), \ |
| .flags = DT_IRQ_BY_IDX(n, i, flags), \ |
| .priority = DT_IRQ_BY_IDX(n, i, priority), \ |
| .pins = _CONCAT(n, ___pins##i), \ |
| .num = ARRAY_SIZE(_CONCAT(n, ___pins##i)), \ |
| .isr = _CONCAT(n, _CONCAT(gpio_ra_isr_, DT_STRING_TOKEN_BY_IDX(n, p, i))), \ |
| }, |
| |
| #define GPIO_RA_ISR_DECL(n, p, i) \ |
| static void _CONCAT(n, _CONCAT(gpio_ra_isr_, DT_STRING_TOKEN_BY_IDX(n, p, i)))( \ |
| const void *arg) \ |
| { \ |
| gpio_ra_isr((const struct device *)arg, \ |
| _CONCAT(RA_NUM_, DT_STRING_UPPER_TOKEN_BY_IDX(n, p, i))); \ |
| } |
| |
| #define GPIO_RA_INIT(idx) \ |
| static struct gpio_ra_data gpio_ra_data_##idx = {}; \ |
| DT_INST_FOREACH_PROP_ELEM(idx, interrupt_names, GPIO_RA_DECL_PINS); \ |
| DT_INST_FOREACH_PROP_ELEM(idx, interrupt_names, GPIO_RA_ISR_DECL); \ |
| struct gpio_ra_irq_info gpio_ra_irq_info_##idx[] = { \ |
| DT_INST_FOREACH_PROP_ELEM(idx, interrupt_names, GPIO_RA_IRQ_INFO)}; \ |
| static struct gpio_ra_config gpio_ra_config_##idx = { \ |
| .common = { \ |
| .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \ |
| }, \ |
| .regs = DT_INST_REG_ADDR(idx), \ |
| .port = (DT_INST_REG_ADDR(idx) - DT_REG_ADDR(DT_NODELABEL(ioport0))) / \ |
| DT_INST_REG_SIZE(idx), \ |
| .irq_info = gpio_ra_irq_info_##idx, \ |
| .irq_info_size = ARRAY_SIZE(gpio_ra_irq_info_##idx), \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(idx, NULL, NULL, &gpio_ra_data_##idx, &gpio_ra_config_##idx, \ |
| PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, &gpio_ra_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(GPIO_RA_INIT) |