[RFC PATCH 7/7] irqchip/apple-aic: add SMP support to the Apple AIC driver.

Mohamed Mediouni mohamed.mediouni at caramail.com
Wed Jan 20 08:27:17 EST 2021


From: Stan Skowronek <stan at corellium.com>

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

Signed-off-by: Stan Skowronek <stan at corellium.com>
Signed-off-by: Mohamed Mediouni <mohamed.mediouni at 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




More information about the linux-arm-kernel mailing list