]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
irqchip/renesas-rzg2l: Add NMI support
authorBiju Das <biju.das.jz@bp.renesas.com>
Wed, 1 Apr 2026 11:45:00 +0000 (12:45 +0100)
committerThomas Gleixner <tglx@kernel.org>
Wed, 1 Apr 2026 16:41:58 +0000 (18:41 +0200)
The RZ/G2L SoC has an NMI interrupt. Add support for the NMI interrupt.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260401114504.332825-1-biju.das.jz@bp.renesas.com
drivers/irqchip/irq-renesas-rzg2l.c

index 199b3c6b02c87c04db5a7b5798cf2198cc40d53f..f6b2e69a2f4e003e3e2376a3ee3d1c838e7f0a70 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/syscore_ops.h>
 
+#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
 #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
 
 /**
  * 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;