From: Biju Das Date: Wed, 1 Apr 2026 11:45:00 +0000 (+0100) Subject: irqchip/renesas-rzg2l: Add NMI support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0e5988549dc73d985cc12c4cb438771ced74f522;p=thirdparty%2Fkernel%2Flinux.git irqchip/renesas-rzg2l: Add NMI support The RZ/G2L SoC has an NMI interrupt. Add support for the NMI interrupt. Signed-off-by: Biju Das Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260401114504.332825-1-biju.das.jz@bp.renesas.com --- diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 199b3c6b02c87..f6b2e69a2f4e0 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -20,11 +20,14 @@ #include #include +#define IRQC_NMI 0 #define IRQC_IRQ_START 1 #define IRQC_TINT_COUNT 32 #define IRQC_SHARED_IRQ_COUNT 8 #define IRQC_IRQ_SHARED_START (IRQC_IRQ_START + IRQC_SHARED_IRQ_COUNT) +#define NSCR 0x0 +#define NITSR 0x4 #define ISCR 0x10 #define IITSR 0x14 #define TSCR 0x20 @@ -43,6 +46,10 @@ #define TSSR_OFFSET(n) ((n) % 4) #define TSSR_INDEX(n) ((n) / 4) +#define NSCR_NSTAT 0 +#define NITSR_NTSEL_EDGE_FALLING 0 +#define NITSR_NTSEL_EDGE_RISING 1 + #define TITSR_TITSEL_EDGE_RISING 0 #define TITSR_TITSEL_EDGE_FALLING 1 #define TITSR_TITSEL_LEVEL_HIGH 2 @@ -63,11 +70,13 @@ /** * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume) - * @iitsr: IITSR register - * @inttsel: INTTSEL register - * @titsr: TITSR registers + * @nitsr: NITSR register + * @iitsr: IITSR register + * @inttsel: INTTSEL register + * @titsr: TITSR registers */ struct rzg2l_irqc_reg_cache { + u32 nitsr; u32 iitsr; u32 inttsel; u32 titsr[2]; @@ -116,6 +125,28 @@ static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data) return data->domain->host_data; } +static void rzg2l_clear_nmi_int(struct rzg2l_irqc_priv *priv) +{ + u32 bit = BIT(NSCR_NSTAT); + u32 reg; + + /* + * No locking required as the register is not shared + * with other interrupts. + * + * Writing is allowed only when NSTAT is 1 + */ + reg = readl_relaxed(priv->base + NSCR); + if (reg & bit) { + writel_relaxed(reg & ~bit, priv->base + NSCR); + /* + * Enforce that the posted write is flushed to prevent that the + * just handled interrupt is raised again. + */ + readl_relaxed(priv->base + NSCR); + } +} + static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq) { unsigned int hw_irq = hwirq - IRQC_IRQ_START; @@ -155,6 +186,14 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir } } +static void rzg2l_irqc_nmi_eoi(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + + rzg2l_clear_nmi_int(priv); + irq_chip_eoi_parent(d); +} + static void rzg2l_irqc_irq_eoi(struct irq_data *d) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); @@ -341,6 +380,26 @@ static void rzg2l_irqc_tint_enable(struct irq_data *d) irq_chip_enable_parent(d); } +static int rzg2l_nmi_set_type(struct irq_data *d, unsigned int type) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + u32 sense; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_FALLING: + sense = NITSR_NTSEL_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_RISING: + sense = NITSR_NTSEL_EDGE_RISING; + break; + default: + return -EINVAL; + } + + writel_relaxed(sense, priv->base + NITSR); + return 0; +} + static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type) { struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); @@ -467,11 +526,23 @@ static int rzg2l_irqc_tint_set_type(struct irq_data *d, unsigned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } +static int rzg2l_irqc_nmi_set_type(struct irq_data *d, unsigned int type) +{ + int ret; + + ret = rzg2l_nmi_set_type(d, type); + if (ret) + return ret; + + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + static int rzg2l_irqc_irq_suspend(void *data) { struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache; void __iomem *base = rzg2l_irqc_data->base; + cache->nitsr = readl_relaxed(base + NITSR); cache->iitsr = readl_relaxed(base + IITSR); if (rzg2l_irqc_data->info.shared_irq_cnt) cache->inttsel = readl_relaxed(base + INTTSEL); @@ -496,6 +567,7 @@ static void rzg2l_irqc_irq_resume(void *data) if (rzg2l_irqc_data->info.shared_irq_cnt) writel_relaxed(cache->inttsel, base + INTTSEL); writel_relaxed(cache->iitsr, base + IITSR); + writel_relaxed(cache->nitsr, base + NITSR); } static const struct syscore_ops rzg2l_irqc_syscore_ops = { @@ -507,6 +579,23 @@ static struct syscore rzg2l_irqc_syscore = { .ops = &rzg2l_irqc_syscore_ops, }; +static const struct irq_chip rzg2l_irqc_nmi_chip = { + .name = "rzg2l-irqc", + .irq_eoi = rzg2l_irqc_nmi_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_disable = irq_chip_disable_parent, + .irq_enable = irq_chip_enable_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_nmi_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + static const struct irq_chip rzg2l_irqc_irq_chip = { .name = "rzg2l-irqc", .irq_eoi = rzg2l_irqc_irq_eoi, @@ -662,7 +751,9 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, * from 16-31 bits. TINT from the pinctrl driver needs to be programmed * in IRQC registers to enable a given gpio pin as interrupt. */ - if (hwirq > priv->info.irq_count) { + if (hwirq == IRQC_NMI) { + chip = &rzg2l_irqc_nmi_chip; + } else if (hwirq > priv->info.irq_count) { tint = TINT_EXTRACT_GPIOINT(hwirq); hwirq = TINT_EXTRACT_HWIRQ(hwirq); chip = priv->tint_chip;