]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
irqchip/renesas-rzt2h: Add software-triggered interrupts support
authorCosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
Wed, 20 May 2026 20:31:16 +0000 (23:31 +0300)
committerThomas Gleixner <tglx@kernel.org>
Wed, 3 Jun 2026 16:27:05 +0000 (18:27 +0200)
The Renesas RZ/T2H ICU supports software-triggerable interrupts.

Add a dedicated rzt2h_icu_intcpu_chip irq_chip which implements
rzt2h_icu_intcpu_set_irqchip_state() to allow injecting these
interrupts.

Request the INTCPU IRQs when IRQ injection is enabled to report them
when they occur.

Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260520203117.1516442-3-cosmin-gabriel.tanislav.xa@renesas.com
drivers/irqchip/irq-renesas-rzt2h.c

index ecb69da55508409f116c84e19027807b7207d1d2..f7813d3d9cb1c5043c0bdd2f926052d993dbf4be 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irqchip.h>
 #include <linux/irqchip/irq-renesas-rzt2h.h>
@@ -40,6 +41,9 @@
        ((n) >= RZT2H_ICU_##type##_START &&                                     \
         (n) <  RZT2H_ICU_##type##_START + RZT2H_ICU_##type##_COUNT)
 
+#define RZT2H_ICU_SWINT                                0x0
+#define RZT2H_ICU_SWINT_IC_MASK(i)             BIT(i)
+
 #define RZT2H_ICU_PORTNF_MD                    0xc
 #define RZT2H_ICU_PORTNF_MDi_MASK(i)           (GENMASK(1, 0) << ((i) * 2))
 #define RZT2H_ICU_PORTNF_MDi_PREP(i, val)      (FIELD_PREP(GENMASK(1, 0), val) << ((i) * 2))
@@ -99,6 +103,12 @@ static inline int rzt2h_icu_irq_to_offset(struct irq_data *d, void __iomem **bas
        } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_S) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, SEI)) {
                *offset = hwirq - RZT2H_ICU_IRQ_S_START;
                *base = priv->base_s;
+       } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS)) {
+               *offset = hwirq - RZT2H_ICU_INTCPU_NS_START;
+               *base = priv->base_ns;
+       } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S)) {
+               *offset = hwirq - RZT2H_ICU_INTCPU_S_START;
+               *base = priv->base_s;
        } else {
                return -EINVAL;
        }
@@ -164,6 +174,28 @@ static int rzt2h_icu_set_type(struct irq_data *d, unsigned int type)
        return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING);
 }
 
+static int rzt2h_icu_intcpu_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which,
+                                             bool state)
+{
+       unsigned int offset;
+       void __iomem *base;
+       int ret;
+
+       if (which != IRQCHIP_STATE_PENDING)
+               return irq_chip_set_parent_state(d, which, state);
+
+       if (!state)
+               return 0;
+
+       ret = rzt2h_icu_irq_to_offset(d, &base, &offset);
+       if (ret)
+               return ret;
+
+       writel_relaxed(RZT2H_ICU_SWINT_IC_MASK(offset), base + RZT2H_ICU_SWINT);
+
+       return 0;
+}
+
 static const struct irq_chip rzt2h_icu_chip = {
        .name                   = "rzt2h-icu",
        .irq_mask               = irq_chip_mask_parent,
@@ -180,10 +212,27 @@ static const struct irq_chip rzt2h_icu_chip = {
                                  IRQCHIP_SKIP_SET_WAKE,
 };
 
+static const struct irq_chip rzt2h_icu_intcpu_chip = {
+       .name                   = "rzt2h-icu",
+       .irq_mask               = irq_chip_mask_parent,
+       .irq_unmask             = irq_chip_unmask_parent,
+       .irq_eoi                = irq_chip_eoi_parent,
+       .irq_set_type           = irq_chip_set_type_parent,
+       .irq_set_wake           = irq_chip_set_wake_parent,
+       .irq_set_affinity       = irq_chip_set_affinity_parent,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
+       .irq_get_irqchip_state  = irq_chip_get_parent_state,
+       .irq_set_irqchip_state  = rzt2h_icu_intcpu_set_irqchip_state,
+       .flags                  = IRQCHIP_MASK_ON_SUSPEND |
+                                 IRQCHIP_SET_TYPE_MASKED |
+                                 IRQCHIP_SKIP_SET_WAKE,
+};
+
 static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs,
                           void *arg)
 {
        struct rzt2h_icu_priv *priv = domain->host_data;
+       const struct irq_chip *chip;
        irq_hw_number_t hwirq;
        unsigned int type;
        int ret;
@@ -192,7 +241,12 @@ static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne
        if (ret)
                return ret;
 
-       ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzt2h_icu_chip, NULL);
+       if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S))
+               chip = &rzt2h_icu_intcpu_chip;
+       else
+               chip = &rzt2h_icu_chip;
+
+       ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, NULL);
        if (ret)
                return ret;
 
@@ -222,6 +276,60 @@ static int rzt2h_icu_parse_interrupts(struct rzt2h_icu_priv *priv, struct device
        return 0;
 }
 
+static irqreturn_t rzt2h_icu_intcpu_irq(int irq, void *data)
+{
+       unsigned int intcpu = (uintptr_t)data;
+
+       pr_info("INTCPU%u software interrupt\n", intcpu);
+       return IRQ_HANDLED;
+}
+
+static int rzt2h_icu_request_irqs(struct platform_device *pdev, struct irq_domain *irq_domain,
+                                 unsigned int start, unsigned int count, irq_handler_t handler,
+                                 void *data)
+{
+       struct device *dev = &pdev->dev;
+       unsigned int offset, virq;
+       struct irq_fwspec fwspec;
+       int ret;
+
+       for (offset = start; offset < start + count; offset++) {
+               fwspec.fwnode = irq_domain->fwnode;
+               fwspec.param_count = 2;
+               fwspec.param[0] = offset;
+               fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+
+               virq = irq_create_fwspec_mapping(&fwspec);
+               if (!virq)
+                       return dev_err_probe(dev, -EINVAL, "Failed to create IRQ %u mapping\n", offset);
+
+               ret = devm_request_irq(dev, virq, handler, 0, dev_name(dev),
+                                      data ?: (void *)(uintptr_t)offset);
+               if (ret)
+                       return dev_err_probe(dev, ret, "Failed to request IRQ %u\n", offset);
+       }
+
+       return 0;
+}
+
+static int rzt2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain)
+{
+       if (IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION)) {
+               int ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_NS_START,
+                                                RZT2H_ICU_INTCPU_NS_COUNT, rzt2h_icu_intcpu_irq,
+                                                NULL);
+               if (ret)
+                       return ret;
+
+               ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_S_START,
+                                            RZT2H_ICU_INTCPU_S_COUNT, rzt2h_icu_intcpu_irq, NULL);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *parent)
 {
        struct irq_domain *irq_domain, *parent_domain;
@@ -265,11 +373,20 @@ static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *pare
        irq_domain = irq_domain_create_hierarchy(parent_domain, 0, RZT2H_ICU_NUM_IRQ,
                                                 dev_fwnode(dev), &rzt2h_icu_domain_ops, priv);
        if (!irq_domain) {
-               pm_runtime_put_sync(dev);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_pm_put;
        }
 
+       ret = rzt2h_icu_setup_irqs(pdev, irq_domain);
+       if (ret)
+               goto err_irq_domain_free;
        return 0;
+
+err_irq_domain_free:
+       irq_domain_remove(irq_domain);
+err_pm_put:
+       pm_runtime_put_sync(dev);
+       return ret;
 }
 
 IRQCHIP_PLATFORM_DRIVER_BEGIN(rzt2h_icu)