From 4f54c38b9dae9b50ee933658298beff5a3a28827 Mon Sep 17 00:00:00 2001 From: David Mosberger <davidm@wailua.hpl.hp.com> Date: Wed, 10 Apr 2002 02:44:18 -0700 Subject: [PATCH] acpi.c: Paul's ACPI update. --- arch/ia64/kernel/acpi.c | 913 ++++++++++++++++++---------------------- 1 file changed, 414 insertions(+), 499 deletions(-) diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 501411199347..fca7fa836bd9 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -1,21 +1,34 @@ /* - * Advanced Configuration and Power Interface + * acpi.c - Architecture-Specific Low-Level ACPI Support * - * Based on 'ACPI Specification 1.0b' February 2, 1999 and - * 'IA-64 Extensions to ACPI Specification' Revision 0.6 + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 2000 Intel Corp. + * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> + * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * - * Copyright (C) 1999 VA Linux Systems - * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> - * Copyright (C) 2000 Hewlett-Packard Co. - * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> - * Copyright (C) 2000 Intel Corp. - * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> - * ACPI based kernel configuration manager. - * ACPI 2.0 & IA64 ext 0.71 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/config.h> - #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -23,29 +36,16 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/irq.h> -#ifdef CONFIG_SERIAL_ACPI -#include <linux/acpi_serial.h> -#endif - -#include <asm/acpi-ext.h> -#include <asm/acpikcfg.h> +#include <linux/acpi.h> #include <asm/efi.h> #include <asm/io.h> #include <asm/iosapic.h> #include <asm/machvec.h> #include <asm/page.h> +#include <asm/system.h> -#undef ACPI_DEBUG /* Guess what this does? */ - -/* global array to record platform interrupt vectors for generic int routing */ -int platform_irq_list[ACPI_MAX_PLATFORM_IRQS]; -/* These are ugly but will be reclaimed by the kernel */ -int __initdata available_cpus; -int __initdata total_cpus; - -void (*pm_idle) (void); -void (*pm_power_off) (void); +#define PREFIX "ACPI: " asm (".weak iosapic_register_irq"); asm (".weak iosapic_register_legacy_irq"); @@ -53,10 +53,16 @@ asm (".weak iosapic_register_platform_irq"); asm (".weak iosapic_init"); asm (".weak iosapic_version"); +void (*pm_idle) (void); +void (*pm_power_off) (void); + + +/* + * TBD: Should go away once we have an ACPI parser. + */ const char * acpi_get_sysname (void) { - /* the following should go away once we have an ACPI parser: */ #ifdef CONFIG_IA64_GENERIC return "hpsim"; #else @@ -72,16 +78,19 @@ acpi_get_sysname (void) # error Unknown platform. Fix acpi.c. # endif #endif - } +#define ACPI_MAX_PLATFORM_IRQS 256 + +/* Array to record platform interrupt vectors for generic interrupt routing. */ +int platform_irq_list[ACPI_MAX_PLATFORM_IRQS]; + /* - * Interrupt routing API for device drivers. - * Provides the interrupt vector for a generic platform event - * (currently only CPEI implemented) + * Interrupt routing API for device drivers. Provides interrupt vector for + * a generic platform event. Currently only CPEI is implemented. */ int -acpi_request_vector(u32 int_type) +acpi_request_vector (u32 int_type) { int vector = -1; @@ -94,586 +103,492 @@ acpi_request_vector(u32 int_type) return vector; } -/* - * Configure legacy IRQ information. - */ -static void __init -acpi_legacy_irq (char *p) -{ - acpi_entry_int_override_t *legacy = (acpi_entry_int_override_t *) p; - unsigned long polarity = 0, edge_triggered = 0; - /* - * If the platform we're running doesn't define - * iosapic_register_legacy_irq(), we ignore this info... - */ - if (!iosapic_register_legacy_irq) - return; - - switch (legacy->flags) { - case 0x5: polarity = 1; edge_triggered = 1; break; - case 0x7: polarity = 0; edge_triggered = 1; break; - case 0xd: polarity = 1; edge_triggered = 0; break; - case 0xf: polarity = 0; edge_triggered = 0; break; - default: - printk(" ACPI Legacy IRQ 0x%02x: Unknown flags 0x%x\n", legacy->isa_irq, - legacy->flags); - break; - } - iosapic_register_legacy_irq(legacy->isa_irq, legacy->pin, polarity, edge_triggered); -} +/* -------------------------------------------------------------------------- + Boot-time Table Parsing + -------------------------------------------------------------------------- */ -/* - * ACPI 2.0 tables parsing functions - */ +static int total_cpus __initdata; +static int available_cpus __initdata; +struct acpi_table_madt * acpi_madt __initdata; -static unsigned long -readl_unaligned(void *p) + +static int __init +acpi_parse_lapic_addr_ovr (acpi_table_entry_header *header) { - unsigned long ret; + struct acpi_table_lapic_addr_ovr *lapic = NULL; + + lapic = (struct acpi_table_lapic_addr_ovr *) header; + if (!lapic) + return -EINVAL; + + acpi_table_print_madt_entry(header); - memcpy(&ret, p, sizeof(long)); - return ret; + if (lapic->address) { + iounmap((void *) ipi_base_addr); + ipi_base_addr = (unsigned long) ioremap(lapic->address, 0); + } + + return 0; } -/* - * Identify usable CPU's and remember them for SMP bringup later. - */ -static void __init -acpi20_lsapic (char *p) + +static int __init +acpi_parse_lsapic (acpi_table_entry_header *header) { - int add = 1; + struct acpi_table_lsapic *lsapic = NULL; - acpi20_entry_lsapic_t *lsapic = (acpi20_entry_lsapic_t *) p; - printk(" CPU %.04x:%.04x: ", lsapic->eid, lsapic->id); + lsapic = (struct acpi_table_lsapic *) header; + if (!lsapic) + return -EINVAL; - if ((lsapic->flags & LSAPIC_ENABLED) == 0) { - printk("disabled.\n"); - add = 0; - } + acpi_table_print_madt_entry(header); -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = -1; -#endif - if (add) { + printk("CPU %d (0x%04x)", total_cpus, (lsapic->id << 8) | lsapic->eid); + + if (lsapic->flags.enabled) { available_cpus++; - printk("available"); + printk(" enabled"); #ifdef CONFIG_SMP smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid; if (hard_smp_processor_id() == smp_boot_data.cpu_phys_id[total_cpus]) printk(" (BSP)"); #endif - printk(".\n"); } + else { + printk(" disabled"); +#ifdef CONFIG_SMP + smp_boot_data.cpu_phys_id[total_cpus] = -1; +#endif + } + + printk("\n"); + total_cpus++; + return 0; } -/* - * Extract iosapic info from madt (again) to determine which iosapic - * this platform interrupt resides in - */ + +static int __init +acpi_parse_lapic_nmi (acpi_table_entry_header *header) +{ + struct acpi_table_lapic_nmi *lacpi_nmi = NULL; + + lacpi_nmi = (struct acpi_table_lapic_nmi*) header; + if (!lacpi_nmi) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* TBD: Support lapic_nmi entries */ + + return 0; +} + + static int __init -acpi20_which_iosapic (int global_vector, acpi_madt_t *madt, u32 *irq_base, char **iosapic_address) +acpi_find_iosapic (int global_vector, u32 *irq_base, char **iosapic_address) { - acpi_entry_iosapic_t *iosapic; - char *p, *end; - int ver, max_pin; + struct acpi_table_iosapic *iosapic = NULL; + int ver = 0; + int max_pin = 0; + char *p = 0; + char *end = 0; - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + if (!irq_base || !iosapic_address) + return -ENODEV; + + p = (char *) (acpi_madt + 1); + end = p + (acpi_madt->header.length - sizeof(struct acpi_table_madt)); while (p < end) { - switch (*p) { - case ACPI20_ENTRY_IO_SAPIC: - /* collect IOSAPIC info for platform int use later */ - iosapic = (acpi_entry_iosapic_t *)p; - *irq_base = iosapic->irq_base; + if (*p == ACPI_MADT_IOSAPIC) { + iosapic = (struct acpi_table_iosapic *) p; + + *irq_base = iosapic->global_irq_base; *iosapic_address = ioremap(iosapic->address, 0); - /* is this the iosapic we're looking for? */ + ver = iosapic_version(*iosapic_address); max_pin = (ver >> 16) & 0xff; + if ((global_vector - *irq_base) <= max_pin) - return 0; /* found it! */ - break; - default: - break; + return 0; /* Found it! */ } p += p[1]; } - return 1; + return -ENODEV; } -/* - * Info on platform interrupt sources: NMI, PMI, INIT, etc. - */ -static void __init -acpi20_platform (char *p, acpi_madt_t *madt) + +static int __init +acpi_parse_iosapic (acpi_table_entry_header *header) { - int vector; - u32 irq_base; - char *iosapic_address; - unsigned long polarity = 0, trigger = 0; - acpi20_entry_platform_src_t *plat = (acpi20_entry_platform_src_t *) p; + struct acpi_table_iosapic *iosapic; - printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n", - plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); + iosapic = (struct acpi_table_iosapic *) header; + if (!iosapic) + return -EINVAL; - /* record platform interrupt vectors for generic int routing code */ + acpi_table_print_madt_entry(header); - if (!iosapic_register_platform_irq) { - printk("acpi20_platform(): no ACPI platform IRQ support\n"); - return; + if (iosapic_init) { +#ifndef CONFIG_ITANIUM + /* PCAT_COMPAT flag indicates dual-8259 setup */ + iosapic_init(iosapic->address, iosapic->global_irq_base, + acpi_madt->flags.pcat_compat); +#else + /* Firmware on old Itanium systems is broken */ + iosapic_init(iosapic->address, iosapic->global_irq_base, 1); +#endif } + return 0; +} + + +static int __init +acpi_parse_plat_int_src (acpi_table_entry_header *header) +{ + struct acpi_table_plat_int_src *plintsrc = NULL; + int vector = 0; + u32 irq_base = 0; + char *iosapic_address = NULL; - /* extract polarity and trigger info from flags */ - switch (plat->flags) { - case 0x5: polarity = 1; trigger = 1; break; - case 0x7: polarity = 0; trigger = 1; break; - case 0xd: polarity = 1; trigger = 0; break; - case 0xf: polarity = 0; trigger = 0; break; - default: - printk("acpi20_platform(): unknown flags 0x%x\n", plat->flags); - break; + plintsrc = (struct acpi_table_plat_int_src *) header; + if (!plintsrc) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + if (!iosapic_register_platform_irq) { + printk(KERN_WARNING PREFIX "No ACPI platform IRQ support\n"); + return -ENODEV; } - /* which iosapic does this IRQ belong to? */ - if (acpi20_which_iosapic(plat->global_vector, madt, &irq_base, &iosapic_address)) { - printk("acpi20_platform(): I/O SAPIC not found!\n"); - return; + if (0 != acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) { + printk(KERN_WARNING PREFIX "IOSAPIC not found\n"); + return -ENODEV; } /* - * get vector assignment for this IRQ, set attributes, and program the IOSAPIC - * routing table + * Get vector assignment for this IRQ, set attributes, and program the + * IOSAPIC routing table. */ - vector = iosapic_register_platform_irq(plat->int_type, - plat->global_vector, - plat->iosapic_vector, - plat->eid, - plat->id, - polarity, - trigger, - irq_base, - iosapic_address); - platform_irq_list[plat->int_type] = vector; + vector = iosapic_register_platform_irq (plintsrc->type, + plintsrc->global_irq, + plintsrc->iosapic_vector, + plintsrc->eid, + plintsrc->id, + (plintsrc->flags.polarity == 1) ? 1 : 0, + (plintsrc->flags.trigger == 1) ? 1 : 0, + irq_base, + iosapic_address); + + platform_irq_list[plintsrc->type] = vector; + return 0; } -/* - * Override the physical address of the local APIC in the MADT stable header. - */ -static void __init -acpi20_lapic_addr_override (char *p) + +static int __init +acpi_parse_int_src_ovr (acpi_table_entry_header *header) { - acpi20_entry_lapic_addr_override_t * lapic = (acpi20_entry_lapic_addr_override_t *) p; + struct acpi_table_int_src_ovr *p = NULL; - if (lapic->lapic_address) { - iounmap((void *)ipi_base_addr); - ipi_base_addr = (unsigned long) ioremap(lapic->lapic_address, 0); + p = (struct acpi_table_int_src_ovr *) header; + if (!p) + return -EINVAL; - printk("LOCAL ACPI override to 0x%lx(p=0x%lx)\n", - ipi_base_addr, lapic->lapic_address); - } + acpi_table_print_madt_entry(header); + + /* Ignore if the platform doesn't support overrides */ + if (!iosapic_register_legacy_irq) + return 0; + + iosapic_register_legacy_irq(p->bus_irq, p->global_irq, + (p->flags.polarity == 1) ? 1 : 0, + (p->flags.trigger == 1) ? 1 : 0); + + return 0; } -/* - * Parse the ACPI Multiple APIC Description Table - */ -static void __init -acpi20_parse_madt (acpi_madt_t *madt) + +static int __init +acpi_parse_nmi_src (acpi_table_entry_header *header) { - acpi_entry_iosapic_t *iosapic = NULL; - acpi20_entry_lsapic_t *lsapic = NULL; - char *p, *end; - int i; - - /* Base address of IPI Message Block */ - if (madt->lapic_address) { - ipi_base_addr = (unsigned long) ioremap(madt->lapic_address, 0); - printk("Lapic address set to 0x%lx\n", ipi_base_addr); - } else - printk("Lapic address set to default 0x%lx\n", ipi_base_addr); + struct acpi_table_nmi_src *nmi_src = NULL; - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + nmi_src = (struct acpi_table_nmi_src*) header; + if (!nmi_src) + return -EINVAL; - /* Initialize platform interrupt vector array */ - for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++) - platform_irq_list[i] = -1; + acpi_table_print_madt_entry(header); - /* - * Split-up entry parsing to ensure ordering. - */ - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE: - printk("ACPI 2.0 MADT: LOCAL APIC Override\n"); - acpi20_lapic_addr_override(p); - break; - - case ACPI20_ENTRY_LOCAL_SAPIC: - printk("ACPI 2.0 MADT: LOCAL SAPIC\n"); - lsapic = (acpi20_entry_lsapic_t *) p; - acpi20_lsapic(p); - break; - - case ACPI20_ENTRY_IO_SAPIC: - iosapic = (acpi_entry_iosapic_t *) p; - if (iosapic_init) - /* - * The PCAT_COMPAT flag indicates that the system has a - * dual-8259 compatible setup. - */ - iosapic_init(iosapic->address, iosapic->irq_base, -#ifdef CONFIG_ITANIUM - 1 /* fw on some Itanium systems is broken... */ -#else - (madt->flags & MADT_PCAT_COMPAT) -#endif - ); - break; - - case ACPI20_ENTRY_PLATFORM_INT_SOURCE: - printk("ACPI 2.0 MADT: PLATFORM INT SOURCE\n"); - acpi20_platform(p, madt); - break; - - case ACPI20_ENTRY_LOCAL_APIC: - printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); break; - case ACPI20_ENTRY_IO_APIC: - printk("ACPI 2.0 MADT: IO APIC entry\n"); break; - case ACPI20_ENTRY_NMI_SOURCE: - printk("ACPI 2.0 MADT: NMI SOURCE entry\n"); break; - case ACPI20_ENTRY_LOCAL_APIC_NMI: - printk("ACPI 2.0 MADT: LOCAL APIC NMI entry\n"); break; - case ACPI20_ENTRY_INT_SRC_OVERRIDE: - break; - default: - printk("ACPI 2.0 MADT: unknown entry skip\n"); break; - break; - } - p += p[1]; - } + /* TBD: Support nimsrc entries */ - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + return 0; +} - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_LOCAL_APIC: - if (lsapic) break; - printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); - /* parse local apic if there's no local Sapic */ - break; - case ACPI20_ENTRY_IO_APIC: - if (iosapic) break; - printk("ACPI 2.0 MADT: IO APIC entry\n"); - /* parse ioapic if there's no ioSapic */ - break; - default: - break; - } - p += p[1]; - } - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); +static int __init +acpi_parse_madt (unsigned long phys_addr, unsigned long size) +{ + int i = 0; - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_INT_SRC_OVERRIDE: - printk("ACPI 2.0 MADT: INT SOURCE Override\n"); - acpi_legacy_irq(p); - break; - default: - break; - } - p += p[1]; + if (!phys_addr || !size) + return -EINVAL; + + acpi_madt = (struct acpi_table_madt *) __va(phys_addr); + if (!acpi_madt) { + printk(KERN_WARNING PREFIX "Unable to map MADT\n"); + return -ENODEV; } - /* Make bootup pretty */ - printk(" %d CPUs available, %d CPUs total\n", - available_cpus, total_cpus); + /* Initialize platform interrupt vector array */ + + for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++) + platform_irq_list[i] = -1; + + /* Get base address of IPI Message Block */ + + if (acpi_madt->lapic_address) + ipi_base_addr = (unsigned long) + ioremap(acpi_madt->lapic_address, 0); + + printk(KERN_INFO PREFIX "Local APIC address 0x%lx\n", ipi_base_addr); + + return 0; } + int __init -acpi20_parse (acpi20_rsdp_t *rsdp20) +acpi_find_rsdp (unsigned long *rsdp_phys) { -# ifdef CONFIG_ACPI - acpi_xsdt_t *xsdt; - acpi_desc_table_hdr_t *hdrp; - acpi_madt_t *madt; - int tables, i; - - if (strncmp(rsdp20->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { - printk("ACPI 2.0 RSDP signature incorrect!\n"); - return 0; - } else { - printk("ACPI 2.0 Root System Description Ptr at 0x%lx\n", - (unsigned long)rsdp20); - } + if (!rsdp_phys) + return -EINVAL; - xsdt = __va(rsdp20->xsdt); - hdrp = &xsdt->header; - if (strncmp(hdrp->signature, - ACPI_XSDT_SIG, ACPI_XSDT_SIG_LEN)) { - printk("ACPI 2.0 XSDT signature incorrect. Trying RSDT\n"); - /* RSDT parsing here */ + if (efi.acpi20) { + (*rsdp_phys) = __pa(efi.acpi20); return 0; - } else { - printk("ACPI 2.0 XSDT at 0x%lx (p=0x%lx)\n", - (unsigned long)xsdt, (unsigned long)rsdp20->xsdt); } + else if (efi.acpi) { + printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n"); + } + + return -ENODEV; +} - printk("ACPI 2.0: %.6s %.8s %d.%d\n", - hdrp->oem_id, - hdrp->oem_table_id, - hdrp->oem_revision >> 16, - hdrp->oem_revision & 0xffff); - acpi_cf_init((void *)rsdp20); +#ifdef CONFIG_SERIAL_ACPI - tables =(hdrp->length -sizeof(acpi_desc_table_hdr_t))>>3; +#include <linux/acpi_serial.h> - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i])); - printk(" :table %4.4s found\n", hdrp->signature); +static int __init +acpi_parse_spcr (unsigned long phys_addr, unsigned long size) +{ + acpi_ser_t *spcr = NULL; + unsigned long global_int = 0; - /* Only interested int the MADT table for now ... */ - if (strncmp(hdrp->signature, - ACPI_MADT_SIG, ACPI_MADT_SIG_LEN) != 0) - continue; + if (!phys_addr || !size) + return -EINVAL; - /* Save MADT pointer for later */ - madt = (acpi_madt_t *) hdrp; - acpi20_parse_madt(madt); - } + if (!iosapic_register_irq) + return -ENODEV; -#ifdef CONFIG_SERIAL_ACPI /* - * Now we're interested in other tables. We want the iosapics already - * initialized, so we do it in a separate loop. + * ACPI is able to describe serial ports that live at non-standard + * memory addresses and use non-standard interrupts, either via + * direct SAPIC mappings or via PCI interrupts. We handle interrupt + * routing for SAPIC-based (non-PCI) devices here. Interrupt routing + * for PCI devices will be handled when processing the PCI Interrupt + * Routing Table (PRT). */ - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i])); - /* - * search for SPCR and DBGP table entries so we can enable - * non-pci interrupts to IO-SAPICs. - */ - if (!strncmp(hdrp->signature, ACPI_SPCRT_SIG, ACPI_SPCRT_SIG_LEN) || - !strncmp(hdrp->signature, ACPI_DBGPT_SIG, ACPI_DBGPT_SIG_LEN)) - { - acpi_ser_t *spcr = (void *)hdrp; - unsigned long global_int; - - setup_serial_acpi(hdrp); - - /* - * ACPI is able to describe serial ports that live at non-standard - * memory space addresses and use SAPIC interrupts. If not also - * PCI devices, there would be no interrupt vector information for - * them. This checks for and fixes that situation. - */ - if (spcr->length < sizeof(acpi_ser_t)) - /* table is not long enough for full info, thus no int */ - break; - - /* - * If the device is not in PCI space, but uses a SAPIC interrupt, - * we need to program the SAPIC so that serial can autoprobe for - * the IA64 interrupt vector later on. If the device is in PCI - * space, it should already be setup via the PCI vectors - */ - if (spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE && - spcr->int_type == ACPI_SERIAL_INT_SAPIC) - { - u32 irq_base; - char *iosapic_address; - int vector; - - /* We have a UART in memory space with a SAPIC interrupt */ - global_int = ( (spcr->global_int[3] << 24) - | (spcr->global_int[2] << 16) - | (spcr->global_int[1] << 8) - | spcr->global_int[0]); - - if (!iosapic_register_irq) - continue; - - /* which iosapic does this IRQ belong to? */ - if (acpi20_which_iosapic(global_int, madt, &irq_base, - &iosapic_address) == 0) - { - vector = iosapic_register_irq(global_int, - 1, /* active high polarity */ - 1, /* edge triggered */ - irq_base, - iosapic_address); - } - } - } + + spcr = (acpi_ser_t *) __va(phys_addr); + if (!spcr) { + printk(KERN_WARNING PREFIX "Unable to map SPCR\n"); + return -ENODEV; } -#endif - acpi_cf_terminate(); -# ifdef CONFIG_SMP - if (available_cpus == 0) { - printk("ACPI: Found 0 CPUS; assuming 1\n"); - available_cpus = 1; /* We've got at least one of these, no? */ + setup_serial_acpi(spcr); + + if (spcr->length < sizeof(acpi_ser_t)) + /* Table not long enough for full info, thus no interrupt */ + return -ENODEV; + + if ((spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE) && + (spcr->int_type == ACPI_SERIAL_INT_SAPIC)) + { + u32 irq_base = 0; + char *iosapic_address = NULL; + int vector = 0; + + /* We have a UART in memory space with an SAPIC interrupt */ + + global_int = ( (spcr->global_int[3] << 24) | + (spcr->global_int[2] << 16) | + (spcr->global_int[1] << 8) | + (spcr->global_int[0]) ); + + /* Which iosapic does this IRQ belong to? */ + + if (0 == acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) { + vector = iosapic_register_irq (global_int, 1, 1, + irq_base, iosapic_address); + } } - smp_boot_data.cpu_count = total_cpus; -# endif -# endif /* CONFIG_ACPI */ - return 1; + return 0; } -/* - * ACPI 1.0b with 0.71 IA64 extensions functions; should be removed once all - * platforms start supporting ACPI 2.0 - */ -/* - * Identify usable CPU's and remember them for SMP bringup later. - */ -static void __init -acpi_lsapic (char *p) +#endif /*CONFIG_SERIAL_ACPI*/ + + +int __init +acpi_boot_init (char *cmdline) { - int add = 1; + int result = 0; + + /* Initialize the ACPI boot-time table parser */ + result = acpi_table_init(cmdline); + if (0 != result) + return result; - acpi_entry_lsapic_t *lsapic = (acpi_entry_lsapic_t *) p; + /* + * MADT + * ---- + * Parse the Multiple APIC Description Table (MADT), if exists. + * Note that this table provides platform SMP configuration + * information -- the successor to MPS tables. + */ - if ((lsapic->flags & LSAPIC_PRESENT) == 0) - return; + result = acpi_table_parse(ACPI_APIC, acpi_parse_madt); + if (1 > result) + return result; - printk(" CPU %d (%.04x:%.04x): ", total_cpus, lsapic->eid, lsapic->id); + /* Local APIC */ - if ((lsapic->flags & LSAPIC_ENABLED) == 0) { - printk("Disabled.\n"); - add = 0; - } else if (lsapic->flags & LSAPIC_PERFORMANCE_RESTRICTED) { - printk("Performance Restricted; ignoring.\n"); - add = 0; + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); + return result; } -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = -1; -#endif - if (add) { - printk("Available.\n"); - available_cpus++; -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid; -#endif /* CONFIG_SMP */ + result = acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic); + if (1 > result) { + printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n"); + return -ENODEV; } - total_cpus++; -} -/* - * Info on platform interrupt sources: NMI. PMI, INIT, etc. - */ -static void __init -acpi_platform (char *p) -{ - acpi_entry_platform_src_t *plat = (acpi_entry_platform_src_t *) p; + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); + return result; + } - printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n", - plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); -} + /* I/O APIC */ -/* - * Parse the ACPI Multiple SAPIC Table - */ -static void __init -acpi_parse_msapic (acpi_sapic_t *msapic) -{ - acpi_entry_iosapic_t *iosapic; - char *p, *end; + result = acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic); + if (1 > result) { + printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n"); + return ((result == 0) ? -ENODEV : result); + } - /* Base address of IPI Message Block */ - ipi_base_addr = (unsigned long) ioremap(msapic->interrupt_block, 0); + /* System-Level Interrupt Routing */ - p = (char *) (msapic + 1); - end = p + (msapic->header.length - sizeof(acpi_sapic_t)); + result = acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n"); + return result; + } - while (p < end) { - switch (*p) { - case ACPI_ENTRY_LOCAL_SAPIC: - acpi_lsapic(p); - break; - - case ACPI_ENTRY_IO_SAPIC: - iosapic = (acpi_entry_iosapic_t *) p; - if (iosapic_init) - /* - * The ACPI I/O SAPIC table doesn't have a PCAT_COMPAT - * flag like the MADT table, but we can safely assume that - * ACPI 1.0b systems have a dual-8259 setup. - */ - iosapic_init(iosapic->address, iosapic->irq_base, 1); - break; - - case ACPI_ENTRY_INT_SRC_OVERRIDE: - acpi_legacy_irq(p); - break; - - case ACPI_ENTRY_PLATFORM_INT_SOURCE: - acpi_platform(p); - break; - - default: - break; - } + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); + return result; + } - /* Move to next table entry. */ - p += p[1]; + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); + return result; } - /* Make bootup pretty */ - printk(" %d CPUs available, %d CPUs total\n", available_cpus, total_cpus); +#ifdef CONFIG_SERIAL_ACPI + /* + * TBD: Need phased approach to table parsing (only do those absolutely + * required during boot-up). Recommend expanding concept of fix- + * feature devices (LDM) to include table-based devices such as + * serial ports, EC, SMBus, etc. + */ + acpi_table_parse(ACPI_SPCR, acpi_parse_spcr); +#endif /*CONFIG_SERIAL_ACPI*/ + +#ifdef CONFIG_SMP + if (available_cpus == 0) { + printk("ACPI: Found 0 CPUS; assuming 1\n"); + available_cpus = 1; /* We've got at least one of these, no? */ + } + smp_boot_data.cpu_count = total_cpus; +#endif + /* Make boot-up look pretty */ + printk("%d CPUs available, %d CPUs total\n", available_cpus, total_cpus); + + return 0; } + +/* -------------------------------------------------------------------------- + PCI Interrupt Routing + -------------------------------------------------------------------------- */ + int __init -acpi_parse (acpi_rsdp_t *rsdp) +acpi_get_prt (struct pci_vector_struct **vectors, int *count) { -# ifdef CONFIG_ACPI - acpi_rsdt_t *rsdt; - acpi_desc_table_hdr_t *hdrp; - long tables, i; + struct pci_vector_struct *vector = NULL; + struct list_head *node = NULL; + struct acpi_prt_entry *entry = NULL; + int i = 0; - if (strncmp(rsdp->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { - printk("Uh-oh, ACPI RSDP signature incorrect!\n"); - return 0; - } + if (!vectors || !count) + return -EINVAL; - rsdt = __va(rsdp->rsdt); - if (strncmp(rsdt->header.signature, ACPI_RSDT_SIG, ACPI_RSDT_SIG_LEN)) { - printk("Uh-oh, ACPI RDST signature incorrect!\n"); - return 0; + *vectors = NULL; + *count = 0; + + if (acpi_prts.count < 0) { + printk(KERN_ERR PREFIX "No PCI IRQ routing entries\n"); + return -ENODEV; } - printk("ACPI: %.6s %.8s %d.%d\n", rsdt->header.oem_id, rsdt->header.oem_table_id, - rsdt->header.oem_revision >> 16, rsdt->header.oem_revision & 0xffff); + /* Allocate vectors */ - acpi_cf_init(rsdp); + *vectors = kmalloc(sizeof(struct pci_vector_struct) * acpi_prts.count, GFP_KERNEL); + if (!(*vectors)) + return -ENOMEM; - tables = (rsdt->header.length - sizeof(acpi_desc_table_hdr_t)) / 8; - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(rsdt->entry_ptrs[i]); + /* Convert PRT entries to IOSAPIC PCI vectors */ - /* Only interested int the MSAPIC table for now ... */ - if (strncmp(hdrp->signature, ACPI_SAPIC_SIG, ACPI_SAPIC_SIG_LEN) != 0) - continue; + vector = *vectors; - acpi_parse_msapic((acpi_sapic_t *) hdrp); + list_for_each(node, &acpi_prts.entries) { + entry = (struct acpi_prt_entry *)node; + vector[i].bus = (u16) entry->id.bus; + vector[i].pci_id = (u32) entry->id.dev << 16 | 0xffff; + vector[i].pin = (u8) entry->id.pin; + vector[i].irq = (u8) entry->source.index; + i++; } + *count = acpi_prts.count; + return 0; +} - acpi_cf_terminate(); +/* Assume IA64 always use I/O SAPIC */ -# ifdef CONFIG_SMP - if (available_cpus == 0) { - printk("ACPI: Found 0 CPUS; assuming 1\n"); - available_cpus = 1; /* We've got at least one of these, no? */ - } - smp_boot_data.cpu_count = total_cpus; -# endif -# endif /* CONFIG_ACPI */ - return 1; +int __init +acpi_get_interrupt_model (int *type) +{ + if (!type) + return -EINVAL; + + *type = ACPI_INT_MODEL_IOSAPIC; + + return 0; } -- 2.30.9