All of lore.kernel.org
 help / color / mirror / Atom feed
From: marc.zyngier@arm.com (Marc Zyngier)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v8 01/14] ARM: gic: add per-cpu interrupt mapping
Date: Tue,  5 Jul 2011 09:49:02 +0100	[thread overview]
Message-ID: <1309855755-6261-2-git-send-email-marc.zyngier@arm.com> (raw)
In-Reply-To: <1309855755-6261-1-git-send-email-marc.zyngier@arm.com>

The kernel doesn't handle very well the concept of per-cpu interrupt
as implemented by the ARM architecture (the same interrupt level is
exposed on each core).

To work around the problem, add another irq_chip to handle PPIs and
remap them so that a single interrupt number is only used on a given
CPU (for example, IRQ 29 and 30 get exposed as IRQ 128 and 129 on
core 0, 130 and 131 on core 1...).

A helper function gic_ppi_map() is used to convert the PPI number
to the per-processor IRQ.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Will Deacon <will.deacon@arm.com>
Tested-by: Stephen Boyd <sboyd@codeaurora.org>
---
 arch/arm/common/Kconfig                         |    5 +
 arch/arm/common/gic.c                           |  137 ++++++++++++++++++++++-
 arch/arm/include/asm/entry-macro-multi.S        |    2 +-
 arch/arm/include/asm/hardware/entry-macro-gic.S |   19 ++-
 arch/arm/include/asm/hardware/gic.h             |   11 ++
 arch/arm/kernel/irq.c                           |    8 +-
 6 files changed, 169 insertions(+), 13 deletions(-)

diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 4b71766..daf6272 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -1,6 +1,11 @@
 config ARM_GIC
 	bool
 
+config ARM_GIC_PPI_MAP
+	depends on ARM_GIC
+	select SPARSE_IRQ
+	bool
+
 config ARM_VIC
 	bool
 
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 4ddd0a6..e0ad9f0 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -42,6 +42,12 @@ struct gic_chip_data {
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+	/* These fields must be 0 on secondary GICs */
+	int	     ppi_base;
+	int	     vppi_base;
+	u16	     nrppis;
+#endif
 };
 
 /*
@@ -262,12 +268,92 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	irq_set_chained_handler(irq, gic_handle_cascade_irq);
 }
 
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+unsigned int gic_ppi_map(unsigned int irq)
+{
+	struct gic_chip_data *chip_data = irq_get_chip_data(irq);
+	unsigned int vppi_irq;
+	unsigned int ppi;
+
+	WARN_ON(!chip_data->vppi_base);
+
+	ppi = irq - chip_data->ppi_base;
+	vppi_irq = ppi + chip_data->nrppis * smp_processor_id();
+	vppi_irq += chip_data->vppi_base;
+
+	return vppi_irq;
+}
+
+static void gic_handle_ppi(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned int vppi_irq;
+
+	vppi_irq = gic_ppi_map(irq);
+	generic_handle_irq(vppi_irq);
+}
+
+static struct irq_data *gic_vppi_to_ppi(struct irq_data *d)
+{
+	struct gic_chip_data *chip_data = irq_data_get_irq_chip_data(d);
+	unsigned int ppi_irq;
+
+	ppi_irq = d->irq - chip_data->vppi_base - chip_data->nrppis * smp_processor_id();
+	ppi_irq += chip_data->ppi_base;
+
+	return irq_get_irq_data(ppi_irq);
+}
+
+static void gic_ppi_eoi_irq(struct irq_data *d)
+{
+	gic_eoi_irq(gic_vppi_to_ppi(d));
+}
+
+static void gic_ppi_mask_irq(struct irq_data *d)
+{
+	gic_mask_irq(gic_vppi_to_ppi(d));
+}
+
+static void gic_ppi_unmask_irq(struct irq_data *d)
+{
+	gic_unmask_irq(gic_vppi_to_ppi(d));
+}
+
+static int gic_ppi_set_type(struct irq_data *d, unsigned int type)
+{
+	return gic_set_type(gic_vppi_to_ppi(d), type);
+}
+
+#ifdef CONFIG_PM
+static int gic_ppi_set_wake(struct irq_data *d, unsigned int on)
+{
+	return gic_set_wake(gic_vppi_to_ppi(d), on);
+}
+#else
+#define gic_ppi_set_wake	NULL
+#endif
+
+static int __init gic_irq_is_ppi(struct gic_chip_data *gic, unsigned int irq)
+{
+	return (irq >= (gic->irq_offset + 16) && irq <= (gic->irq_offset + 31));
+}
+
+static struct irq_chip gic_ppi_chip = {
+	.name			= "GIC-PPI",
+	.irq_eoi		= gic_ppi_eoi_irq,
+	.irq_mask		= gic_ppi_mask_irq,
+	.irq_unmask		= gic_ppi_unmask_irq,
+	.irq_set_type		= gic_ppi_set_type,
+	.irq_set_wake		= gic_ppi_set_wake,
+};
+#endif
+
 static void __init gic_dist_init(struct gic_chip_data *gic,
 	unsigned int irq_start)
 {
-	unsigned int gic_irqs, irq_limit, i;
+	unsigned int gic_irqs, irq_limit, i, nrvppis = 0;
 	void __iomem *base = gic->dist_base;
 	u32 cpumask = 1 << smp_processor_id();
+	u32 dist_ctr, nrcpus;
 
 	cpumask |= cpumask << 8;
 	cpumask |= cpumask << 16;
@@ -278,11 +364,32 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	 * Find out how many interrupts are supported.
 	 * The GIC only supports up to 1020 interrupt sources.
 	 */
-	gic_irqs = readl_relaxed(base + GIC_DIST_CTR) & 0x1f;
-	gic_irqs = (gic_irqs + 1) * 32;
+	dist_ctr = readl_relaxed(base + GIC_DIST_CTR);
+	gic_irqs = ((dist_ctr & 0x1f) + 1) * 32;
 	if (gic_irqs > 1020)
 		gic_irqs = 1020;
 
+	/* Find out how many CPUs are supported (8 max). */
+	nrcpus = ((dist_ctr >> 5) & 7) + 1;
+
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+	/*
+	 * Nobody would be insane enough to use PPIs on a secondary
+	 * GIC, right?
+	 */
+	if (gic == &gic_data[0]) {
+		gic->nrppis = 16 - (irq_start % 16);
+		gic->ppi_base = gic->irq_offset + 32 - gic->nrppis;
+		nrvppis = gic->nrppis * nrcpus;
+	} else {
+		gic->ppi_base = 0;
+		gic->vppi_base = 0;
+	}
+#endif
+
+	pr_info("Configuring GIC with %d sources (%d additional PPIs)\n",
+		gic_irqs, nrvppis);
+
 	/*
 	 * Set all global interrupts to be level triggered, active low.
 	 */
@@ -319,10 +426,32 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	 * Setup the Linux IRQ subsystem.
 	 */
 	for (i = irq_start; i < irq_limit; i++) {
-		irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq);
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+		if (nrvppis && gic_irq_is_ppi(gic, i))
+			irq_set_chip_and_handler(i, &gic_chip, gic_handle_ppi);
+		else
+#endif
+		{
+			irq_set_chip_and_handler(i, &gic_chip,
+						 handle_fasteoi_irq);
+			set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+		}
+		irq_set_chip_data(i, gic);
+	}
+
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+	if (!nrvppis)
+		goto out;
+	gic->vppi_base = irq_alloc_descs(-1, 0, nrvppis, 0);
+	if (WARN_ON(gic->vppi_base < 0))
+		goto out;
+	for (i = gic->vppi_base; i < (gic->vppi_base + nrvppis); i++) {
+		irq_set_chip_and_handler(i, &gic_ppi_chip, handle_percpu_irq);
 		irq_set_chip_data(i, gic);
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
 	}
+out:
+#endif
 
 	writel_relaxed(1, base + GIC_DIST_CTRL);
 }
diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S
index 2da8547..c3f9db4 100644
--- a/arch/arm/include/asm/entry-macro-multi.S
+++ b/arch/arm/include/asm/entry-macro-multi.S
@@ -26,7 +26,7 @@
 	adrne	lr, BSYM(1b)
 	bne	do_IPI
 
-#ifdef CONFIG_LOCAL_TIMERS
+#if defined(CONFIG_LOCAL_TIMERS) && !defined(CONFIG_ARM_GIC_PPI_MAP)
 	test_for_ltirq r0, r6, r5, lr
 	movne	r0, sp
 	adrne	lr, BSYM(1b)
diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S
index c115b82..cf8895f 100644
--- a/arch/arm/include/asm/hardware/entry-macro-gic.S
+++ b/arch/arm/include/asm/hardware/entry-macro-gic.S
@@ -17,23 +17,26 @@
 	.endm
 #endif
 
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+#define DO_IRQ_BASE	16
+#else
+#define DO_IRQ_BASE	30
+#endif
+
 /*
  * The interrupt numbering scheme is defined in the
  * interrupt controller spec.  To wit:
  *
  * Interrupts 0-15 are IPI
- * 16-28 are reserved
- * 29-31 are local.  We allow 30 to be used for the watchdog.
+ * 16-31 are local.  We allow 30 to be used for the watchdog.
  * 32-1020 are global
  * 1021-1022 are reserved
  * 1023 is "spurious" (no interrupt)
  *
- * For now, we ignore all local interrupts so only return an interrupt if it's
- * between 30 and 1020.  The test_for_ipi routine below will pick up on IPIs.
- *
  * A simple read from the controller will tell us the number of the highest
  * priority enabled interrupt.  We then just need to check whether it is in the
- * valid range for an IRQ (30-1020 inclusive).
+ * valid range for an IRQ (30-1020 inclusive). If CONFIG_ARM_GIC_PPI_MAP is
+ * enabled, local interrupts are handled the same way as global ones.
  */
 
 	.macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
@@ -43,7 +46,7 @@
 
 	ldr	\tmp, =1021
 	bic     \irqnr, \irqstat, #0x1c00
-	cmp     \irqnr, #29
+	cmp     \irqnr, #(DO_IRQ_BASE - 1)
 	cmpcc	\irqnr, \irqnr
 	cmpne	\irqnr, \tmp
 	cmpcs	\irqnr, \irqnr
@@ -63,6 +66,7 @@
 	cmpcs	\irqnr, \irqnr
 	.endm
 
+#ifndef CONFIG_ARM_GIC_PPI_MAP
 /* As above, this assumes that irqstat and base are preserved.. */
 
 	.macro test_for_ltirq, irqnr, irqstat, base, tmp
@@ -73,3 +77,4 @@
 	streq	\irqstat, [\base, #GIC_CPU_EOI]
 	cmp	\tmp, #0
 	.endm
+#endif
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 0691f9d..cdaff9d 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -33,6 +33,8 @@
 #define GIC_DIST_SOFTINT		0xf00
 
 #ifndef __ASSEMBLY__
+#include <linux/cpumask.h>
+
 extern void __iomem *gic_cpu_base_addr;
 extern struct irq_chip gic_arch_extn;
 
@@ -41,6 +43,15 @@ void gic_secondary_init(unsigned int);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+unsigned int gic_ppi_map(unsigned int irq);
+#else
+static inline unsigned int gic_ppi_map(unsigned int irq)
+{
+	return irq;
+}
+#endif
+
 #endif
 
 #endif
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 83bbad0..a033d75 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -124,8 +124,14 @@ void __init init_IRQ(void)
 #ifdef CONFIG_SPARSE_IRQ
 int __init arch_probe_nr_irqs(void)
 {
+	int initcnt;
+
 	nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
-	return nr_irqs;
+	initcnt = nr_irqs;
+#ifdef CONFIG_ARM_GIC_PPI_MAP
+	nr_irqs += 16 * 8;	/* 16 PPIs, 8 CPUs */
+#endif
+	return initcnt;
 }
 #endif
 
-- 
1.7.0.4

  reply	other threads:[~2011-07-05  8:49 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-05  8:49 [PATCH v8 00/14] Consolidating GIC per-cpu interrupts Marc Zyngier
2011-07-05  8:49 ` Marc Zyngier [this message]
2011-07-05  8:49 ` [PATCH v8 02/14] ARM: smp: add interrupt handler for local timers Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 03/14] ARM: smp_twd: add support for remapped PPI interrupts Marc Zyngier
2011-07-08 19:27   ` Russell King - ARM Linux
2011-07-10 15:13     ` Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 04/14] ARM: omap4: use remapped PPI interrupts for local timer Marc Zyngier
2011-07-07 14:32   ` Tony Lindgren
2011-07-05  8:49 ` [PATCH v8 05/14] ARM: versatile: " Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 06/14] ARM: shmobile: " Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 07/14] ARM: ux500: " Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 08/14] ARM: tegra: " Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 09/14] ARM: msm: " Marc Zyngier
2011-07-05 18:06   ` David Brown
2011-07-05  8:49 ` [PATCH v8 10/14] ARM: exynos4: " Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 11/14] ARM: gic: remove previous local timer interrupt handling Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 12/14] ARM: gic: add compute_irqnr macro for exynos4 Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 13/14] ARM: SMP: automatically select ARM_GIC_PPI_MAP Marc Zyngier
2011-07-05  8:49 ` [PATCH v8 14/14] ARM: gic: add gic_ppi_map_on_cpu() Marc Zyngier
2011-07-08 19:57   ` Russell King - ARM Linux
2011-07-10 15:10     ` Marc Zyngier
2011-07-10 15:37       ` Russell King - ARM Linux
2011-07-10 18:30         ` Russell King - ARM Linux
2011-07-11  9:52           ` Marc Zyngier
2011-07-11 10:17             ` Russell King - ARM Linux
2011-07-11 11:14               ` Marc Zyngier
2011-07-11 11:38                 ` Russell King - ARM Linux
2011-07-11 12:36                   ` Marc Zyngier
2011-07-06  5:46 ` [PATCH v8 00/14] Consolidating GIC per-cpu interrupts Shawn Guo
2011-07-06  9:53   ` Marc Zyngier
2011-07-06 11:02 ` Marc Zyngier
2011-07-06 11:11   ` Russell King - ARM Linux
2011-07-06 11:18     ` Marc Zyngier
2011-07-06 12:26     ` Arnd Bergmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1309855755-6261-2-git-send-email-marc.zyngier@arm.com \
    --to=marc.zyngier@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.