@@ -99,8 +99,6 @@ static inline void arch_fix_phys_package_id(int num, u32 slot) { }
extern int (*acpi_suspend_lowlevel)(void);
#define acpi_wakeup_address 0
-#define MAX_GIC_CPU_INTERFACE 65535
-
#endif /* CONFIG_ACPI */
#endif /*_ASM_ACPI_H*/
@@ -21,6 +21,7 @@
#include <linux/irqdomain.h>
#include <linux/bootmem.h>
#include <linux/smp.h>
+#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
@@ -159,7 +160,8 @@ static int __init acpi_parse_madt_gic_cpu_interface_entries(void)
* we need for SMP init
*/
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
- acpi_parse_gic_cpu_interface, MAX_GIC_CPU_INTERFACE);
+ acpi_parse_gic_cpu_interface,
+ ACPI_GIC_MAX_CPU_INTERFACE);
if (!count) {
pr_err("No GIC CPU interface entries present\n");
@@ -311,6 +313,28 @@ int __init acpi_boot_init(void)
return err;
}
+void __init acpi_gic_init(void)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+ acpi_size tbl_size;
+ int err;
+
+ status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size);
+ if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_err("Failed to get MADT table, %s\n", msg);
+ return;
+ }
+
+ 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);
+}
+
/* MPIDR value provided in GICC structure is 64 bits, but
* the acpi processor driver use the 32 bits cpu hardware
* ID (apic_id on intel platform) everywhere, it is pretty
@@ -28,6 +28,7 @@
#include <linux/irqchip.h>
#include <linux/seq_file.h>
#include <linux/ratelimit.h>
+#include <linux/irqchip/arm-gic-acpi.h>
unsigned long irq_err_count;
@@ -78,6 +79,10 @@ void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
void __init init_IRQ(void)
{
irqchip_init();
+
+ if (!handle_arch_irq)
+ acpi_gic_init();
+
if (!handle_arch_irq)
panic("No interrupt controller found.");
}
@@ -35,12 +35,14 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -1082,3 +1084,115 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
#endif
+
+#ifdef CONFIG_ACPI
+static u64 dist_phy_base, cpu_phy_base = ULONG_MAX;
+
+static int __init
+gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *processor;
+ u64 gic_cpu_base;
+
+ processor = (struct acpi_madt_generic_interrupt *)header;
+
+ if (BAD_MADT_ENTRY(processor, end))
+ return -EINVAL;
+
+ gic_cpu_base = processor->base_address;
+ if (!gic_cpu_base)
+ return -EFAULT;
+
+ /*
+ * There is no support for non-banked GICv1/2 register in ACPI spec.
+ * All CPU interface addresses have to be the same.
+ */
+ if (cpu_phy_base != ULONG_MAX && gic_cpu_base != cpu_phy_base)
+ return -EFAULT;
+
+ cpu_phy_base = gic_cpu_base;
+ return 0;
+}
+
+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_phy_base = dist->base_address;
+ if (!dist_phy_base)
+ return -EFAULT;
+
+ return 0;
+}
+
+int __init
+gic_v2_acpi_init(struct acpi_table_header *table)
+{
+ void __iomem *cpu_base, *dist_base;
+ int count;
+
+ /* Collect CPU base addresses */
+ 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 < 0) {
+ pr_err("Error during GICC entries parsing\n");
+ return -EFAULT;
+ } else if (!count) {
+ /* No GICC entries provided, use address from MADT header */
+ struct acpi_table_madt *madt = (struct acpi_table_madt *)table;
+
+ if (!madt->address)
+ return -EFAULT;
+
+ cpu_phy_base = (u64)madt->address;
+ }
+
+ /*
+ * Find distributor base address. We expect one distributor entry since
+ * ACPI 5.1 spec neither support multi-GIC instances nor GIC 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");
+ return -EFAULT;
+ } else if (count > 1) {
+ pr_err("More than one GICD entry detected\n");
+ return -EINVAL;
+ }
+
+ cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
+ if (!cpu_base) {
+ pr_err("Unable to map GICC registers\n");
+ return -ENOMEM;
+ }
+
+ dist_base = ioremap(dist_phy_base, ACPI_GIC_DIST_MEM_SIZE);
+ if (!dist_base) {
+ pr_err("Unable to map GICD registers\n");
+ iounmap(cpu_base);
+ return -ENOMEM;
+ }
+
+ /*
+ * Initialize zero GIC instance (no multi-GIC support). Also, set GIC
+ * as default IRQ domain to allow for GSI registration and GSI to IRQ
+ * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
+ */
+ gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
+ irq_set_default_host(gic_data[0].domain);
+ return 0;
+}
+#endif
new file mode 100644
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, Linaro Ltd.
+ * Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef ARM_GIC_ACPI_H_
+#define ARM_GIC_ACPI_H_
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_ACPI
+#define ACPI_GIC_MAX_CPU_INTERFACE 65535
+#define ACPI_GIC_MAX_DISTRIBUTOR 1
+
+/*
+ * Hard code here, we can not get memory size from MADT (but FDT does),
+ * Actually no need to do that, because this size can be inferred
+ * from GIC spec.
+ */
+#define ACPI_GIC_DIST_MEM_SIZE (SZ_64K)
+#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K)
+
+void acpi_gic_init(void);
+int gic_v2_acpi_init(struct acpi_table_header *table);
+#else
+static inline void acpi_gic_init(void)
+{
+
+}
+#endif
+
+#endif /* ARM_GIC_ACPI_H_ */