drivers: intc: renesas: Add gpio interrupt (tint) for RZ family

Add support for gpio interrupt (tint) for Renesas RZ familiy.

Signed-off-by: Nhut Nguyen <nhut.nguyen.kc@renesas.com>
This commit is contained in:
Nhut Nguyen
2025-12-17 14:19:44 +07:00
committed by Fabio Baltieri
parent 0037a5fc4b
commit 982231a8fe
7 changed files with 409 additions and 1 deletions

View File

@@ -60,6 +60,7 @@ zephyr_library_sources_ifdef(CONFIG_NXP_SIUL2_EIRQ intc_nxp_siul2_eirq.c)
zephyr_library_sources_ifdef(CONFIG_PLIC intc_plic.c)
zephyr_library_sources_ifdef(CONFIG_RENESAS_RX_ICU intc_renesas_rx_icu.c)
zephyr_library_sources_ifdef(CONFIG_RENESAS_RZ_EXT_IRQ intc_renesas_rz_ext_irq.c)
zephyr_library_sources_ifdef(CONFIG_RENESAS_RZ_TINT intc_renesas_rz_tint.c)
zephyr_library_sources_ifdef(CONFIG_RV32M1_INTMUX intc_rv32m1_intmux.c)
zephyr_library_sources_ifdef(CONFIG_SAM0_EIC intc_sam0_eic.c)
zephyr_library_sources_ifdef(CONFIG_SHARED_IRQ intc_shared_irq.c)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2024 Renesas Electronics Corporation
# Copyright (c) 2024-2025 Renesas Electronics Corporation
# SPDX-License-Identifier: Apache-2.0
config RENESAS_RZ_EXT_IRQ
@@ -20,3 +20,30 @@ config RENESAS_RZ_INTC_HAS_NMI
Renesas RZ interrupt controller has NMI (Non Maskable Interrupt) pin
endif
config RENESAS_RZ_INTC_SELECT_INTERRUPT
bool "Renesas RZ INTC select interrupt"
default y
depends on DT_HAS_RENESAS_RZ_INTC_V2_ENABLED
help
Renesas RZ INTC select interrupt
config RENESAS_RZ_TINT
bool "Renesas RZ GPIO interrupt (TINT) controller driver"
default y
depends on DT_HAS_RENESAS_RZ_TINT_ENABLED
help
Renesas RZ GPIO interrupt (TINT) controller driver
if RENESAS_RZ_TINT
config RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG
bool "Renesas RZ TINT support status clear register"
default y
depends on DT_HAS_RENESAS_RZ_INTC_V2_ENABLED
help
Renesas RZ/V2H, RZ/V2N support a Status Clear Register
to reset interrupt flags, while the remaining SoCs
reset interrupt flags in the Status Register.
endif

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2025 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rz_tint
#include <zephyr/device.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/interrupt_controller/gic.h>
#include <zephyr/drivers/interrupt_controller/intc_rz_tint.h>
#include <zephyr/dt-bindings/interrupt-controller/arm-gic.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(rz_intc, CONFIG_INTC_LOG_LEVEL);
struct intc_rz_tint_config {
uint8_t tint;
uint8_t max_gpioint;
uint32_t irq;
uint32_t prio;
};
struct intc_rz_tint_data {
uint8_t port;
uint8_t pin;
uint8_t gpioint;
enum intc_rz_tint_trigger trigger_type;
intc_rz_tint_callback_t callback;
void *callback_data;
};
#define RZ_INTC_BASE DT_REG_ADDR(DT_NODELABEL(intc))
#define RZ_INTC_TSCR (RZ_INTC_BASE + DT_REG_ADDR_BY_NAME(DT_NODELABEL(intc), tscr))
#define RZ_INTC_TITSR0 (RZ_INTC_BASE + DT_REG_ADDR_BY_NAME(DT_NODELABEL(intc), titsr0))
#define RZ_INTC_TSSR0 (RZ_INTC_BASE + DT_REG_ADDR_BY_NAME(DT_NODELABEL(intc), tssr0))
#define RZ_INTC_INTSEL (RZ_INTC_BASE + DT_REG_ADDR_BY_NAME(DT_NODELABEL(intc), intsel))
#define REG_TITSR_READ(tint) sys_read32(RZ_INTC_TITSR0 + ((tint) / 16) * 4)
#define REG_TITSR_WRITE(tint, v) sys_write32((v), RZ_INTC_TITSR0 + ((tint) / 16) * 4)
#define REG_TITSR_TITSEL_MASK(tint) (BIT_MASK(2) << ((tint % 16) * 2))
#define REG_TSSR_READ(tint) sys_read32(RZ_INTC_TSSR0 + ((tint) / 4) * 4)
#define REG_TSSR_WRITE(tint, v) sys_write32((v), RZ_INTC_TSSR0 + ((tint) / 4) * 4)
#define REG_TSSR_TSSEL_MASK(tint) (BIT_MASK(7) << ((tint % 4) * 8))
#define REG_TSSR_TIEN_MASK(tint) (BIT(7) << ((tint % 4) * 8))
#if defined(CONFIG_RENESAS_RZ_TINT_SUPPORT_STATUS_CLEAR_REG)
/* V2H, V2N */
#define REG_TSCTR_READ(tint) sys_read32(RZ_INTC_TSCR)
#define REG_TSCLR_WRITE(tint, v) sys_write32((v), RZ_INTC_TSCR + 4)
#define TINT_STATUS_READ(tint) REG_TSCTR_READ(tint)
#define TINT_STATUS_CLEAR(tint) REG_TSCLR_WRITE(tint, TINT_STATUS_READ(tint) | BIT(tint))
#else
#define REG_TSCR_READ(tint) sys_read32(RZ_INTC_TSCR)
#define REG_TSCR_WRITE(tint, v) sys_write32((v), RZ_INTC_TSCR)
#define TINT_STATUS_READ(tint) REG_TSCR_READ(tint)
#define TINT_STATUS_CLEAR(tint) REG_TSCR_WRITE(tint, TINT_STATUS_READ(tint) & ~BIT(tint))
#endif
#define OFFSET(irq) ((irq) - 353 - COND_CODE_1(CONFIG_GIC, (32), (0)))
#define REG_INTSEL_READ(irq) sys_read32(RZ_INTC_INTSEL + (OFFSET(irq) / 3) * 4)
#define REG_INTSEL_WRITE(irq, v) sys_write32((v), RZ_INTC_INTSEL + (OFFSET(irq) / 3) * 4)
#define REG_INTSEL_SPIk_SEL_MASK(irq) (BIT_MASK(10) << ((OFFSET(irq) % 3) * 10))
static const uint8_t gpioint_table[] = DT_PROP(DT_NODELABEL(intc), gpioint_table);
static inline void intc_rz_tint_clear_irq_status(const struct device *dev)
{
const struct intc_rz_tint_config *config = dev->config;
uint8_t tint = config->tint;
TINT_STATUS_CLEAR(tint);
/*
* User's manual: Clear Timing of Interrupt Cause
* Dummy read is required after write
*/
TINT_STATUS_READ(tint);
}
int intc_rz_tint_enable(const struct device *dev)
{
const struct intc_rz_tint_config *config = dev->config;
irq_enable(config->irq);
return 0;
}
int intc_rz_tint_disable(const struct device *dev)
{
const struct intc_rz_tint_config *config = dev->config;
irq_disable(config->irq);
return 0;
}
int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger trig)
{
const struct intc_rz_tint_config *config = dev->config;
struct intc_rz_tint_data *data = dev->data;
uint8_t tint = config->tint;
uint32_t reg_val = REG_TITSR_READ(tint);
uint32_t flags = IRQ_TYPE_LEVEL;
uint32_t set = 0;
switch (trig) {
case RZ_TINT_FAILING_EDGE:
set = 1;
break;
case RZ_TINT_RISING_EDGE:
set = 0;
break;
case RZ_TINT_LOW_LEVEL:
set = 3;
break;
case RZ_TINT_HIGH_LEVEL:
set = 2;
break;
case RZ_TINT_BOTH_EDGE:
default:
return -ENOTSUP;
}
/* Select interrupt type */
reg_val = (reg_val & ~REG_TITSR_TITSEL_MASK(tint)) |
FIELD_PREP(REG_TITSR_TITSEL_MASK(tint), set);
REG_TITSR_WRITE(tint, reg_val);
/*
* User's manual: Precaution when Changing Interrupt Settings
* When changing the TINT interrupt detection method to the edge type,
* write 0 to the TSTATn bit of TSCR.
*/
if ((trig == RZ_TINT_RISING_EDGE) || (trig == RZ_TINT_FAILING_EDGE)) {
flags = IRQ_TYPE_EDGE;
intc_rz_tint_clear_irq_status(dev);
}
/* Set interrupt type for GIC, and clear pending interrupt */
#ifdef CONFIG_GIC
arm_gic_irq_set_priority(config->irq, config->prio, flags);
arm_gic_irq_clear_pending(config->irq);
#else
NVIC_ClearPendingIRQ(config->irq);
#endif
data->trigger_type = trig;
return 0;
}
void intc_rz_tint_isr(const struct device *dev)
{
const struct intc_rz_tint_config *config = dev->config;
struct intc_rz_tint_data *data = dev->data;
intc_rz_tint_clear_irq_status(dev);
/* Clear pending interrupt */
#ifdef CONFIG_GIC
arm_gic_irq_clear_pending(config->irq);
#else
NVIC_ClearPendingIRQ(config->irq);
#endif
if (data->callback) {
data->callback(data->callback_data);
}
}
static int intc_rz_tint_init(const struct device *dev)
{
struct intc_rz_tint_data *data = dev->data;
#if defined(CONFIG_RENESAS_RZ_INTC_SELECT_INTERRUPT)
const struct intc_rz_tint_config *config = dev->config;
uint8_t tint = config->tint;
uint32_t irq = config->irq;
uint32_t reg_val = REG_INTSEL_READ(irq);
reg_val &= ~REG_INTSEL_SPIk_SEL_MASK(irq);
reg_val |= FIELD_PREP(REG_INTSEL_SPIk_SEL_MASK(irq), tint);
REG_INTSEL_WRITE(irq, reg_val);
#endif
return intc_rz_tint_set_type(dev, data->trigger_type);
};
int intc_rz_tint_connect(const struct device *dev, uint8_t port, uint8_t pin)
{
const struct intc_rz_tint_config *config = dev->config;
struct intc_rz_tint_data *data = dev->data;
uint8_t tint = config->tint;
/* Map to GPIOINT */
uint8_t gpioint = gpioint_table[port] + pin;
if (gpioint > config->max_gpioint) {
return -EINVAL;
}
uint32_t reg_val = REG_TSSR_READ(tint);
reg_val &= ~(REG_TSSR_TSSEL_MASK(tint) | REG_TSSR_TIEN_MASK(tint));
reg_val |= FIELD_PREP(REG_TSSR_TSSEL_MASK(tint), gpioint);
reg_val |= FIELD_PREP(REG_TSSR_TIEN_MASK(tint), 1U);
REG_TSSR_WRITE(tint, reg_val);
data->gpioint = gpioint;
data->port = port;
data->pin = pin;
return 0;
}
int intc_rz_tint_set_callback(const struct device *dev, intc_rz_tint_callback_t cb, void *arg)
{
struct intc_rz_tint_data *data = dev->data;
data->callback = cb;
data->callback_data = arg;
return 0;
}
#define TINT_RZ_IRQ_CONNECT(index, isr) \
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(index, 0, irq), DT_INST_IRQ_BY_IDX(index, 0, priority), \
isr, DEVICE_DT_INST_GET(index), \
COND_CODE_1(CONFIG_GIC, (DT_INST_IRQ_BY_IDX(index, 0, flags)), (0)));
#define INTC_RZ_TINT_INIT(index) \
static const struct intc_rz_tint_config intc_rz_tint_config##index = { \
.tint = DT_INST_REG_ADDR(index), \
.irq = DT_INST_IRQ_BY_IDX(index, 0, irq), \
.prio = DT_INST_IRQ_BY_IDX(index, 0, priority), \
.max_gpioint = DT_PROP(DT_INST_PARENT(index), max_gpioint), \
}; \
struct intc_rz_tint_data intc_rz_tint_data##index = { \
.trigger_type = DT_INST_ENUM_IDX_OR(index, trigger_type, 0), \
}; \
static int intc_rz_tint_init##index(const struct device *dev) \
{ \
TINT_RZ_IRQ_CONNECT(index, intc_rz_tint_isr) \
return intc_rz_tint_init(dev); \
}; \
\
DEVICE_DT_INST_DEFINE(index, intc_rz_tint_init##index, NULL, &intc_rz_tint_data##index, \
&intc_rz_tint_config##index, PRE_KERNEL_2, \
CONFIG_INTC_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(INTC_RZ_TINT_INIT)

View File

@@ -0,0 +1,21 @@
# Copyright (c) 2025 Renesas Electronics Corporation
# SPDX-License-Identifier: Apache-2.0
description: Renesas RZ Interrupt Controller
compatible: "renesas,rz-intc-v2"
include: base.yaml
properties:
reg:
required: true
reg-names:
required: true
gpioint-table:
type: array
max-gpioint:
required: true
type: int

View File

@@ -9,3 +9,13 @@ include: base.yaml
properties:
reg:
required: true
reg-names:
required: true
gpioint-table:
type: array
max-gpioint:
required: true
type: int

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2025 Renesas Electronics Corporation
# SPDX-License-Identifier: Apache-2.0
description: Renesas RZ GPIO interrupt (TINT) controller
compatible: "renesas,rz-tint"
include: base.yaml
properties:
trigger-type:
required: true
type: string
description: |
Indicates the condition that will trigger an interrupt when detected.
enum:
- "rising"
- "falling"
- "high_level"
- "low_level"
"#irq-cells":
type: int
const: 1
irq-cells:
- pin

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
enum intc_rz_tint_trigger {
/** Interrupt triggered on falling edge */
RZ_TINT_FAILING_EDGE,
/** Interrupt triggered on rising edge */
RZ_TINT_RISING_EDGE,
/** Interrupt triggered on both edges */
RZ_TINT_BOTH_EDGE,
/** Interrupt triggered on low-level */
RZ_TINT_LOW_LEVEL,
/** Interrupt triggered on high-level */
RZ_TINT_HIGH_LEVEL,
};
/** RZ GPIO interrupt (TINT) callback */
typedef void (*intc_rz_tint_callback_t)(void *arg);
/**
* @brief Connect a TINT channel to a specific GPIO pins
*
* @param dev: pointer to interrupt controller instance
* @param port: GPIO port
* @param pin: GPIO pin
* @return 0 on success, or negative value on error
*/
int intc_rz_tint_connect(const struct device *dev, uint8_t port, uint8_t pin);
/**
* @brief Change trigger interrupt type
*
* @param dev: pointer to interrupt controller instance
* @param trig: trigger type to be changed
* @return 0 on success, or negative value on error
*/
int intc_rz_tint_set_type(const struct device *dev, enum intc_rz_tint_trigger trig);
/**
* @brief Enable TINT interrupt.
*
* @param dev: pointer to interrupt controller instance
* @return 0 on success, or negative value on error
*/
int intc_rz_tint_enable(const struct device *dev);
/**
* @brief Disable TINT interrupt.
*
* @param dev: pointer to interrupt controller instance
* @return 0 on success, or negative value on error
*/
int intc_rz_tint_disable(const struct device *dev);
/**
* @brief Updates the user callback
*
* @param dev: pointer to interrupt controller instance
* @param cb: callback to set
* @param arg: user data passed to callback
* @return 0 on success, or negative value on error
*/
int intc_rz_tint_set_callback(const struct device *dev, intc_rz_tint_callback_t cb, void *arg);