/* * Copyright (c) 2025 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT renesas_rza2m_gpio #include #include #include #include #include "gpio_renesas_rza2m.h" LOG_MODULE_REGISTER(rza2m_gpio, CONFIG_GPIO_LOG_LEVEL); /* clang-format off */ static const struct device *gpio_port_devs[] = { DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PARENT(DT_DRV_INST(0)), DEVICE_DT_GET, (,))}; static const uint32_t gpio_port_regs[] = { DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PARENT(DT_DRV_INST(0)), DT_REG_ADDR, (,))}; /* clang-format on */ /* TINT pin map (According to the Table 51.37 of HW manual) */ static const gpio_rza2m_port_tint_map_t gpio_rza2m_port_tint_map[] = { {TINT31, {PORT0, UNUSED_PORT}, {0x7F, UNUSED_MASK}}, {TINT30, {PORT1, UNUSED_PORT}, {0x1F, UNUSED_MASK}}, {TINT29, {PORT2, PORT3}, {0x0F, 0x01}}, {TINT28, {PORT3, PORT4}, {0x3E, 0x01}}, {TINT27, {PORT4, UNUSED_PORT}, {0xFE, UNUSED_MASK}}, {TINT26, {PORT5, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT25, {PORT5, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT24, {PORT6, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT23, {PORT6, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT22, {PORT7, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT21, {PORT7, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT20, {PORT8, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT19, {PORT8, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT18, {PORT9, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT17, {PORT9, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT16, {PORTA, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT15, {PORTA, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT14, {PORTB, UNUSED_PORT}, {0x3F, UNUSED_MASK}}, {TINT13, {PORTC, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT12, {PORTC, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT11, {PORTD, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT10, {PORTD, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT9, {PORTE, UNUSED_PORT}, {0x7F, UNUSED_MASK}}, {TINT8, {PORTF, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT7, {PORTF, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT6, {PORTG, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT5, {PORTG, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT4, {PORTH, UNUSED_PORT}, {0x7F, UNUSED_MASK}}, {TINT3, {PORTJ, UNUSED_PORT}, {0x0F, UNUSED_MASK}}, {TINT2, {PORTJ, UNUSED_PORT}, {0xF0, UNUSED_MASK}}, {TINT1, {PORTK, UNUSED_PORT}, {0x3F, UNUSED_MASK}}, {TINT0, {PORTL, PORTM}, {0x1F, 0x01}}}; static int gpio_rza2m_get_pin_interrupt_line(uint32_t port, uint8_t pin) { int i, j; for (i = 0; i < ARRAY_SIZE(gpio_rza2m_port_tint_map); i++) { for (j = 0; j < RZA2M_MAX_PORTS_PER_TINT; j++) { if (port == gpio_rza2m_port_tint_map[i].ports[j] && (gpio_rza2m_port_tint_map[i].masks[j] & BIT(pin))) { return gpio_rza2m_port_tint_map[i].tint; } } } return -ENOTSUP; } static uint8_t gpio_rza2m_port_get_output(const struct device *port_dev) { const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; return sys_read8(RZA2M_PODR(int_dev, port)); } static void gpio_rza2m_port_write(const struct device *port_dev, uint8_t value) { const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; sys_write8(value, RZA2M_PODR(int_dev, port)); } static int gpio_rza2m_port_set_bits_raw(const struct device *port_dev, gpio_port_pins_t pins) { uint8_t base_value = gpio_rza2m_port_get_output(port_dev); gpio_rza2m_port_write(port_dev, (base_value | pins)); return 0; } static int gpio_rza2m_port_clear_bits_raw(const struct device *port_dev, gpio_port_pins_t pins) { uint8_t base_value = gpio_rza2m_port_get_output(port_dev); gpio_rza2m_port_write(port_dev, (base_value & ~pins)); return 0; } static void gpio_rza2m_pin_configure_as_gpio(const struct device *port_dev, uint8_t pin, uint8_t dir) { uint16_t mask16; uint16_t reg16; uint8_t reg8; const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; /* Set pin direction */ reg16 = sys_read16(RZA2M_PDR(int_dev, port)); mask16 = RZA2M_PDR_MASK << (pin * 2); reg16 &= ~mask16; reg16 |= dir << (pin * 2); sys_write16(reg16, RZA2M_PDR(int_dev, port)); /* Select general I/O pin function */ reg8 = sys_read8(RZA2M_PMR(int_dev, port)); reg8 &= ~BIT(pin); sys_write8(reg8, RZA2M_PMR(int_dev, port)); } static void gpio_rza2m_set_pin_mux_protection(const struct device *port_dev, bool protect) { const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t reg_value = sys_read8(RZA2M_PWPR(int_dev)); if (protect) { reg_value &= ~RZA2M_PWPR_PFSWE_MASK; sys_write8(reg_value, RZA2M_PWPR(int_dev)); reg_value |= RZA2M_PWPR_B0WI_MASK; sys_write8(reg_value, RZA2M_PWPR(int_dev)); } else { reg_value &= ~RZA2M_PWPR_B0WI_MASK; sys_write8(reg_value, RZA2M_PWPR(int_dev)); reg_value |= RZA2M_PWPR_PFSWE_MASK; sys_write8(reg_value, RZA2M_PWPR(int_dev)); } } static void gpio_rza2m_set_pin_int(const struct device *port_dev, uint8_t pin, bool int_en) { uint8_t value; const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; /* PFS Register Write Protect : OFF */ gpio_rza2m_set_pin_mux_protection(port_dev, false); value = sys_read8(RZA2M_PFS(int_dev, port, pin)); if (int_en) { value |= RZA2M_PFS_ISEL_MASK; } else { value &= ~RZA2M_PFS_ISEL_MASK; } sys_write8(value, RZA2M_PFS(int_dev, port, pin)); /* PFS Register Write Protect : ON */ gpio_rza2m_set_pin_mux_protection(port_dev, true); } static void rza2m_configure_interrupt_line(int tint_num, enum gpio_rza2m_tint_sense sense) { mm_reg_t reg; uint32_t mask; uint32_t reg_val; if (tint_num >= TINT16) { reg = RZA2M_GICD_ICFGR31; } else { reg = RZA2M_GICD_ICFGR30; } mask = (1u << (((tint_num % 16u) * 2u) + 1u)); reg_val = sys_read32(reg); reg_val &= ~mask; if (sense == RZA2M_GPIO_TINT_SENSE_RISING_EDGE) { reg_val |= mask; } sys_write32(reg_val, reg); sys_read32(reg); irq_enable(tint_num); } /* * GPIO HIGH is only possible for * PG_2, PG_3, PG_4, PG_5, PG_6, PG_7, PJ_0, PJ_1, PJ_2, PJ_3, PJ_4, PJ_5, and PJ_6 * see 51.3.5 section of HW Manual */ static const gpio_rza2m_high_allowed_pin_t allowed_gpio_high_pins[] = { {PORTG, 0xFC}, /* 0xFC = 0b11111100 for bits 2-7 */ {PORTJ, 0x7F}, /* 0x7F = 0b01111111 for bits 0-6 */ }; static bool is_gpio_high_allowed(uint8_t port, uint8_t pin) { int num_allowed_pins = ARRAY_SIZE(allowed_gpio_high_pins); int i; for (i = 0; i < num_allowed_pins; i++) { if (allowed_gpio_high_pins[i].port == port && (allowed_gpio_high_pins[i].mask & BIT(pin))) { return true; } } return false; } static int gpio_rza2m_pin_drive_set(const struct device *port_dev, uint8_t pin, gpio_flags_t flags) { uint16_t reg16; uint16_t mask16; const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; uint8_t drive_strength; if (flags & RZA2M_GPIO_DRIVE_HIGH) { if (!is_gpio_high_allowed(port, pin)) { return -ENOTSUP; } drive_strength = RZA2M_GPIO_DRIVE_STRENGTH_HIGH; } else { drive_strength = RZA2M_GPIO_DRIVE_STRENGTH_NORMAL; } reg16 = sys_read16(RZA2M_DSCR(int_dev, port)); mask16 = RZA2M_DSCR_MASK << (pin * 2); reg16 &= ~mask16; reg16 |= drive_strength << (pin * 2); sys_write16(reg16, RZA2M_DSCR(int_dev, port)); return 0; } static int gpio_rza2m_pin_interrupt_configure(const struct device *port_dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { const struct gpio_rza2m_port_config *config = port_dev->config; struct gpio_rza2m_port_data *data = port_dev->data; enum gpio_rza2m_tint_sense sense; int tint_num; /* Validate pin */ if (pin >= config->ngpios) { return -EINVAL; } /* Map mode and trigger to sense */ switch (mode) { case GPIO_INT_MODE_EDGE: if (trig != GPIO_INT_TRIG_HIGH) { return -ENOTSUP; } sense = RZA2M_GPIO_TINT_SENSE_RISING_EDGE; break; case GPIO_INT_MODE_LEVEL: if (trig != GPIO_INT_TRIG_HIGH) { return -ENOTSUP; } sense = RZA2M_GPIO_TINT_SENSE_HIGH_LEVEL; break; case GPIO_INT_MODE_DISABLED: data->mask_irq_en &= ~BIT(pin); gpio_rza2m_set_pin_int(port_dev, pin, false); return 0; default: return -EINVAL; } /* Enable interrupt */ data->mask_irq_en |= BIT(pin); tint_num = gpio_rza2m_get_pin_interrupt_line(config->port, pin); if (tint_num < 0) { return tint_num; } rza2m_configure_interrupt_line(tint_num, sense); gpio_rza2m_set_pin_int(port_dev, pin, true); return 0; } static int gpio_rza2m_pin_configure(const struct device *port_dev, gpio_pin_t pin, gpio_flags_t flags) { int ret = 0; const struct gpio_rza2m_port_config *config = port_dev->config; if (pin >= config->ngpios) { LOG_ERR("provided pin %d > %d (ngpios)", pin, config->ngpios); return -EINVAL; } if ((flags & GPIO_PULL_UP) || (flags & GPIO_PULL_DOWN)) { return -ENOTSUP; } if (!flags) { /* Disconnected mode */ gpio_rza2m_pin_configure_as_gpio(port_dev, pin, RZA2M_PDR_HIZ); } else if (!(flags & GPIO_OPEN_DRAIN)) { /* Configure pin direction */ if (flags & GPIO_OUTPUT) { gpio_rza2m_pin_configure_as_gpio(port_dev, pin, RZA2M_PDR_OUTPUT); } else if (flags & GPIO_INPUT) { gpio_rza2m_pin_configure_as_gpio(port_dev, pin, RZA2M_PDR_INPUT); } /* Configure pin drive strength */ ret = gpio_rza2m_pin_drive_set(port_dev, pin, flags); if (ret) { LOG_ERR("unable to set gpio drive level"); return ret; } /* Configure pin initial value */ if (flags & GPIO_OUTPUT_INIT_HIGH) { ret = gpio_rza2m_port_set_bits_raw(port_dev, BIT(pin)); } else if (flags & GPIO_OUTPUT_INIT_LOW) { ret = gpio_rza2m_port_clear_bits_raw(port_dev, BIT(pin)); } } return ret; } #ifdef CONFIG_GPIO_GET_CONFIG static int gpio_rza2m_pin_get_config(const struct device *port_dev, gpio_pin_t pin, gpio_flags_t *flags) { const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; uint16_t mask16; uint16_t reg16; uint8_t reg8; /* Get pin direction */ reg16 = sys_read16(RZA2M_PDR(int_dev, port)); mask16 = RZA2M_PDR_MASK << (pin * 2); reg16 &= mask16; if ((reg16 >> (pin * 2)) == RZA2M_PDR_INPUT) { *flags |= GPIO_INPUT; } else if ((reg16 >> (pin * 2)) == RZA2M_PDR_OUTPUT) { *flags |= GPIO_OUTPUT; } /* Get pin initial value */ reg8 = sys_read8(RZA2M_PODR(int_dev, port)); if (reg8 & BIT(pin)) { *flags |= GPIO_OUTPUT_INIT_HIGH; } else { *flags |= GPIO_OUTPUT_INIT_LOW; } /* Get pin drive strength */ reg16 = sys_read16(RZA2M_DSCR(int_dev, port)); mask16 = RZA2M_DSCR_MASK << (pin * 2); reg16 &= mask16; if ((reg16 >> (pin * 2)) == RZA2M_GPIO_DRIVE_STRENGTH_HIGH) { *flags |= RZA2M_GPIO_DRIVE_HIGH; } return 0; } #endif static int gpio_rza2m_port_get_raw(const struct device *port_dev, gpio_port_value_t *value) { const struct gpio_rza2m_port_config *config = port_dev->config; const struct device *int_dev = config->int_dev; uint8_t port = config->port; *value = sys_read8(RZA2M_PIDR(int_dev, port)); return 0; } static int gpio_rza2m_port_set_masked_raw(const struct device *port_dev, gpio_port_pins_t mask, gpio_port_value_t value) { uint8_t base_value = gpio_rza2m_port_get_output(port_dev); gpio_rza2m_port_write(port_dev, (base_value & ~mask) | (value & mask)); return 0; } static int gpio_rza2m_port_toggle_bits(const struct device *port_dev, gpio_port_pins_t pins) { uint8_t base_value = gpio_rza2m_port_get_output(port_dev); gpio_rza2m_port_write(port_dev, (base_value ^ pins)); return 0; } static int gpio_rza2m_manage_callback(const struct device *port_dev, struct gpio_callback *callback, bool set) { struct gpio_rza2m_port_data *data = port_dev->data; gpio_manage_callback(&data->callbacks, callback, set); return 0; } static DEVICE_API(gpio, gpio_rza2m_driver_api) = { .pin_configure = gpio_rza2m_pin_configure, #ifdef CONFIG_GPIO_GET_CONFIG .pin_get_config = gpio_rza2m_pin_get_config, #endif .port_get_raw = gpio_rza2m_port_get_raw, .port_set_masked_raw = gpio_rza2m_port_set_masked_raw, .port_set_bits_raw = gpio_rza2m_port_set_bits_raw, .port_clear_bits_raw = gpio_rza2m_port_clear_bits_raw, .port_toggle_bits = gpio_rza2m_port_toggle_bits, .pin_interrupt_configure = gpio_rza2m_pin_interrupt_configure, .manage_callback = gpio_rza2m_manage_callback, }; static void gpio_rza2m_isr_common(uint16_t idx) { gpio_port_value_t value; for (int j = 0; j < RZA2M_MAX_PORTS_PER_TINT; j++) { uint32_t port = gpio_rza2m_port_tint_map[idx].ports[j]; uint16_t mask = gpio_rza2m_port_tint_map[idx].masks[j]; if (port == UNUSED_PORT) { continue; } for (int i = 0; i < ARRAY_SIZE(gpio_port_regs); i++) { if (gpio_port_regs[i] == port) { const struct device *port_dev = gpio_port_devs[i]; struct gpio_rza2m_port_data *data = port_dev->data; int pin; gpio_rza2m_port_get_raw(port_dev, &value); pin = find_lsb_set(data->mask_irq_en & (value & mask)) - 1; if (pin >= 0) { gpio_fire_callbacks(&data->callbacks, port_dev, BIT(pin)); } break; } } } } static int gpio_rza2m_int_init(const struct device *dev) { const struct gpio_rza2m_tint_config *config = dev->config; DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); config->gpio_int_init(); return 0; } static int gpio_rza2m_port_init(const struct device *port_dev) { return 0; } #define GPIO_RZA2M_IRQ_DECLARE_ISR(irq_idx, node_id) \ static void gpio_rza2m_##irq_idx##_isr(void *param) \ { \ int idx = (ARRAY_SIZE(gpio_rza2m_port_tint_map) + TINT0) - \ (DT_IRQ_BY_IDX(node_id, irq_idx, irq) - GIC_SPI_INT_BASE) - 1; \ \ gpio_rza2m_isr_common(idx); \ } #define GPIO_RZA2M_DECLARE_ALL_IRQS(node_id) \ LISTIFY(DT_NUM_IRQS(node_id), GPIO_RZA2M_IRQ_DECLARE_ISR, (;), node_id) #define GPIO_RZA2M_TINT_CONNECT(irq_idx, node_id) \ IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, irq_idx, irq) - GIC_SPI_INT_BASE, \ DT_IRQ_BY_IDX(node_id, irq_idx, priority), gpio_rza2m_##irq_idx##_isr, NULL, \ DT_IRQ_BY_IDX(node_id, irq_idx, flags)); #define GPIO_RZA2M_TINT_CONNECT_FUNC(node_id) \ static void gpio_rza2m_tint_connect_func##node_id(void) \ { \ LISTIFY(DT_NUM_IRQS(node_id), \ GPIO_RZA2M_TINT_CONNECT, (;), node_id) \ } #define GPIO_RZA2M_INT_INIT(node_id) \ GPIO_RZA2M_DECLARE_ALL_IRQS(node_id) \ GPIO_RZA2M_TINT_CONNECT_FUNC(node_id) \ static const struct gpio_rza2m_tint_config gpio_rza2m_tint_cfg_##node_id = { \ DEVICE_MMIO_ROM_INIT(DT_PARENT(DT_INST(0, renesas_rza2m_gpio_int))), \ .gpio_int_init = gpio_rza2m_tint_connect_func##node_id, \ }; \ static struct gpio_rza2m_tint_data gpio_rza2m_tint_data_##node_id; \ DEVICE_DT_DEFINE(node_id, gpio_rza2m_int_init, NULL, &gpio_rza2m_tint_data_##node_id, \ &gpio_rza2m_tint_cfg_##node_id, POST_KERNEL, \ UTIL_DEC(CONFIG_GPIO_INIT_PRIORITY), NULL); #define GPIO_RZA2M_PORT_INIT(inst) \ static const struct gpio_rza2m_port_config gpio_rza2m_cfg_##inst = { \ .common = \ { \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \ }, \ .port = DT_INST_REG_ADDR(inst), \ .ngpios = DT_INST_PROP(inst, ngpios), \ .int_dev = DEVICE_DT_GET_OR_NULL(DT_INST(0, renesas_rza2m_gpio_int)), \ }; \ static struct gpio_rza2m_port_data gpio_rza2m_data_##inst = {.mask_irq_en = 0}; \ \ DEVICE_DT_INST_DEFINE(inst, gpio_rza2m_port_init, NULL, &gpio_rza2m_data_##inst, \ &gpio_rza2m_cfg_##inst, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ &gpio_rza2m_driver_api); DT_INST_FOREACH_STATUS_OKAY(GPIO_RZA2M_PORT_INIT) DT_FOREACH_STATUS_OKAY(renesas_rza2m_gpio_int, GPIO_RZA2M_INT_INIT)