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 @@ struct gic_chip_data { #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 @@ static DEFINE_RAW_SPINLOCK(cpu_map_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. + * + * 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 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) 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 @@ static int gic_cpu_init(struct gic_chip_data *gic) * 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 @@ static int gic_pm_init(struct gic_chip_data *gic) #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 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) } 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 @@ static int gic_init_bases(struct gic_chip_data *gic, int irq_start, 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 @@ gic_of_init(struct device_node *node, struct device_node *parent) 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_of_init(struct device_node *node, struct device_node *parent) 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);