gem5: Add support for gem5's extended GIC mode
gem5's GIC model contains a gem5-specific extension that enables
support for up to 255 CPUs. When enabled, the GIC interprets registers
where a CPU bitmask would normally be present as a CPU ID.
This extensions need to be enabled by setting the OF compatible string
to "gem5,gic".
Kudos to Matt Evans for the original version of this patch.
Change-Id: I1a8776f1adcbfcd97813573050a8595176de6a7e
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Stephan Diestelhorst <stephan.diestelhorst@arm.com>
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
[Ported to 4.4]
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
[gabor.dozsa@arm.com: Ported to 4.9 and merged initialization fixup from 4.4.]
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 651d726..2b06254 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -89,6 +89,8 @@
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
#endif
+ bool probe_gem5_extensions;
+ bool gem5_extensions;
};
#ifdef CONFIG_BL_SWITCHER
@@ -117,8 +119,12 @@
* 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.
+ *
+ * The CPU map normally contains a bitmask that maps interrupts to
+ * CPUs. When gem5 extensions have been enabled, it represents an 8
+ * bit CPU ID instead.
*/
-#define NR_GIC_CPU_IF 8
+#define NR_GIC_CPU_IF 256
static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
@@ -439,16 +445,24 @@
void __iomem *base = gic_data_dist_base(gic);
u32 mask, i;
- for (i = mask = 0; i < 32; i += 4) {
- mask = readl_relaxed(base + GIC_DIST_TARGET + i);
- mask |= mask >> 16;
- mask |= mask >> 8;
- if (mask)
- break;
- }
+ if (!gic->gem5_extensions) {
+ for (i = mask = 0; i < 32; i += 4) {
+ mask = readl_relaxed(base + GIC_DIST_TARGET + i);
+ mask |= mask >> 16;
+ mask |= mask >> 8;
+ if (mask)
+ break;
+ }
- if (!mask && num_possible_cpus() > 1)
- pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
+ if (!mask && num_possible_cpus() > 1)
+ pr_crit("GIC CPU mask not found - kernel will fail "
+ "to boot.\n");
+ } else {
+ /* This isn't a mask, it's an actual CPU number. It always
+ * exists in TARGETR0.
+ */
+ mask = readl_relaxed(base + GIC_DIST_TARGET);
+ }
return mask;
}
@@ -522,9 +536,11 @@
* Clear our mask from the other map entries in case they're
* still undefined.
*/
- for (i = 0; i < NR_GIC_CPU_IF; i++)
- if (i != cpu)
- gic_cpu_map[i] &= ~cpu_mask;
+ if (!gic->gem5_extensions) {
+ for (i = 0; i < NR_GIC_CPU_IF; i++)
+ if (i != cpu)
+ gic_cpu_map[i] &= ~cpu_mask;
+ }
}
gic_cpu_config(dist_base, NULL);
@@ -787,6 +803,16 @@
#endif
#ifdef CONFIG_SMP
+static int gic_cpu_map_initialized(const struct cpumask *mask)
+{
+ int cpu;
+ for_each_cpu(cpu, mask) {
+ if (gic_cpu_map[cpu] == 0xff)
+ return 0;
+ }
+ return 1;
+}
+
static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
{
int cpu;
@@ -800,20 +826,36 @@
}
gic_lock_irqsave(flags);
+ if (gic_data[0].gem5_extensions) {
+ dmb(ishst);
+ if (!gic_cpu_map_initialized(mask)) {
+ /* We don't know the CPU mapping yet, so send
+ * a broadcast */
+ writel_relaxed(0x1 << 24 | irq,
+ gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+ } else {
+ /* We can only send one IPI at once, so have
+ * to loop in here. */
+ for_each_cpu(cpu, mask) {
+ int dest = gic_cpu_map[cpu];
+ writel_relaxed(dest << 16 | irq,
+ gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+ }
+ }
+ dmb(ishst);
+ } else {
+ /* Convert our logical CPU mask into a physical one. */
+ for_each_cpu(cpu, mask)
+ map |= gic_cpu_map[cpu];
+ /*
+ * Ensure that stores to Normal memory are visible to the
+ * other CPUs before they observe us issuing the IPI.
+ */
+ dmb(ishst);
- /* Convert our logical CPU mask into a physical one. */
- for_each_cpu(cpu, mask)
- map |= gic_cpu_map[cpu];
-
- /*
- * Ensure that stores to Normal memory are visible to the
- * other CPUs before they observe us issuing the IPI.
- */
- dmb(ishst);
-
- /* this always happens on GIC0 */
- writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
-
+ /* this always happens on GIC0 */
+ writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+ }
gic_unlock_irqrestore(flags);
}
#endif
@@ -1112,6 +1154,19 @@
gic_set_base_accessor(gic, gic_get_common_base);
}
+ gic->gem5_extensions = false;
+ if (gic->probe_gem5_extensions) {
+#ifndef CONFIG_BL_SWITCHER
+ if (readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x100) {
+ pr_info("GIC: Detected gem5 extensions\n");
+ writel_relaxed(0x200, gic_data_dist_base(gic) + GIC_DIST_CTR);
+ gic->gem5_extensions = true;
+ }
+#else
+ WARN(1, "gem5 GIC extensions aren't supported when using the bL switcher.\n");
+#endif
+ }
+
/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
@@ -1395,6 +1450,9 @@
if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
static_key_slow_dec(&supports_deactivate);
+ gic->probe_gem5_extensions =
+ node && of_device_is_compatible(node, "gem5,gic");
+
ret = __gic_init_bases(gic, -1, &node->fwnode);
if (ret) {
gic_teardown(gic);
@@ -1417,6 +1475,7 @@
gic_cnt++;
return 0;
}
+IRQCHIP_DECLARE(gem5_gic, "gem5,gic", gic_of_init);
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(arm11mp_gic, "arm,arm11mp-gic", gic_of_init);
IRQCHIP_DECLARE(arm1176jzf_dc_gic, "arm,arm1176jzf-devchip-gic", gic_of_init);