[PATCH] irqchip: gic: Add a cpu map for each GIC instance

From: Jon Hunter
Date: Wed Jul 29 2015 - 10:45:05 EST


The gic_init_bases() function initialises an array that stores the mapping
between the GIC and CPUs. This array is a global array that is
unconditionally initialised on every call to gic_init_bases(). Although,
it is not common for there to be more than one GIC instance, there are
some devices that do support nested GIC controllers and gic_init_bases()
can be called more than once.

A 2nd call to gic_init_bases() will clear the previous CPU mapping and
will only setup the mapping again for CPU0. This is because for child GIC
controllers there is most likely only one recipient of the interrupt.

Fix this by moving the CPU mapping array to the GIC chip data structure
so that it is initialised for each GIC instance separately. It is assumed
that the bL switcher code is only interested in the root or primary GIC
instance.

Signed-off-by: Jon Hunter <jonathanh@xxxxxxxxxx>
---
arch/arm/common/bL_switcher.c | 2 +-
drivers/irqchip/irq-gic.c | 41 ++++++++++++++++++++++-------------------
include/linux/irqchip/arm-gic.h | 2 +-
3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
index 37dc0fe1093f..4111d7b36077 100644
--- a/arch/arm/common/bL_switcher.c
+++ b/arch/arm/common/bL_switcher.c
@@ -488,7 +488,7 @@ static int bL_switcher_halve_cpus(void)
cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1);

/* Let's take note of the GIC ID for this CPU */
- gic_id = gic_get_cpu_id(i);
+ gic_id = gic_get_cpu_id(i, 0);
if (gic_id < 0) {
pr_err("%s: bad GIC ID for CPU %d\n", __func__, i);
bL_switcher_restore_cpus();
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index a530d9a9b810..78a7706c607e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -50,6 +50,8 @@

#include "irq-gic-common.h"

+#define NR_GIC_CPU_IF 8
+
union gic_base {
void __iomem *common_base;
void __percpu * __iomem *percpu_base;
@@ -70,18 +72,16 @@ struct gic_chip_data {
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
#endif
+ /*
+ * The GIC mapping of CPU interfaces does not necessarily match
+ * the logical CPU numbering. Let's use a mapping as returned
+ * by the GIC itself.
+ */
+ u8 cpu_map[NR_GIC_CPU_IF];
};

static DEFINE_RAW_SPINLOCK(irq_controller_lock);

-/*
- * The GIC mapping of CPU interfaces does not necessarily match
- * the logical CPU numbering. Let's use a mapping as returned
- * by the GIC itself.
- */
-#define NR_GIC_CPU_IF 8
-static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
-
#ifndef MAX_GIC_NR
#define MAX_GIC_NR 1
#endif
@@ -237,6 +237,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
+ struct gic_chip_data *gic = irq_data_get_irq_chip_data(d);
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
unsigned int cpu, shift = (gic_irq(d) % 4) * 8;
u32 val, mask, bit;
@@ -252,7 +253,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,

raw_spin_lock_irqsave(&irq_controller_lock, flags);
mask = 0xff << shift;
- bit = gic_cpu_map[cpu] << shift;
+ bit = gic->cpu_map[cpu] << shift;
val = readl_relaxed(reg) & ~mask;
writel_relaxed(val | bit, reg);
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
@@ -420,7 +421,7 @@ static void gic_cpu_init(struct gic_chip_data *gic)
*/
BUG_ON(cpu >= NR_GIC_CPU_IF);
cpu_mask = gic_get_cpumask(gic);
- gic_cpu_map[cpu] = cpu_mask;
+ gic->cpu_map[cpu] = cpu_mask;

/*
* Clear our mask from the other map entries in case they're
@@ -428,7 +429,7 @@ static void gic_cpu_init(struct gic_chip_data *gic)
*/
for (i = 0; i < NR_GIC_CPU_IF; i++)
if (i != cpu)
- gic_cpu_map[i] &= ~cpu_mask;
+ gic->cpu_map[i] &= ~cpu_mask;

gic_cpu_config(dist_base, NULL);

@@ -677,7 +678,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)

/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
- map |= gic_cpu_map[cpu];
+ map |= gic->cpu_map[cpu];

/*
* Ensure that stores to Normal memory are visible to the
@@ -722,15 +723,17 @@ void gic_send_sgi(unsigned int cpu_id, unsigned int irq)
* or -1 if the CPU number is too large or the interface ID is
* unknown (more than one bit set).
*/
-int gic_get_cpu_id(unsigned int cpu)
+int gic_get_cpu_id(unsigned int cpu, unsigned int gic_nr)
{
unsigned int cpu_bit;

+ if (gic_nr >= MAX_GIC_NR)
+ return -EINVAL;
if (cpu >= NR_GIC_CPU_IF)
- return -1;
- cpu_bit = gic_cpu_map[cpu];
+ return -EINVAL;
+ cpu_bit = gic_data[gic_nr].cpu_map[cpu];
if (cpu_bit & (cpu_bit - 1))
- return -1;
+ return -EINVAL;
return __ffs(cpu_bit);
}

@@ -759,14 +762,14 @@ void gic_migrate_target(unsigned int new_cpu_id)
return;
gic_irqs = gic_data[gic_nr].gic_irqs;

- cur_cpu_id = __ffs(gic_cpu_map[cpu]);
+ cur_cpu_id = __ffs(gic->cpu_map[cpu]);
cur_target_mask = 0x01010101 << cur_cpu_id;
ror_val = (cur_cpu_id - new_cpu_id) & 31;

raw_spin_lock(&irq_controller_lock);

/* Update the target interface for this logical CPU */
- gic_cpu_map[cpu] = 1 << new_cpu_id;
+ gic->cpu_map[cpu] = 1 << new_cpu_id;

/*
* Find all the peripheral interrupts targetting the current
@@ -981,7 +984,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
* It will be refined as each CPU probes its ID.
*/
for (i = 0; i < NR_GIC_CPU_IF; i++)
- gic_cpu_map[i] = 0xff;
+ gic->cpu_map[i] = 0xff;

/*
* Find out how many interrupts are supported.
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index f52a9024be9a..9a4d30564492 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -111,7 +111,7 @@ static inline void gic_init(unsigned int nr, int start,
int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);

void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
-int gic_get_cpu_id(unsigned int cpu);
+int gic_get_cpu_id(unsigned int cpu, unsigned int gic_nr);
void gic_migrate_target(unsigned int new_cpu_id);
unsigned long gic_get_sgir_physaddr(void);

--
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/