Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

545 lines
17 KiB

/*
* Copyright (c) 2025 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rza2m_gpio
#include <zephyr/drivers/gpio/gpio_utils.h>
#include <zephyr/drivers/interrupt_controller/gic.h>
#include <zephyr/dt-bindings/gpio/renesas-rza2m-gpio.h>
#include <zephyr/logging/log.h>
#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)