Implement core functions for GIC and GIC distributor entries in MADT, after this patch, we can parse MADT for ARM now. here is the boot information for MADT parse:
enter early_acpi_boot_init enter acpi_boot_init ACPI: GIC (acpi_id[0x0000] gic_id[0x0000] enabled) ACPI: GIC (acpi_id[0x0001] gic_id[0x0001] enabled) ACPI: GIC (acpi_id[0x0002] gic_id[0x0002] enabled) ACPI: GIC (acpi_id[0x0003] gic_id[0x0003] enabled) ACPI: GIC (acpi_id[0x0004] gic_id[0x0004] disabled) ACPI: GIC (acpi_id[0x0005] gic_id[0x0005] disabled) ACPI: GIC (acpi_id[0x0006] gic_id[0x0006] disabled) ACPI: GIC (acpi_id[0x0007] gic_id[0x0007] disabled) ACPI: GIC Distributor (id[0x0000] address[0x2c001000] gsi_base[0]) Using ACPI (MADT) for SMP configuration information PERCPU: Embedded 10 pages/cpu @ffffffc03ffa7000 s11520 r8192 d21248 u40960
NOTE: Parse GIC distributor for irq system is not implemented, should fix it in the future.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/kernel/setup.c | 4 + drivers/acpi/plat/arm/boot.c | 165 +++++++++++++++++++++++++++++++++++++++++- drivers/acpi/tables.c | 21 ++++++ 3 files changed, 188 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index cb5a299..7e37ba0 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -299,6 +299,10 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif #endif + +#ifdef CONFIG_ACPI + acpi_boot_init(); +#endif }
static int __init arm64_device_init(void) diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index e62d167..3a27232 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -51,6 +51,10 @@ u32 acpi_rsdt_forced; int acpi_disabled; EXPORT_SYMBOL(acpi_disabled);
+/* TODO: should be moved to a head file */ +#define MAX_LOCAL_APIC 256 +#define MAX_IO_APICS 64 + #define BAD_MADT_ENTRY(entry, end) ( \ (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) @@ -70,6 +74,7 @@ int acpi_sci_override_gsi __initdata; int acpi_skip_timer_override __initdata; int acpi_use_timer_override __initdata; int acpi_fix_pin2_polarity __initdata; +static u64 acpi_lapic_addr __initdata;
struct acpi_arm_root acpi_arm_rsdp_info; /* info about RSDP from FDT */
@@ -117,6 +122,73 @@ void __init __acpi_unmap_table(char *map, unsigned long size) return; }
+static int __init acpi_parse_madt(struct acpi_table_header *table) +{ + struct acpi_table_madt *madt = NULL; + + madt = (struct acpi_table_madt *)table; + if (!madt) { + printk(KERN_WARNING PREFIX "Unable to map MADT\n"); + return -ENODEV; + } + + if (madt->address) { + acpi_lapic_addr = (u64) madt->address; + + printk(KERN_DEBUG PREFIX "Local APIC address 0x%08x\n", + madt->address); + } + + return 0; +} + +static void __cpuinit acpi_register_gic(int id, u8 enabled) +{ + return; +} + +static int __init +acpi_parse_gic(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor = NULL; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* + * We need to register disabled CPU as well to permit + * counting disabled CPUs. This allows us to size + * cpus_possible_map more accurately, to permit + * to not preallocating memory for all NR_CPUS + * when we use CPU hotplug. + */ + acpi_register_gic(processor->gic_id, /* APIC ID */ + processor->flags & ACPI_MADT_ENABLED); + + return 0; +} + +static int __init +acpi_parse_gic_distributor(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_generic_distributor *distributor = NULL; + + distributor = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(distributor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* TODO: handle with the base_address and irq_base for irq system */ + + return 0; +} + /* * acpi_pic_sci_set_trigger() * @@ -227,9 +299,9 @@ void __init acpi_set_irq_model_pic(void) acpi_ioapic = 0; }
-void __init acpi_set_irq_model_ioapic(void) +void __init acpi_set_irq_model_gic(void) { - acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; + acpi_irq_model = ACPI_IRQ_MODEL_GIC; __acpi_register_gsi = acpi_register_gsi_ioapic; acpi_ioapic = 1; } @@ -437,17 +509,106 @@ late_initcall(hpet_insert_resource); #define acpi_parse_hpet NULL #endif
+static int __init acpi_parse_madt_lapic_entries(void) +{ + int count; + + /* + * do a partial walk of MADT to determine how many CPUs + * we have including disabled CPUs + */ + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + acpi_parse_gic, MAX_LOCAL_APIC); + + if (!count) { + printk(KERN_ERR PREFIX "No LAPIC entries present\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return -ENODEV; + } else if (count < 0) { + printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + + return 0; +} + static int __init acpi_parse_fadt(struct acpi_table_header *table) { return 0; }
+/* + * Parse IOAPIC related entries in MADT + * returns 0 on success, < 0 on error + */ +static int __init acpi_parse_madt_ioapic_entries(void) +{ + int count; + + /* + * ACPI interpreter is required to complete interrupt setup, + * so if it is off, don't enumerate the io-apics with ACPI. + * If MPS is present, it will handle them, + * otherwise the system will stay in PIC mode + */ + if (acpi_disabled || acpi_noirq) + return -ENODEV; + + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, + acpi_parse_gic_distributor, MAX_IO_APICS); + + if (!count) { + printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); + return -ENODEV; + } else if (count < 0) { + printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n"); + return count; + } + + return 0; +} + static void __init early_acpi_process_madt(void) { + /* should I introduce CONFIG_ARM_LOCAL_APIC like x86 does? */ + acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt); }
static void __init acpi_process_madt(void) { + /* should I introduce CONFIG_ARM_LOCAL_APIC like x86 does? */ + int error; + + if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) { + + /* + * Parse MADT LAPIC entries + */ + error = acpi_parse_madt_lapic_entries(); + if (!error) { + acpi_lapic = 1; + + /* + * Parse MADT IO-APIC entries + */ + error = acpi_parse_madt_ioapic_entries(); + if (!error) + acpi_set_irq_model_gic(); + } + } + + /* + * ACPI supports both logical (e.g. Hyper-Threading) and physical + * processors, where MPS only supports physical. + */ + if (acpi_lapic && acpi_ioapic) + printk(KERN_INFO "Using ACPI (MADT) for SMP configuration " + "information\n"); + else if (acpi_lapic) + printk(KERN_INFO "Using ACPI for processor (LAPIC) " + "configuration information\n"); + return; }
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index d67a1fe..55e5198 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -191,6 +191,27 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } break;
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT: + { + struct acpi_madt_generic_interrupt *p = + (struct acpi_madt_generic_interrupt *)header; + printk(KERN_INFO PREFIX + "GIC (acpi_id[0x%04x] gic_id[0x%04x] %s)\n", + p->uid, p->gic_id, + (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + } + break; + + case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR: + { + struct acpi_madt_generic_distributor *p = + (struct acpi_madt_generic_distributor *)header; + printk(KERN_INFO PREFIX + "GIC Distributor (id[0x%04x] address[0x%08x] gsi_base[%d])\n", + p->gic_id, p->base_address, p->global_irq_base); + } + break; + default: printk(KERN_WARNING PREFIX "Found unsupported MADT entry (type = 0x%x)\n",