summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Nowicki <tomasz.nowicki@linaro.org>2014-08-25 11:59:25 +0200
committerTomasz Nowicki <tomasz.nowicki@linaro.org>2014-09-02 17:31:35 +0200
commit25b32600ec9608bb335547bc0e1c5fa522714e36 (patch)
tree24e4dfdea536137ac8d36c581a5d579b650d4125
parent2e013949c4ce51ec391a2d11b3f76f9d3fc81f23 (diff)
ACPI, GICV3+: Add support for GICv3+ initialization.acpi-gicv3
Obtain GICv3+ re-distributor base addresses from MADT subtable, check data integrity and call GICv3 init funtion. GIC drivers probe order: if MADT provides redistributors, try GICv3 driver, otherwise try GICv2. Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
-rw-r--r--arch/arm64/kernel/acpi.c5
-rw-r--r--drivers/irqchip/irq-gic-v3.c202
-rw-r--r--include/linux/irqchip/arm-gic-acpi.h1
3 files changed, 207 insertions, 1 deletions
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index b3b82b09d5e1..f00a17b423f9 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -329,10 +329,13 @@ void __init acpi_gic_init(void)
return;
}
- err = gic_v2_acpi_init(table);
+ err = gic_v3_acpi_init(table);
+ if (err)
+ err = gic_v2_acpi_init(table);
if (err)
pr_err("Failed to initialize GIC IRQ controller");
+
early_acpi_os_unmap_memory((char *)table, tbl_size);
}
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 23510e9a905f..567ca600bafe 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -21,10 +21,12 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
#include <asm/exception.h>
@@ -748,3 +750,203 @@ out_unmap_dist:
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
#endif
+
+#ifdef CONFIG_ACPI
+static struct rdist_region *redist_regs;
+static u32 redist_regions;
+static void __iomem *dist_base;
+
+static void __iomem * __init
+gic_acpi_map_one_redist(u64 redist_base_address)
+{
+ void __iomem *redist_base;
+ u64 typer;
+ u32 reg;
+
+ /* Map RD + SGI pages */
+ redist_base = ioremap(redist_base_address, 2 * SZ_64K);
+ if (!redist_base)
+ return NULL;
+
+ /*
+ * Map another two pages VLPI + reserved, if GIC support
+ * virtual LPI.
+ */
+ reg = readl_relaxed(redist_base + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
+ if (reg != 0x30 && reg != 0x40) { /* We're in trouble... */
+ pr_warn("No redistributor present @%p\n", redist_base);
+ iounmap(redist_base);
+ return NULL;
+ }
+
+ typer = readq_relaxed(redist_base + GICR_TYPER);
+ if (typer & GICR_TYPER_VLPIS) {
+ iounmap(redist_base);
+ redist_base = ioremap(redist_base_address, 4 * SZ_64K);
+ }
+
+ return redist_base;
+}
+
+static int __init
+gic_acpi_register_redist(u64 redist_base_address, u64 size, int region)
+{
+ struct rdist_region *redist_regs_new;
+ void __iomem *redist_base;
+
+ redist_regs_new = krealloc(redist_regs,
+ sizeof(*redist_regs) * (redist_regions + 1),
+ GFP_KERNEL);
+ if (!redist_regs_new) {
+ pr_err("Couldn't allocate resource for GICR region\n");
+ return -ENOMEM;
+ }
+
+ redist_regs = redist_regs_new;
+
+ /*
+ * Region contains a distinct set of GIC redistributors. Region size
+ * gives us all info we need to map redistributors properly.
+ *
+ * If it is not region, we assume to deal with one redistributor.
+ * Redistributor size is probeable and depends on GIC version:
+ * GICv3: RD + SGI pages
+ * GICv4: RD + SGI + VLPI + reserved pages
+ */
+ if (region)
+ redist_base = ioremap(redist_base_address, size);
+ else
+ redist_base = gic_acpi_map_one_redist(redist_base_address);
+
+ if (!redist_base) {
+ pr_err("Couldn't map GICR region @%lx\n",
+ (long int)redist_base_address);
+ return -ENOMEM;
+ }
+
+ redist_regs[redist_regions].phys_base = redist_base_address;
+ redist_regs[redist_regions++].redist_base = redist_base;
+ return 0;
+}
+
+static int __init
+gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *processor;
+
+ if (BAD_MADT_ENTRY(header, end))
+ return -EINVAL;
+
+ processor = (struct acpi_madt_generic_interrupt *)header;
+ if (!processor->gicr_base_address)
+ return -EINVAL;
+
+ return gic_acpi_register_redist(processor->gicr_base_address, 0, 0);
+}
+
+static int __init
+gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_redistributor *redist;
+
+ if (BAD_MADT_ENTRY(header, end))
+ return -EINVAL;
+
+ redist = (struct acpi_madt_generic_redistributor *)header;
+ if (!redist->base_address)
+ return -EINVAL;
+
+ return gic_acpi_register_redist(redist->base_address,
+ redist->length, 1);
+}
+
+static int __init
+gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_distributor *dist;
+
+ dist = (struct acpi_madt_generic_distributor *)header;
+
+ if (BAD_MADT_ENTRY(dist, end))
+ return -EINVAL;
+
+ dist_base = ioremap(dist->base_address, ACPI_GIC_DIST_MEM_SIZE);
+ if (!dist_base) {
+ pr_err("Unable to map GICD registers\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int __init
+gic_v3_acpi_init(struct acpi_table_header *table)
+{
+ int count, i, err = 0;
+
+ /* Collect redistributor base addresses */
+ count = acpi_parse_entries(sizeof(struct acpi_table_madt),
+ gic_acpi_parse_madt_redist, table,
+ ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+ ACPI_GIC_MAX_CPU_INTERFACE);
+ if (!count)
+ pr_info("No valid GICR entries exist\n");
+ else if (count < 0) {
+ pr_err("Error during GICR entries parsing\n");
+ err = -EINVAL;
+ goto out_redist_unmap;
+ } else
+ goto madt_dist;
+
+ /*
+ * There might be no GICR structure but we can still obtain
+ * redistributor collection from GICC subtables.
+ */
+ count = acpi_parse_entries(sizeof(struct acpi_table_madt),
+ gic_acpi_parse_madt_cpu, table,
+ ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ ACPI_GIC_MAX_CPU_INTERFACE);
+ if (!count) {
+ pr_info("No valid GICC entries exist\n");
+ return -EINVAL;
+ } else if (count < 0) {
+ pr_err("Error during GICC entries parsing\n");
+ err = -EINVAL;
+ goto out_redist_unmap;
+ }
+
+madt_dist:
+ /*
+ * We assume to parse one distributor entry since ACPI 5.0 spec
+ * neither support multi-GIC instances nor cascade.
+ */
+ count = acpi_parse_entries(sizeof(struct acpi_table_madt),
+ gic_acpi_parse_madt_distributor, table,
+ ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ ACPI_GIC_MAX_DISTRIBUTOR);
+ if (count <= 0) {
+ pr_err("Error during GICD entries parsing\n");
+ err = -EINVAL;
+ goto out_redist_unmap;
+ }
+
+ err = gic_init_bases(dist_base, redist_regs, redist_regions, 0, NULL);
+ if (err)
+ goto out_dist_unmap;
+
+ irq_set_default_host(gic_data.domain);
+ return 0;
+
+out_dist_unmap:
+ iounmap(dist_base);
+out_redist_unmap:
+ for (i = 0; i < redist_regions; i++)
+ if (redist_regs[i].redist_base)
+ iounmap(redist_regs[i].redist_base);
+ kfree(redist_regs);
+ return err;
+}
+#endif
diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h
index ce2ae1a8a18c..de6a582e08de 100644
--- a/include/linux/irqchip/arm-gic-acpi.h
+++ b/include/linux/irqchip/arm-gic-acpi.h
@@ -26,6 +26,7 @@
void acpi_gic_init(void);
int gic_v2_acpi_init(struct acpi_table_header *table);
+int gic_v3_acpi_init(struct acpi_table_header *table);
#else
static inline void acpi_gic_init(void) { }
#endif