From: Stan Skowronek <s...@corellium.com>

This includes IPI support and a workaround for non-working WFI on
Apple processors.

Signed-off-by: Stan Skowronek <s...@corellium.com>
Signed-off-by: Mohamed Mediouni <mohamed.medio...@caramail.com>
---
 drivers/irqchip/irq-apple-aic.c | 177 +++++++++++++++++++++++++++++---
 1 file changed, 165 insertions(+), 12 deletions(-)

diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c
index c601bc4b501a..ce4e39d56fcf 100644
--- a/drivers/irqchip/irq-apple-aic.c
+++ b/drivers/irqchip/irq-apple-aic.c
@@ -17,6 +17,7 @@

 #include <asm/exception.h>
 #include <asm/irq.h>
+#include <asm/smp.h>

 #define REG_ID_REVISION 0x0000
 #define REG_ID_CONFIG 0x0004
@@ -53,12 +54,17 @@
 #define REG_PERCPU(r, c)                                                       
\
        ((r) + REG_CPU_REGION - REG_CPU_LOCAL + ((c) << REG_CPU_SHIFT))

+#define NUM_IPI 8
+
 static struct aic_chip_data {
        void __iomem *base;
        struct irq_domain *domain;
        unsigned int num_irqs;
+       bool fast_ipi;
 } aic;

+static DEFINE_PER_CPU(atomic_t, aic_ipi_mask);
+
 static void apple_aic_irq_mask(struct irq_data *d)
 {
        writel(REG_IRQ_xABLE_MASK(d->hwirq),
@@ -78,18 +84,71 @@ static struct irq_chip apple_aic_irq_chip = {
        .irq_unmask = apple_aic_irq_unmask,
 };

-static void apple_aic_fiq_mask(struct irq_data *d)
+static void apple_aic_fiq_ipi_mask(struct irq_data *d)
 {
 }

-static void apple_aic_fiq_unmask(struct irq_data *d)
+static void apple_aic_fiq_ipi_unmask(struct irq_data *d)
 {
 }

 static struct irq_chip apple_aic_irq_chip_fiq = {
        .name = "apple_aic_fiq",
-       .irq_mask = apple_aic_fiq_mask,
-       .irq_unmask = apple_aic_fiq_unmask,
+       .irq_mask = apple_aic_fiq_ipi_mask,
+       .irq_unmask = apple_aic_fiq_ipi_unmask,
+};
+
+#define SR_APPLE_IPI_LOCAL s3_5_c15_c0_0
+#define SR_APPLE_IPI_REMOTE s3_5_c15_c0_1
+#define SR_APPLE_IPI_STAT s3_5_c15_c1_1
+
+#ifdef CONFIG_SMP
+static void apple_aic_ipi_send_mask(struct irq_data *d,
+                                   const struct cpumask *mask)
+{
+       int cpu, lcpu;
+       int irqnr = d->hwirq - (aic.num_irqs + 2);
+
+       if (WARN_ON(irqnr < 0 || irqnr >= NUM_IPI))
+               return;
+
+       /*
+     * Ensure that stores to Normal memory are visible to the
+     * other CPUs before issuing the IPI.
+     */
+       wmb();
+
+       for_each_cpu (cpu, mask) {
+               smp_mb__before_atomic();
+               atomic_or(1u << irqnr, per_cpu_ptr(&aic_ipi_mask, cpu));
+               smp_mb__after_atomic();
+               lcpu = get_cpu();
+               if (aic.fast_ipi) {
+                       if ((lcpu >> 2) == (cpu >> 2))
+                               write_sysreg(cpu & 3, SR_APPLE_IPI_LOCAL);
+                       else
+                               write_sysreg((cpu & 3) | ((cpu >> 2) << 16),
+                                            SR_APPLE_IPI_REMOTE);
+               } else
+                       writel(lcpu == cpu ? REG_IPI_FLAG_SELF :
+                                                  (REG_IPI_FLAG_OTHER << cpu),
+                              aic.base + REG_IPI_SET);
+               put_cpu();
+       }
+
+       /* Force the above writes to be executed */
+       if (aic.fast_ipi)
+               isb();
+}
+#else
+#define apple_aic_ipi_send_mask NULL
+#endif
+
+static struct irq_chip apple_aic_irq_chip_ipi = {
+       .name = "apple_aic_ipi",
+       .irq_mask = apple_aic_fiq_ipi_mask,
+       .irq_unmask = apple_aic_fiq_ipi_unmask,
+       .ipi_send_mask = apple_aic_ipi_send_mask,
 };

 static int apple_aic_irq_domain_xlate(struct irq_domain *d,
@@ -98,16 +157,27 @@ static int apple_aic_irq_domain_xlate(struct irq_domain *d,
                                      unsigned long *out_hwirq,
                                      unsigned int *out_type)
 {
-       if (intspec[0]) { /* FIQ */
+       switch (intspec[0]) {
+       case 0: /* IRQ */
+               if (intspec[1] >= aic.num_irqs)
+                       return -EINVAL;
+               if (out_hwirq)
+                       *out_hwirq = intspec[1];
+               break;
+       case 1: /* FIQ */
                if (intspec[1] >= 2)
                        return -EINVAL;
                if (out_hwirq)
                        *out_hwirq = aic.num_irqs + intspec[1];
-       } else {
-               if (intspec[1] >= aic.num_irqs)
+               break;
+       case 2: /* IPI */
+               if (intspec[1] >= NUM_IPI)
                        return -EINVAL;
                if (out_hwirq)
-                       *out_hwirq = intspec[1];
+                       *out_hwirq = aic.num_irqs + 2 + intspec[1];
+               break;
+       default:
+               return -EINVAL;
        }

        if (out_type)
@@ -118,7 +188,13 @@ static int apple_aic_irq_domain_xlate(struct irq_domain *d,
 static int apple_aic_irq_domain_map(struct irq_domain *d, unsigned int virq,
                                    irq_hw_number_t hw)
 {
-       if (hw >= aic.num_irqs) {
+       if (hw >= aic.num_irqs + 2) {
+               irq_set_percpu_devid(virq);
+               irq_domain_set_info(d, virq, hw, &apple_aic_irq_chip_ipi,
+                                   d->host_data, handle_percpu_devid_irq, NULL,
+                                   NULL);
+               irq_set_status_flags(virq, IRQ_NOAUTOEN);
+       } else if (hw >= aic.num_irqs) {
                irq_set_percpu_devid(virq);
                irq_domain_set_info(d, virq, hw, &apple_aic_irq_chip_fiq,
                                    d->host_data, handle_percpu_devid_irq, NULL,
@@ -141,8 +217,10 @@ static const struct irq_domain_ops 
apple_aic_irq_domain_ops = {

 static void __exception_irq_entry apple_aic_handle_irq(struct pt_regs *regs)
 {
+       atomic_t *maskptr;
        uint32_t ack;
-       unsigned done = 0;
+       unsigned done = 0, irqnr;
+       unsigned long mask;

        while (1) {
                ack = readl(aic.base + REG_IRQ_ACK);
@@ -154,6 +232,36 @@ static void __exception_irq_entry 
apple_aic_handle_irq(struct pt_regs *regs)
                        handle_domain_irq(aic.domain,
                                          ack & REG_IRQ_ACK_NUM_MASK, regs);
                        break;
+               case REG_IRQ_ACK_TYPE_IPI:
+#ifdef CONFIG_SMP
+                       if (ack == REG_IRQ_ACK_IPI_SELF)
+                               writel(REG_IPI_FLAG_SELF,
+                                      aic.base + REG_IPI_CLEAR);
+                       else
+                               writel(REG_IPI_FLAG_OTHER,
+                                      aic.base + REG_IPI_CLEAR);
+                       maskptr = get_cpu_ptr(&aic_ipi_mask);
+                       smp_mb__before_atomic();
+                       mask = atomic_xchg(maskptr, 0);
+                       smp_mb__after_atomic();
+                       put_cpu_ptr(&aic_ipi_mask);
+                       for_each_set_bit (irqnr, &mask, NUM_IPI) {
+                               handle_domain_irq(aic.domain,
+                                                 aic.num_irqs + 2 + irqnr,
+                                                 regs);
+                       }
+                       if (ack == REG_IRQ_ACK_IPI_SELF)
+                               writel(REG_IPI_FLAG_SELF,
+                                      aic.base +
+                                              REG_PERCPU(REG_IPI_ENABLE,
+                                                         
__smp_processor_id()));
+                       else
+                               writel(REG_IPI_FLAG_OTHER,
+                                      aic.base +
+                                              REG_PERCPU(REG_IPI_ENABLE,
+                                                         
__smp_processor_id()));
+#endif
+                       break;
                }
                if (done)
                        break;
@@ -162,6 +270,27 @@ static void __exception_irq_entry 
apple_aic_handle_irq(struct pt_regs *regs)

 static void __exception_irq_entry apple_aic_handle_fiq(struct pt_regs *regs)
 {
+#ifdef CONFIG_SMP
+       atomic_t *maskptr;
+       unsigned long mask;
+       unsigned irqnr;
+
+       if (aic.fast_ipi) {
+               if (read_sysreg(SR_APPLE_IPI_STAT)) {
+                       write_sysreg(1, SR_APPLE_IPI_STAT);
+
+                       maskptr = get_cpu_ptr(&aic_ipi_mask);
+                       smp_mb__before_atomic();
+                       mask = atomic_xchg(maskptr, 0);
+                       smp_mb__after_atomic();
+                       put_cpu_ptr(&aic_ipi_mask);
+                       for_each_set_bit (irqnr, &mask, NUM_IPI)
+                               handle_domain_irq(aic.domain,
+                                                 aic.num_irqs + 2 + irqnr,
+                                                 regs);
+               }
+       }
+#endif
        handle_domain_irq(aic.domain, aic.num_irqs, regs);
 }

@@ -169,6 +298,13 @@ void apple_aic_cpu_prepare(unsigned int cpu)
 {
        unsigned i;

+       if (aic.fast_ipi)
+               writel(REG_IPI_FLAG_SELF | REG_IPI_FLAG_OTHER,
+                      aic.base + REG_PERCPU(REG_IPI_DISABLE, cpu));
+       else
+               writel(REG_IPI_FLAG_SELF | REG_IPI_FLAG_OTHER,
+                      aic.base + REG_PERCPU(REG_IPI_ENABLE, cpu));
+
        for (i = 0; i < aic.num_irqs; i++)
                writel(readl(aic.base + REG_IRQ_AFFINITY(i)) | (1u << cpu),
                       aic.base + REG_IRQ_AFFINITY(i));
@@ -178,6 +314,9 @@ static int __init apple_aic_init(struct device_node *node,
                                 struct device_node *interrupt_parent)
 {
        unsigned i;
+#ifdef CONFIG_SMP
+       int base_ipi, ret;
+#endif

        if (!node)
                return -ENODEV;
@@ -186,8 +325,11 @@ static int __init apple_aic_init(struct device_node *node,
        if (WARN(!aic.base, "unable to map aic registers\n"))
                return -EINVAL;

+       aic.fast_ipi = of_property_read_bool(node, "fast-ipi");
+
        aic.num_irqs = readl(aic.base + REG_ID_CONFIG) & 0xFFFF;
-       pr_info("Apple AIC: %d IRQs + 1 FIQ + 1 dummy\n", aic.num_irqs);
+       pr_info("Apple AIC: %d IRQs + 1 FIQ + 1 dummy + %d IPIs%s\n",
+               aic.num_irqs, NUM_IPI, aic.fast_ipi ? " (fast)" : "");

        for (i = 0; i < aic.num_irqs; i++)
                writel(1, aic.base + REG_IRQ_AFFINITY(i));
@@ -201,10 +343,21 @@ static int __init apple_aic_init(struct device_node *node,

        apple_aic_cpu_prepare(0);

-       aic.domain = irq_domain_add_linear(node, aic.num_irqs + 2,
+       aic.domain = irq_domain_add_linear(node, aic.num_irqs + 2 + NUM_IPI,
                                           &apple_aic_irq_domain_ops,
                                           &apple_aic_irq_chip);
        irq_set_default_host(aic.domain);
+
+#ifdef CONFIG_SMP
+       base_ipi = aic.num_irqs + 2;
+       ret = irq_create_strict_mappings(aic.domain, base_ipi, aic.num_irqs + 2,
+                                        NUM_IPI);
+       if (ret < 0)
+               pr_err("%s: irq_create_strict_mappings failed with %d\n",
+                      __func__, ret);
+       set_smp_ipi_range(base_ipi, NUM_IPI);
+#endif
+
        return 0;
 }

--
2.29.2

Reply via email to