Change the way we deal with GIC SGIs by turning them into proper
IRQs, and calling into the arch code to register the interrupt range
instead of a callback.

Signed-off-by: Marc Zyngier <m...@kernel.org>
---
 drivers/irqchip/irq-gic.c | 113 +++++++++++++++++++++++++-------------
 1 file changed, 74 insertions(+), 39 deletions(-)

diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index e289810e794c..8d146ac28c74 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -47,6 +47,8 @@
 
 #include "irq-gic-common.h"
 
+#define GIC_IRQ_TYPE_SGI       0x561
+
 #ifdef CONFIG_ARM64
 #include <asm/cpufeature.h>
 
@@ -335,31 +337,22 @@ static void __exception_irq_entry gic_handle_irq(struct 
pt_regs *regs)
                irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
                irqnr = irqstat & GICC_IAR_INT_ID_MASK;
 
-               if (likely(irqnr > 15 && irqnr < 1020)) {
-                       if (static_branch_likely(&supports_deactivate_key))
-                               writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-                       isb();
-                       handle_domain_irq(gic->domain, irqnr, regs);
-                       continue;
-               }
-               if (irqnr < 16) {
+               if (unlikely(irqnr >= 1020))
+                       break;
+
+               if (static_branch_likely(&supports_deactivate_key))
                        writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-                       if (static_branch_likely(&supports_deactivate_key))
-                               writel_relaxed(irqstat, cpu_base + 
GIC_CPU_DEACTIVATE);
-#ifdef CONFIG_SMP
-                       /*
-                        * Ensure any shared data written by the CPU sending
-                        * the IPI is read after we've read the ACK register
-                        * on the GIC.
-                        *
-                        * Pairs with the write barrier in gic_raise_softirq
-                        */
+               isb();
+
+               /*
+                * Ensure any shared data written by the CPU sending the IPI
+                * is read after we've read the ACK register on the GIC.
+                *
+                * Pairs with the write barrier in gic_ipi_send_mask
+                */
+               if (irqnr <= 15)
                        smp_rmb();
-                       handle_IPI(irqnr, regs);
-#endif
-                       continue;
-               }
-               break;
+               handle_domain_irq(gic->domain, irqnr, regs);
        } while (1);
 }
 
@@ -801,14 +794,14 @@ static int gic_set_affinity(struct irq_data *d, const 
struct cpumask *mask_val,
        return IRQ_SET_MASK_OK_DONE;
 }
 
-static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
+static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
 {
        int cpu;
        unsigned long flags, map = 0;
 
        if (unlikely(nr_cpu_ids == 1)) {
                /* Only one CPU? let's do a self-IPI... */
-               writel_relaxed(2 << 24 | irq,
+               writel_relaxed(2 << 24 | d->hwirq,
                               gic_data_dist_base(&gic_data[0]) + 
GIC_DIST_SOFTINT);
                return;
        }
@@ -826,7 +819,7 @@ static void gic_raise_softirq(const struct cpumask *mask, 
unsigned int irq)
        dmb(ishst);
 
        /* this always happens on GIC0 */
-       writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + 
GIC_DIST_SOFTINT);
+       writel_relaxed(map << 16 | d->hwirq, gic_data_dist_base(&gic_data[0]) + 
GIC_DIST_SOFTINT);
 
        gic_unlock_irqrestore(flags);
 }
@@ -839,14 +832,40 @@ static int gic_starting_cpu(unsigned int cpu)
 
 static __init void gic_smp_init(void)
 {
-       set_smp_cross_call(gic_raise_softirq);
+       struct irq_fwspec sgi_fwspec = {
+               .fwnode         = gic_data[0].domain->fwnode,
+       };
+       int base_sgi;
+
        cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
                                  "irqchip/arm/gic:starting",
                                  gic_starting_cpu, NULL);
+
+       if (is_of_node(gic_data[0].domain->fwnode)) {
+               /* DT */
+               sgi_fwspec.param_count = 3;
+               sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI;
+               sgi_fwspec.param[1] = 0;
+               sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else {
+               /* ACPI */
+               sgi_fwspec.param_count = 2;
+               sgi_fwspec.param[0] = 0;
+               sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+       }
+
+       base_sgi = __irq_domain_alloc_irqs(gic_data[0].domain, -1, 8,
+                                          NUMA_NO_NODE, &sgi_fwspec,
+                                          false, NULL);
+       if (WARN_ON(base_sgi <= 0))
+               return;
+
+       set_smp_ipi_range(base_sgi, 8);
 }
 #else
 #define gic_smp_init()         do { } while(0)
 #define gic_set_affinity       NULL
+#define gic_ipi_send_mask      NULL
 #endif
 
 #ifdef CONFIG_BL_SWITCHER
@@ -993,16 +1012,26 @@ static int gic_irq_domain_map(struct irq_domain *d, 
unsigned int irq,
 {
        struct gic_chip_data *gic = d->host_data;
 
-       if (hw < 32) {
+       switch (hw) {
+       case 0 ... 15:
+               irq_set_percpu_devid(irq);
+               irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
+                                   handle_percpu_devid_fasteoi_ipi,
+                                   NULL, NULL);
+               irq_set_status_flags(irq, IRQ_NOAUTOEN);
+               break;
+       case 16 ... 31:
                irq_set_percpu_devid(irq);
                irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
                                    handle_percpu_devid_irq, NULL, NULL);
                irq_set_status_flags(irq, IRQ_NOAUTOEN);
-       } else {
+               break;
+       default:
                irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
                                    handle_fasteoi_irq, NULL, NULL);
                irq_set_probe(irq);
                irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
+               break;
        }
        return 0;
 }
@@ -1020,15 +1049,19 @@ static int gic_irq_domain_translate(struct irq_domain 
*d,
                if (fwspec->param_count < 3)
                        return -EINVAL;
 
-               /* Get the interrupt number and add 16 to skip over SGIs */
-               *hwirq = fwspec->param[1] + 16;
-
-               /*
-                * For SPIs, we need to add 16 more to get the GIC irq
-                * ID number
-                */
-               if (!fwspec->param[0])
-                       *hwirq += 16;
+               switch (fwspec->param[0]) {
+               case 0:                 /* SPI */
+                       *hwirq = fwspec->param[1] + 32;
+                       break;
+               case 1:                 /* PPI */
+                       *hwirq = fwspec->param[1] + 16;
+                       break;
+               case GIC_IRQ_TYPE_SGI:  /* SGI, can't be set via DT */
+                       *hwirq = fwspec->param[1];
+                       break;
+               default:
+                       return -EINVAL;
+               }
 
                *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
 
@@ -1097,8 +1130,10 @@ static void gic_init_chip(struct gic_chip_data *gic, 
struct device *dev,
                gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity;
        }
 
-       if (gic == &gic_data[0])
+       if (gic == &gic_data[0]) {
                gic->chip.irq_set_affinity = gic_set_affinity;
+               gic->chip.ipi_send_mask = gic_ipi_send_mask;
+       }
 }
 
 static int gic_init_bases(struct gic_chip_data *gic,
-- 
2.26.2

Reply via email to