From f86c4fbd930ff6fecf3d8a1c313182bd0f49f496 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 26 Apr 2016 12:00:00 +0100 Subject: irqchip/gic: Ensure ordering between read of INTACK and shared data When an IPI is generated by a CPU, the pattern looks roughly like: smp_wmb(); On the receiving CPU we rely on the fact that, once we've taken the interrupt, then the freshly written shared data must be visible to us. Put another way, the CPU isn't going to speculate taking an interrupt. Unfortunately, this assumption turns out to be broken. Consider that CPUx wants to send an IPI to CPUy, which will cause CPUy to read some shared_data. Before CPUx has done anything, a random peripheral raises an IRQ to the GIC and the IRQ line on CPUy is raised. CPUy then takes the IRQ and starts executing the entry code, heading towards gic_handle_irq. Furthermore, let's assume that a bunch of the previous interrupts handled by CPUy were SGIs, so the branch predictor kicks in and speculates that irqnr will be <16 and we're likely to head into handle_IPI. The prefetcher then grabs a speculative copy of shared_data which contains a stale value. Meanwhile, CPUx gets round to updating shared_data and asking the GIC to send an SGI to CPUy. Internally, the GIC decides that the SGI is more important than the peripheral interrupt (which hasn't yet been ACKed) but doesn't need to do anything to CPUy, because the IRQ line is already raised. CPUy then reads the ACK register on the GIC, sees the SGI value which confirms the branch prediction and we end up with a stale shared_data value. This patch fixes the problem by adding an smp_rmb() to the IPI entry code in gic_handle_irq. As it turns out, the combination of a control dependency and an ISB instruction from the EOI in the GICv3 driver is enough to provide the ordering we need, so we add a comment there justifying the absence of an explicit smp_rmb(). Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 282344b95ec2..5c4da5808b15 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -344,6 +344,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE); #ifdef CONFIG_SMP + /* + * Ensure any shared data written by the CPU sending + * the IPI is read after we've read the ACK register + * on the GIC. + * + * Pairs with the write barrier in gic_raise_softirq + */ + smp_rmb(); handle_IPI(irqnr, regs); #endif continue; -- cgit From 26acfe7463dc7162e595a8f7bd7ef3e06111e25d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:40 +0100 Subject: irqchip/gic: Don't initialise chip if mapping IO space fails If we fail to map the address space for the GIC distributor or CPU interface, then don't attempt to initialise the chip, just WARN and return. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 5c4da5808b15..612a3584a282 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1209,10 +1209,14 @@ gic_of_init(struct device_node *node, struct device_node *parent) return -ENODEV; dist_base = of_iomap(node, 0); - WARN(!dist_base, "unable to map gic dist registers\n"); + if (WARN(!dist_base, "unable to map gic dist registers\n")) + return -ENOMEM; cpu_base = of_iomap(node, 1); - WARN(!cpu_base, "unable to map gic cpu registers\n"); + if (WARN(!cpu_base, "unable to map gic cpu registers\n")) { + iounmap(dist_base); + return -ENOMEM; + } /* * Disable split EOI/Deactivate if either HYP is not available -- cgit From c2baa2f3f42fc6ea302bb666889c5843986a19a3 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:41 +0100 Subject: irqchip/gic: Remove static irq_chip definition for eoimode1 There are only 3 differences (not including the name) in the definitions of the gic_chip and gic_eoimode1_chip structures. Instead of statically defining the gic_eoimode1_chip structure, remove it and populate the eoimode1 functions dynamically for the appropriate GIC irqchips. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 612a3584a282..a8cb86bea544 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -399,20 +399,6 @@ static struct irq_chip gic_chip = { IRQCHIP_MASK_ON_SUSPEND, }; -static struct irq_chip gic_eoimode1_chip = { - .name = "GICv2", - .irq_mask = gic_eoimode1_mask_irq, - .irq_unmask = gic_unmask_irq, - .irq_eoi = gic_eoimode1_eoi_irq, - .irq_set_type = gic_set_type, - .irq_get_irqchip_state = gic_irq_get_irqchip_state, - .irq_set_irqchip_state = gic_irq_set_irqchip_state, - .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, - .flags = IRQCHIP_SET_TYPE_MASKED | - IRQCHIP_SKIP_SET_WAKE | - IRQCHIP_MASK_ON_SUSPEND, -}; - void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) { BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); @@ -1034,10 +1020,14 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic = &gic_data[gic_nr]; /* Initialize irq_chip */ + gic->chip = gic_chip; + if (static_key_true(&supports_deactivate) && gic_nr == 0) { - gic->chip = gic_eoimode1_chip; + gic->chip.irq_mask = gic_eoimode1_mask_irq; + gic->chip.irq_eoi = gic_eoimode1_eoi_irq; + gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; + gic->chip.name = "GICv2"; } else { - gic->chip = gic_chip; gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } -- cgit From dc9722cc57eba336038b2ade111436656c5a87d0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:42 +0100 Subject: irqchip/gic: Return an error if GIC initialisation fails If the GIC initialisation fails, then currently we do not return an error or clean-up afterwards. Although for root controllers, this failure may be fatal anyway, for secondary controllers, it may not be fatal and so return an error on failure and clean-up. Update the functions gic_cpu_init() and gic_pm_init() to return an error instead of calling BUG() and perform any necessary clean-up. For non-banked GIC controllers, make sure that we free any memory allocated if we fail to initialise the IRQ domain. Please note that free_percpu() only frees memory if the pointer passed to it is not NULL and so it is unnecessary to check if both pointers are valid or not. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 99 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 26 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index a8cb86bea544..7cf138286442 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -467,7 +467,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL); } -static void gic_cpu_init(struct gic_chip_data *gic) +static int gic_cpu_init(struct gic_chip_data *gic) { void __iomem *dist_base = gic_data_dist_base(gic); void __iomem *base = gic_data_cpu_base(gic); @@ -483,7 +483,9 @@ static void gic_cpu_init(struct gic_chip_data *gic) /* * Get what the GIC says our CPU mask is. */ - BUG_ON(cpu >= NR_GIC_CPU_IF); + if (WARN_ON(cpu >= NR_GIC_CPU_IF)) + return -EINVAL; + cpu_mask = gic_get_cpumask(gic); gic_cpu_map[cpu] = cpu_mask; @@ -500,6 +502,8 @@ static void gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK); gic_cpu_if_up(gic); + + return 0; } int gic_cpu_if_down(unsigned int gic_nr) @@ -713,26 +717,39 @@ static struct notifier_block gic_notifier_block = { .notifier_call = gic_notifier, }; -static void __init gic_pm_init(struct gic_chip_data *gic) +static int __init gic_pm_init(struct gic_chip_data *gic) { gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_enable); + if (WARN_ON(!gic->saved_ppi_enable)) + return -ENOMEM; gic->saved_ppi_active = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_active); + if (WARN_ON(!gic->saved_ppi_active)) + goto free_ppi_enable; gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, sizeof(u32)); - BUG_ON(!gic->saved_ppi_conf); + if (WARN_ON(!gic->saved_ppi_conf)) + goto free_ppi_active; if (gic == &gic_data[0]) cpu_pm_register_notifier(&gic_notifier_block); + + return 0; + +free_ppi_active: + free_percpu(gic->saved_ppi_active); +free_ppi_enable: + free_percpu(gic->saved_ppi_enable); + + return -ENOMEM; } #else -static void __init gic_pm_init(struct gic_chip_data *gic) +static int __init gic_pm_init(struct gic_chip_data *gic) { + return 0; } #endif @@ -1005,13 +1022,13 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .unmap = gic_irq_domain_unmap, }; -static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, +static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct fwnode_handle *handle) { irq_hw_number_t hwirq_base; struct gic_chip_data *gic; - int gic_irqs, irq_base, i; + int gic_irqs, irq_base, i, ret; BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); @@ -1026,7 +1043,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.irq_mask = gic_eoimode1_mask_irq; gic->chip.irq_eoi = gic_eoimode1_eoi_irq; gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; - gic->chip.name = "GICv2"; + gic->chip.name = kasprintf(GFP_KERNEL, "GICv2"); } else { gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } @@ -1036,17 +1053,16 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.irq_set_affinity = gic_set_affinity; #endif -#ifdef CONFIG_GIC_NON_BANKED - if (percpu_offset) { /* Frankein-GIC without banked registers... */ + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + /* Frankein-GIC without banked registers... */ unsigned int cpu; gic->dist_base.percpu_base = alloc_percpu(void __iomem *); gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); if (WARN_ON(!gic->dist_base.percpu_base || !gic->cpu_base.percpu_base)) { - free_percpu(gic->dist_base.percpu_base); - free_percpu(gic->cpu_base.percpu_base); - return; + ret = -ENOMEM; + goto error; } for_each_possible_cpu(cpu) { @@ -1058,9 +1074,8 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, } gic_set_base_accessor(gic, gic_get_percpu_base); - } else -#endif - { /* Normal, sane GIC... */ + } else { + /* Normal, sane GIC... */ WARN(percpu_offset, "GIC_NON_BANKED not enabled, ignoring %08x offset!", percpu_offset); @@ -1110,8 +1125,10 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, hwirq_base, &gic_irq_domain_ops, gic); } - if (WARN_ON(!gic->domain)) - return; + if (WARN_ON(!gic->domain)) { + ret = -ENODEV; + goto error; + } if (gic_nr == 0) { /* @@ -1131,8 +1148,25 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, } gic_dist_init(gic); - gic_cpu_init(gic); - gic_pm_init(gic); + ret = gic_cpu_init(gic); + if (ret) + goto error; + + ret = gic_pm_init(gic); + if (ret) + goto error; + + return 0; + +error: + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + free_percpu(gic->dist_base.percpu_base); + free_percpu(gic->cpu_base.percpu_base); + } + + kfree(gic->chip.name); + + return ret; } void __init gic_init(unsigned int gic_nr, int irq_start, @@ -1193,7 +1227,7 @@ gic_of_init(struct device_node *node, struct device_node *parent) void __iomem *cpu_base; void __iomem *dist_base; u32 percpu_offset; - int irq; + int irq, ret; if (WARN_ON(!node)) return -ENODEV; @@ -1218,8 +1252,14 @@ gic_of_init(struct device_node *node, struct device_node *parent) if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) percpu_offset = 0; - __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, + ret = __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, &node->fwnode); + if (ret) { + iounmap(dist_base); + iounmap(cpu_base); + return ret; + } + if (!gic_cnt) gic_init_physaddr(node); @@ -1308,7 +1348,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, struct acpi_madt_generic_distributor *dist; void __iomem *cpu_base, *dist_base; struct fwnode_handle *domain_handle; - int count; + int count, ret; /* Collect CPU base addresses */ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, @@ -1351,7 +1391,14 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, return -ENOMEM; } - __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + ret = __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + if (ret) { + pr_err("Failed to initialise GIC\n"); + irq_domain_free_fwnode(domain_handle); + iounmap(cpu_base); + iounmap(dist_base); + return ret; + } acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); -- cgit From 6e5b5924d9a897062527b6fc9b06f31f7bfd5744 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:43 +0100 Subject: irqchip/gic: Pass GIC pointer to save/restore functions Instead of passing the GIC index to the save/restore functions pass a pointer to the GIC chip data. This will allow these save/restore functions to be re-used by a platform driver where the GIC chip data structure is allocated dynamically and so there is no applicable index for identifying the GIC. Signed-off-by: Jon Hunter Acked-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 74 +++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 35 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 7cf138286442..d75aa1a8d6da 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -529,34 +529,35 @@ int gic_cpu_if_down(unsigned int gic_nr) * this function, no interrupts will be delivered by the GIC, and another * platform-specific wakeup source must be enabled. */ -static void gic_dist_save(unsigned int gic_nr) +static void gic_dist_save(struct gic_chip_data *gic) { unsigned int gic_irqs; void __iomem *dist_base; int i; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); + gic_irqs = gic->gic_irqs; + dist_base = gic_data_dist_base(gic); if (!dist_base) return; for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) - gic_data[gic_nr].saved_spi_conf[i] = + gic->saved_spi_conf[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) - gic_data[gic_nr].saved_spi_target[i] = + gic->saved_spi_target[i] = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) - gic_data[gic_nr].saved_spi_enable[i] = + gic->saved_spi_enable[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) - gic_data[gic_nr].saved_spi_active[i] = + gic->saved_spi_active[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); } @@ -567,16 +568,17 @@ static void gic_dist_save(unsigned int gic_nr) * handled normally, but any edge interrupts that occured will not be seen by * the GIC and need to be handled by the platform-specific wakeup source. */ -static void gic_dist_restore(unsigned int gic_nr) +static void gic_dist_restore(struct gic_chip_data *gic) { unsigned int gic_irqs; unsigned int i; void __iomem *dist_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); + gic_irqs = gic->gic_irqs; + dist_base = gic_data_dist_base(gic); if (!dist_base) return; @@ -584,7 +586,7 @@ static void gic_dist_restore(unsigned int gic_nr) writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) - writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], + writel_relaxed(gic->saved_spi_conf[i], dist_base + GIC_DIST_CONFIG + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) @@ -592,85 +594,87 @@ static void gic_dist_restore(unsigned int gic_nr) dist_base + GIC_DIST_PRI + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) - writel_relaxed(gic_data[gic_nr].saved_spi_target[i], + writel_relaxed(gic->saved_spi_target[i], dist_base + GIC_DIST_TARGET + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); - writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], + writel_relaxed(gic->saved_spi_enable[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); } for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); - writel_relaxed(gic_data[gic_nr].saved_spi_active[i], + writel_relaxed(gic->saved_spi_active[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4); } writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL); } -static void gic_cpu_save(unsigned int gic_nr) +static void gic_cpu_save(struct gic_chip_data *gic) { int i; u32 *ptr; void __iomem *dist_base; void __iomem *cpu_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); - cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); + dist_base = gic_data_dist_base(gic); + cpu_base = gic_data_cpu_base(gic); if (!dist_base || !cpu_base) return; - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + ptr = raw_cpu_ptr(gic->saved_ppi_enable); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active); + ptr = raw_cpu_ptr(gic->saved_ppi_active); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + ptr = raw_cpu_ptr(gic->saved_ppi_conf); for (i = 0; i < DIV_ROUND_UP(32, 16); i++) ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); } -static void gic_cpu_restore(unsigned int gic_nr) +static void gic_cpu_restore(struct gic_chip_data *gic) { int i; u32 *ptr; void __iomem *dist_base; void __iomem *cpu_base; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic)) + return; - dist_base = gic_data_dist_base(&gic_data[gic_nr]); - cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); + dist_base = gic_data_dist_base(gic); + cpu_base = gic_data_cpu_base(gic); if (!dist_base || !cpu_base) return; - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + ptr = raw_cpu_ptr(gic->saved_ppi_enable); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); } - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active); + ptr = raw_cpu_ptr(gic->saved_ppi_active); for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { writel_relaxed(GICD_INT_EN_CLR_X32, dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); writel_relaxed(ptr[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4); } - ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + ptr = raw_cpu_ptr(gic->saved_ppi_conf); for (i = 0; i < DIV_ROUND_UP(32, 16); i++) writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); @@ -679,7 +683,7 @@ static void gic_cpu_restore(unsigned int gic_nr) dist_base + GIC_DIST_PRI + i * 4); writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK); - gic_cpu_if_up(&gic_data[gic_nr]); + gic_cpu_if_up(gic); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) @@ -694,18 +698,18 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) #endif switch (cmd) { case CPU_PM_ENTER: - gic_cpu_save(i); + gic_cpu_save(&gic_data[i]); break; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: - gic_cpu_restore(i); + gic_cpu_restore(&gic_data[i]); break; case CPU_CLUSTER_PM_ENTER: - gic_dist_save(i); + gic_dist_save(&gic_data[i]); break; case CPU_CLUSTER_PM_ENTER_FAILED: case CPU_CLUSTER_PM_EXIT: - gic_dist_restore(i); + gic_dist_restore(&gic_data[i]); break; } } -- cgit From f673b9b5cb5453fa14032d99edd55f49ac3980cc Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:44 +0100 Subject: irqchip/gic: Store GIC configuration parameters Store the GIC configuration parameters in the GIC chip data structure. This will allow us to simplify the code by reducing the number of parameters passed between functions. Update the __gic_init_bases() function so that we only need to pass a pointer to the GIC chip data structure and no longer need to pass the GIC index in order to look-up the chip data. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 114 ++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 49 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d75aa1a8d6da..f4c14f9ca15b 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -72,6 +72,9 @@ struct gic_chip_data { struct irq_chip chip; union gic_base dist_base; union gic_base cpu_base; + void __iomem *raw_dist_base; + void __iomem *raw_cpu_base; + u32 percpu_offset; #ifdef CONFIG_CPU_PM u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; u32 saved_spi_active[DIV_ROUND_UP(1020, 32)]; @@ -1026,38 +1029,36 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .unmap = gic_irq_domain_unmap, }; -static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, - void __iomem *dist_base, void __iomem *cpu_base, - u32 percpu_offset, struct fwnode_handle *handle) +static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start, + struct fwnode_handle *handle) { irq_hw_number_t hwirq_base; - struct gic_chip_data *gic; int gic_irqs, irq_base, i, ret; - BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); + if (WARN_ON(!gic || gic->domain)) + return -EINVAL; gic_check_cpu_features(); - gic = &gic_data[gic_nr]; - /* Initialize irq_chip */ gic->chip = gic_chip; - if (static_key_true(&supports_deactivate) && gic_nr == 0) { + if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) { gic->chip.irq_mask = gic_eoimode1_mask_irq; gic->chip.irq_eoi = gic_eoimode1_eoi_irq; gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; gic->chip.name = kasprintf(GFP_KERNEL, "GICv2"); } else { - gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); + gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", + (int)(gic - &gic_data[0])); } #ifdef CONFIG_SMP - if (gic_nr == 0) + if (gic == &gic_data[0]) gic->chip.irq_set_affinity = gic_set_affinity; #endif - if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { /* Frankein-GIC without banked registers... */ unsigned int cpu; @@ -1072,19 +1073,21 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, for_each_possible_cpu(cpu) { u32 mpidr = cpu_logical_map(cpu); u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); - unsigned long offset = percpu_offset * core_id; - *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; - *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; + unsigned long offset = gic->percpu_offset * core_id; + *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = + gic->raw_dist_base + offset; + *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = + gic->raw_cpu_base + offset; } gic_set_base_accessor(gic, gic_get_percpu_base); } else { /* Normal, sane GIC... */ - WARN(percpu_offset, + WARN(gic->percpu_offset, "GIC_NON_BANKED not enabled, ignoring %08x offset!", - percpu_offset); - gic->dist_base.common_base = dist_base; - gic->cpu_base.common_base = cpu_base; + gic->percpu_offset); + gic->dist_base.common_base = gic->raw_dist_base; + gic->cpu_base.common_base = gic->raw_cpu_base; gic_set_base_accessor(gic, gic_get_common_base); } @@ -1107,7 +1110,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, * For primary GICs, skip over SGIs. * For secondary GICs, skip over PPIs, too. */ - if (gic_nr == 0 && (irq_start & 31) > 0) { + if (gic == &gic_data[0] && (irq_start & 31) > 0) { hwirq_base = 16; if (irq_start != -1) irq_start = (irq_start & ~31) + 16; @@ -1134,7 +1137,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, goto error; } - if (gic_nr == 0) { + if (gic == &gic_data[0]) { /* * Initialize the CPU interface map to all CPUs. * It will be refined as each CPU probes its ID. @@ -1163,7 +1166,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start, return 0; error: - if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) { + if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { free_percpu(gic->dist_base.percpu_base); free_percpu(gic->cpu_base.percpu_base); } @@ -1176,12 +1179,22 @@ error: void __init gic_init(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base) { + struct gic_chip_data *gic; + + if (WARN_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR)) + return; + /* * Non-DT/ACPI systems won't run a hypervisor, so let's not * bother with these... */ static_key_slow_dec(&supports_deactivate); - __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL); + + gic = &gic_data[gic_nr]; + gic->raw_dist_base = dist_base; + gic->raw_cpu_base = cpu_base; + + __gic_init_bases(gic, irq_start, NULL); } #ifdef CONFIG_OF @@ -1228,21 +1241,24 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) int __init gic_of_init(struct device_node *node, struct device_node *parent) { - void __iomem *cpu_base; - void __iomem *dist_base; - u32 percpu_offset; + struct gic_chip_data *gic; int irq, ret; if (WARN_ON(!node)) return -ENODEV; - dist_base = of_iomap(node, 0); - if (WARN(!dist_base, "unable to map gic dist registers\n")) + if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR)) + return -EINVAL; + + gic = &gic_data[gic_cnt]; + + gic->raw_dist_base = of_iomap(node, 0); + if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) return -ENOMEM; - cpu_base = of_iomap(node, 1); - if (WARN(!cpu_base, "unable to map gic cpu registers\n")) { - iounmap(dist_base); + gic->raw_cpu_base = of_iomap(node, 1); + if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) { + iounmap(gic->raw_dist_base); return -ENOMEM; } @@ -1250,17 +1266,16 @@ gic_of_init(struct device_node *node, struct device_node *parent) * Disable split EOI/Deactivate if either HYP is not available * or the CPU interface is too small. */ - if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base)) + if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base)) static_key_slow_dec(&supports_deactivate); - if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) - percpu_offset = 0; + if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) + gic->percpu_offset = 0; - ret = __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, - &node->fwnode); + ret = __gic_init_bases(gic, -1, &node->fwnode); if (ret) { - iounmap(dist_base); - iounmap(cpu_base); + iounmap(gic->raw_dist_base); + iounmap(gic->raw_cpu_base); return ret; } @@ -1350,8 +1365,8 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, const unsigned long end) { struct acpi_madt_generic_distributor *dist; - void __iomem *cpu_base, *dist_base; struct fwnode_handle *domain_handle; + struct gic_chip_data *gic = &gic_data[0]; int count, ret; /* Collect CPU base addresses */ @@ -1362,17 +1377,18 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, return -EINVAL; } - cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); - if (!cpu_base) { + gic->raw_cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!gic->raw_cpu_base) { pr_err("Unable to map GICC registers\n"); return -ENOMEM; } dist = (struct acpi_madt_generic_distributor *)header; - dist_base = ioremap(dist->base_address, ACPI_GICV2_DIST_MEM_SIZE); - if (!dist_base) { + gic->raw_dist_base = ioremap(dist->base_address, + ACPI_GICV2_DIST_MEM_SIZE); + if (!gic->raw_dist_base) { pr_err("Unable to map GICD registers\n"); - iounmap(cpu_base); + iounmap(gic->raw_cpu_base); return -ENOMEM; } @@ -1387,20 +1403,20 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, /* * Initialize GIC instance zero (no multi-GIC support). */ - domain_handle = irq_domain_alloc_fwnode(dist_base); + domain_handle = irq_domain_alloc_fwnode(gic->raw_dist_base); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); - iounmap(cpu_base); - iounmap(dist_base); + iounmap(gic->raw_cpu_base); + iounmap(gic->raw_dist_base); return -ENOMEM; } - ret = __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); + ret = __gic_init_bases(gic, -1, domain_handle); if (ret) { pr_err("Failed to initialise GIC\n"); irq_domain_free_fwnode(domain_handle); - iounmap(cpu_base); - iounmap(dist_base); + iounmap(gic->raw_cpu_base); + iounmap(gic->raw_dist_base); return ret; } -- cgit From d6490461a102094891d8a2712c51365f86ac1a40 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2016 16:14:45 +0100 Subject: irqchip/gic: Add helper functions for GIC setup and teardown Move the code that sets-up a GIC via device-tree into it's own function and add a generic function for GIC teardown that can be used for both device-tree and ACPI to unmap the GIC memory. Signed-off-by: Jon Hunter Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 61 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'drivers/irqchip/irq-gic.c') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index f4c14f9ca15b..113e2d02c812 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1197,6 +1197,17 @@ void __init gic_init(unsigned int gic_nr, int irq_start, __gic_init_bases(gic, irq_start, NULL); } +static void gic_teardown(struct gic_chip_data *gic) +{ + if (WARN_ON(!gic)) + return; + + if (gic->raw_dist_base) + iounmap(gic->raw_dist_base); + if (gic->raw_cpu_base) + iounmap(gic->raw_cpu_base); +} + #ifdef CONFIG_OF static int gic_cnt __initdata; @@ -1238,6 +1249,30 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) return true; } +static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) +{ + if (!gic || !node) + return -EINVAL; + + gic->raw_dist_base = of_iomap(node, 0); + if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) + goto error; + + gic->raw_cpu_base = of_iomap(node, 1); + if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) + goto error; + + if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) + gic->percpu_offset = 0; + + return 0; + +error: + gic_teardown(gic); + + return -ENOMEM; +} + int __init gic_of_init(struct device_node *node, struct device_node *parent) { @@ -1252,15 +1287,9 @@ gic_of_init(struct device_node *node, struct device_node *parent) gic = &gic_data[gic_cnt]; - gic->raw_dist_base = of_iomap(node, 0); - if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) - return -ENOMEM; - - gic->raw_cpu_base = of_iomap(node, 1); - if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) { - iounmap(gic->raw_dist_base); - return -ENOMEM; - } + ret = gic_of_setup(gic, node); + if (ret) + return ret; /* * Disable split EOI/Deactivate if either HYP is not available @@ -1269,13 +1298,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); - if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) - gic->percpu_offset = 0; - ret = __gic_init_bases(gic, -1, &node->fwnode); if (ret) { - iounmap(gic->raw_dist_base); - iounmap(gic->raw_cpu_base); + gic_teardown(gic); return ret; } @@ -1388,7 +1413,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, ACPI_GICV2_DIST_MEM_SIZE); if (!gic->raw_dist_base) { pr_err("Unable to map GICD registers\n"); - iounmap(gic->raw_cpu_base); + gic_teardown(gic); return -ENOMEM; } @@ -1406,8 +1431,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, domain_handle = irq_domain_alloc_fwnode(gic->raw_dist_base); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); - iounmap(gic->raw_cpu_base); - iounmap(gic->raw_dist_base); + gic_teardown(gic); return -ENOMEM; } @@ -1415,8 +1439,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, if (ret) { pr_err("Failed to initialise GIC\n"); irq_domain_free_fwnode(domain_handle); - iounmap(gic->raw_cpu_base); - iounmap(gic->raw_dist_base); + gic_teardown(gic); return ret; } -- cgit