Commit 4f54c38b authored by David Mosberger's avatar David Mosberger

acpi.c:

  Paul's ACPI update.
parent e19ca5df
/* /*
* 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 VA Linux Systems
* Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
...@@ -10,12 +7,28 @@ ...@@ -10,12 +7,28 @@
* Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
* Copyright (C) 2000 Intel Corp. * Copyright (C) 2000 Intel Corp.
* Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com>
* ACPI based kernel configuration manager. * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* 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/config.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -23,29 +36,16 @@ ...@@ -23,29 +36,16 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/irq.h> #include <linux/irq.h>
#ifdef CONFIG_SERIAL_ACPI #include <linux/acpi.h>
#include <linux/acpi_serial.h>
#endif
#include <asm/acpi-ext.h>
#include <asm/acpikcfg.h>
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/iosapic.h> #include <asm/iosapic.h>
#include <asm/machvec.h> #include <asm/machvec.h>
#include <asm/page.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); #define PREFIX "ACPI: "
void (*pm_power_off) (void);
asm (".weak iosapic_register_irq"); asm (".weak iosapic_register_irq");
asm (".weak iosapic_register_legacy_irq"); asm (".weak iosapic_register_legacy_irq");
...@@ -53,10 +53,16 @@ asm (".weak iosapic_register_platform_irq"); ...@@ -53,10 +53,16 @@ asm (".weak iosapic_register_platform_irq");
asm (".weak iosapic_init"); asm (".weak iosapic_init");
asm (".weak iosapic_version"); 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 * const char *
acpi_get_sysname (void) acpi_get_sysname (void)
{ {
/* the following should go away once we have an ACPI parser: */
#ifdef CONFIG_IA64_GENERIC #ifdef CONFIG_IA64_GENERIC
return "hpsim"; return "hpsim";
#else #else
...@@ -72,16 +78,19 @@ acpi_get_sysname (void) ...@@ -72,16 +78,19 @@ acpi_get_sysname (void)
# error Unknown platform. Fix acpi.c. # error Unknown platform. Fix acpi.c.
# endif # endif
#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. * Interrupt routing API for device drivers. Provides interrupt vector for
* Provides the interrupt vector for a generic platform event * a generic platform event. Currently only CPEI is implemented.
* (currently only CPEI implemented)
*/ */
int int
acpi_request_vector(u32 int_type) acpi_request_vector (u32 int_type)
{ {
int vector = -1; int vector = -1;
...@@ -94,586 +103,492 @@ acpi_request_vector(u32 int_type) ...@@ -94,586 +103,492 @@ acpi_request_vector(u32 int_type)
return vector; 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 Boot-time Table Parsing
* 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);
}
/* static int total_cpus __initdata;
* ACPI 2.0 tables parsing functions 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)); if (lapic->address) {
return ret; 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 int __init
*/ acpi_parse_lsapic (acpi_table_entry_header *header)
static void __init
acpi20_lsapic (char *p)
{ {
int add = 1; struct acpi_table_lsapic *lsapic = NULL;
acpi20_entry_lsapic_t *lsapic = (acpi20_entry_lsapic_t *) p; lsapic = (struct acpi_table_lsapic *) header;
printk(" CPU %.04x:%.04x: ", lsapic->eid, lsapic->id); if (!lsapic)
return -EINVAL;
if ((lsapic->flags & LSAPIC_ENABLED) == 0) { acpi_table_print_madt_entry(header);
printk("disabled.\n");
add = 0;
}
#ifdef CONFIG_SMP printk("CPU %d (0x%04x)", total_cpus, (lsapic->id << 8) | lsapic->eid);
smp_boot_data.cpu_phys_id[total_cpus] = -1;
#endif if (lsapic->flags.enabled) {
if (add) {
available_cpus++; available_cpus++;
printk("available"); printk(" enabled");
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid; 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]) if (hard_smp_processor_id() == smp_boot_data.cpu_phys_id[total_cpus])
printk(" (BSP)"); printk(" (BSP)");
#endif #endif
printk(".\n");
} }
else {
printk(" disabled");
#ifdef CONFIG_SMP
smp_boot_data.cpu_phys_id[total_cpus] = -1;
#endif
}
printk("\n");
total_cpus++; total_cpus++;
return 0;
} }
/*
* Extract iosapic info from madt (again) to determine which iosapic
* this platform interrupt resides in
*/
static int __init static int __init
acpi20_which_iosapic (int global_vector, acpi_madt_t *madt, u32 *irq_base, char **iosapic_address) acpi_parse_lapic_nmi (acpi_table_entry_header *header)
{ {
acpi_entry_iosapic_t *iosapic; struct acpi_table_lapic_nmi *lacpi_nmi = NULL;
char *p, *end;
int ver, max_pin; lacpi_nmi = (struct acpi_table_lapic_nmi*) header;
if (!lacpi_nmi)
return -EINVAL;
p = (char *) (madt + 1); acpi_table_print_madt_entry(header);
end = p + (madt->header.length - sizeof(acpi_madt_t));
/* TBD: Support lapic_nmi entries */
return 0;
}
static int __init
acpi_find_iosapic (int global_vector, u32 *irq_base, char **iosapic_address)
{
struct acpi_table_iosapic *iosapic = NULL;
int ver = 0;
int max_pin = 0;
char *p = 0;
char *end = 0;
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) { while (p < end) {
switch (*p) { if (*p == ACPI_MADT_IOSAPIC) {
case ACPI20_ENTRY_IO_SAPIC: iosapic = (struct acpi_table_iosapic *) p;
/* collect IOSAPIC info for platform int use later */
iosapic = (acpi_entry_iosapic_t *)p; *irq_base = iosapic->global_irq_base;
*irq_base = iosapic->irq_base;
*iosapic_address = ioremap(iosapic->address, 0); *iosapic_address = ioremap(iosapic->address, 0);
/* is this the iosapic we're looking for? */
ver = iosapic_version(*iosapic_address); ver = iosapic_version(*iosapic_address);
max_pin = (ver >> 16) & 0xff; max_pin = (ver >> 16) & 0xff;
if ((global_vector - *irq_base) <= max_pin) if ((global_vector - *irq_base) <= max_pin)
return 0; /* found it! */ return 0; /* Found it! */
break;
default:
break;
} }
p += p[1]; p += p[1];
} }
return 1; return -ENODEV;
} }
/*
* Info on platform interrupt sources: NMI, PMI, INIT, etc. static int __init
*/ acpi_parse_iosapic (acpi_table_entry_header *header)
static void __init
acpi20_platform (char *p, acpi_madt_t *madt)
{ {
int vector; struct acpi_table_iosapic *iosapic;
u32 irq_base;
char *iosapic_address;
unsigned long polarity = 0, trigger = 0;
acpi20_entry_platform_src_t *plat = (acpi20_entry_platform_src_t *) p;
printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n", iosapic = (struct acpi_table_iosapic *) header;
plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); if (!iosapic)
return -EINVAL;
/* record platform interrupt vectors for generic int routing code */ acpi_table_print_madt_entry(header);
if (!iosapic_register_platform_irq) { if (iosapic_init) {
printk("acpi20_platform(): no ACPI platform IRQ support\n"); #ifndef CONFIG_ITANIUM
return; /* 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;
}
/* extract polarity and trigger info from flags */
switch (plat->flags) { static int __init
case 0x5: polarity = 1; trigger = 1; break; acpi_parse_plat_int_src (acpi_table_entry_header *header)
case 0x7: polarity = 0; trigger = 1; break; {
case 0xd: polarity = 1; trigger = 0; break; struct acpi_table_plat_int_src *plintsrc = NULL;
case 0xf: polarity = 0; trigger = 0; break; int vector = 0;
default: u32 irq_base = 0;
printk("acpi20_platform(): unknown flags 0x%x\n", plat->flags); char *iosapic_address = NULL;
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 (0 != acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) {
if (acpi20_which_iosapic(plat->global_vector, madt, &irq_base, &iosapic_address)) { printk(KERN_WARNING PREFIX "IOSAPIC not found\n");
printk("acpi20_platform(): I/O SAPIC not found!\n"); return -ENODEV;
return;
} }
/* /*
* get vector assignment for this IRQ, set attributes, and program the IOSAPIC * Get vector assignment for this IRQ, set attributes, and program the
* routing table * IOSAPIC routing table.
*/ */
vector = iosapic_register_platform_irq(plat->int_type, vector = iosapic_register_platform_irq (plintsrc->type,
plat->global_vector, plintsrc->global_irq,
plat->iosapic_vector, plintsrc->iosapic_vector,
plat->eid, plintsrc->eid,
plat->id, plintsrc->id,
polarity, (plintsrc->flags.polarity == 1) ? 1 : 0,
trigger, (plintsrc->flags.trigger == 1) ? 1 : 0,
irq_base, irq_base,
iosapic_address); iosapic_address);
platform_irq_list[plat->int_type] = vector;
platform_irq_list[plintsrc->type] = vector;
return 0;
} }
/*
* Override the physical address of the local APIC in the MADT stable header. static int __init
*/ acpi_parse_int_src_ovr (acpi_table_entry_header *header)
static void __init
acpi20_lapic_addr_override (char *p)
{ {
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) { p = (struct acpi_table_int_src_ovr *) header;
iounmap((void *)ipi_base_addr); if (!p)
ipi_base_addr = (unsigned long) ioremap(lapic->lapic_address, 0); return -EINVAL;
printk("LOCAL ACPI override to 0x%lx(p=0x%lx)\n", acpi_table_print_madt_entry(header);
ipi_base_addr, lapic->lapic_address);
} /* 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 int __init
*/ acpi_parse_nmi_src (acpi_table_entry_header *header)
static void __init
acpi20_parse_madt (acpi_madt_t *madt)
{ {
acpi_entry_iosapic_t *iosapic = NULL; struct acpi_table_nmi_src *nmi_src = 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);
p = (char *) (madt + 1); nmi_src = (struct acpi_table_nmi_src*) header;
end = p + (madt->header.length - sizeof(acpi_madt_t)); if (!nmi_src)
return -EINVAL;
/* Initialize platform interrupt vector array */ acpi_table_print_madt_entry(header);
for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++)
platform_irq_list[i] = -1;
/* /* TBD: Support nimsrc entries */
* 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];
}
p = (char *) (madt + 1); return 0;
end = p + (madt->header.length - sizeof(acpi_madt_t)); }
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); static int __init
end = p + (madt->header.length - sizeof(acpi_madt_t)); acpi_parse_madt (unsigned long phys_addr, unsigned long size)
{
int i = 0;
while (p < end) { if (!phys_addr || !size)
switch (*p) { return -EINVAL;
case ACPI20_ENTRY_INT_SRC_OVERRIDE:
printk("ACPI 2.0 MADT: INT SOURCE Override\n"); acpi_madt = (struct acpi_table_madt *) __va(phys_addr);
acpi_legacy_irq(p); if (!acpi_madt) {
break; printk(KERN_WARNING PREFIX "Unable to map MADT\n");
default: return -ENODEV;
break;
}
p += p[1];
} }
/* Make bootup pretty */ /* Initialize platform interrupt vector array */
printk(" %d CPUs available, %d CPUs total\n",
available_cpus, total_cpus); 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 int __init
acpi20_parse (acpi20_rsdp_t *rsdp20) acpi_find_rsdp (unsigned long *rsdp_phys)
{ {
# ifdef CONFIG_ACPI if (!rsdp_phys)
acpi_xsdt_t *xsdt; return -EINVAL;
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);
}
xsdt = __va(rsdp20->xsdt); if (efi.acpi20) {
hdrp = &xsdt->header; (*rsdp_phys) = __pa(efi.acpi20);
if (strncmp(hdrp->signature,
ACPI_XSDT_SIG, ACPI_XSDT_SIG_LEN)) {
printk("ACPI 2.0 XSDT signature incorrect. Trying RSDT\n");
/* RSDT parsing here */
return 0; return 0;
} else { }
printk("ACPI 2.0 XSDT at 0x%lx (p=0x%lx)\n", else if (efi.acpi) {
(unsigned long)xsdt, (unsigned long)rsdp20->xsdt); printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n");
} }
printk("ACPI 2.0: %.6s %.8s %d.%d\n", return -ENODEV;
hdrp->oem_id, }
hdrp->oem_table_id,
hdrp->oem_revision >> 16,
hdrp->oem_revision & 0xffff);
acpi_cf_init((void *)rsdp20);
tables =(hdrp->length -sizeof(acpi_desc_table_hdr_t))>>3;
for (i = 0; i < tables; i++) { #ifdef CONFIG_SERIAL_ACPI
hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i]));
printk(" :table %4.4s found\n", hdrp->signature);
/* Only interested int the MADT table for now ... */ #include <linux/acpi_serial.h>
if (strncmp(hdrp->signature,
ACPI_MADT_SIG, ACPI_MADT_SIG_LEN) != 0)
continue;
/* Save MADT pointer for later */ static int __init
madt = (acpi_madt_t *) hdrp; acpi_parse_spcr (unsigned long phys_addr, unsigned long size)
acpi20_parse_madt(madt); {
} acpi_ser_t *spcr = NULL;
unsigned long global_int = 0;
#ifdef CONFIG_SERIAL_ACPI if (!phys_addr || !size)
/* return -EINVAL;
* Now we're interested in other tables. We want the iosapics already
* initialized, so we do it in a separate loop.
*/
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); if (!iosapic_register_irq)
return -ENODEV;
/* /*
* ACPI is able to describe serial ports that live at non-standard * ACPI is able to describe serial ports that live at non-standard
* memory space addresses and use SAPIC interrupts. If not also * memory addresses and use non-standard interrupts, either via
* PCI devices, there would be no interrupt vector information for * direct SAPIC mappings or via PCI interrupts. We handle interrupt
* them. This checks for and fixes that situation. * routing for SAPIC-based (non-PCI) devices here. Interrupt routing
* for PCI devices will be handled when processing the PCI Interrupt
* Routing Table (PRT).
*/ */
spcr = (acpi_ser_t *) __va(phys_addr);
if (!spcr) {
printk(KERN_WARNING PREFIX "Unable to map SPCR\n");
return -ENODEV;
}
setup_serial_acpi(spcr);
if (spcr->length < sizeof(acpi_ser_t)) if (spcr->length < sizeof(acpi_ser_t))
/* table is not long enough for full info, thus no int */ /* Table not long enough for full info, thus no interrupt */
break; return -ENODEV;
/* if ((spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE) &&
* If the device is not in PCI space, but uses a SAPIC interrupt, (spcr->int_type == ACPI_SERIAL_INT_SAPIC))
* 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; u32 irq_base = 0;
char *iosapic_address; char *iosapic_address = NULL;
int vector; int vector = 0;
/* We have a UART in memory space with a SAPIC interrupt */ /* 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]);
if (!iosapic_register_irq) global_int = ( (spcr->global_int[3] << 24) |
continue; (spcr->global_int[2] << 16) |
(spcr->global_int[1] << 8) |
(spcr->global_int[0]) );
/* which iosapic does this IRQ belong to? */ /* 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);
}
}
}
}
#endif
acpi_cf_terminate();
# ifdef CONFIG_SMP if (0 == acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) {
if (available_cpus == 0) { vector = iosapic_register_irq (global_int, 1, 1,
printk("ACPI: Found 0 CPUS; assuming 1\n"); irq_base, iosapic_address);
available_cpus = 1; /* We've got at least one of these, no? */
} }
smp_boot_data.cpu_count = total_cpus; }
# endif return 0;
# endif /* CONFIG_ACPI */
return 1;
} }
/*
* ACPI 1.0b with 0.71 IA64 extensions functions; should be removed once all
* platforms start supporting ACPI 2.0
*/
/* #endif /*CONFIG_SERIAL_ACPI*/
* Identify usable CPU's and remember them for SMP bringup later.
*/
static void __init int __init
acpi_lsapic (char *p) acpi_boot_init (char *cmdline)
{ {
int add = 1; int result = 0;
acpi_entry_lsapic_t *lsapic = (acpi_entry_lsapic_t *) p; /* Initialize the ACPI boot-time table parser */
result = acpi_table_init(cmdline);
if (0 != result)
return result;
if ((lsapic->flags & LSAPIC_PRESENT) == 0) /*
return; * MADT
* ----
* Parse the Multiple APIC Description Table (MADT), if exists.
* Note that this table provides platform SMP configuration
* information -- the successor to MPS tables.
*/
printk(" CPU %d (%.04x:%.04x): ", total_cpus, lsapic->eid, lsapic->id); result = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
if (1 > result)
return result;
if ((lsapic->flags & LSAPIC_ENABLED) == 0) { /* Local APIC */
printk("Disabled.\n");
add = 0;
} else if (lsapic->flags & LSAPIC_PERFORMANCE_RESTRICTED) {
printk("Performance Restricted; ignoring.\n");
add = 0;
}
#ifdef CONFIG_SMP result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
smp_boot_data.cpu_phys_id[total_cpus] = -1; if (0 > result) {
#endif printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
if (add) { return result;
printk("Available.\n");
available_cpus++;
#ifdef CONFIG_SMP
smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid;
#endif /* CONFIG_SMP */
} }
total_cpus++;
}
/* result = acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic);
* Info on platform interrupt sources: NMI. PMI, INIT, etc. if (1 > result) {
*/ printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n");
static void __init return -ENODEV;
acpi_platform (char *p) }
{
acpi_entry_platform_src_t *plat = (acpi_entry_platform_src_t *) p;
printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n",
plat->iosapic_vector, plat->global_vector, plat->eid, plat->id);
}
/* result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
* Parse the ACPI Multiple SAPIC Table if (0 > result) {
*/ printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
static void __init return result;
acpi_parse_msapic (acpi_sapic_t *msapic) }
{
acpi_entry_iosapic_t *iosapic;
char *p, *end;
/* Base address of IPI Message Block */ /* I/O APIC */
ipi_base_addr = (unsigned long) ioremap(msapic->interrupt_block, 0);
p = (char *) (msapic + 1); result = acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic);
end = p + (msapic->header.length - sizeof(acpi_sapic_t)); if (1 > result) {
printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n");
return ((result == 0) ? -ENODEV : result);
}
while (p < end) { /* System-Level Interrupt Routing */
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: result = acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src);
acpi_legacy_irq(p); if (0 > result) {
break; printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n");
return result;
}
case ACPI_ENTRY_PLATFORM_INT_SOURCE: result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
acpi_platform(p); if (0 > result) {
break; printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
return result;
}
default: result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
break; if (0 > result) {
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
return result;
} }
/* Move to next table entry. */ #ifdef CONFIG_SERIAL_ACPI
p += p[1]; /*
* 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);
/* Make bootup pretty */ return 0;
printk(" %d CPUs available, %d CPUs total\n", available_cpus, total_cpus);
} }
/* --------------------------------------------------------------------------
PCI Interrupt Routing
-------------------------------------------------------------------------- */
int __init int __init
acpi_parse (acpi_rsdp_t *rsdp) acpi_get_prt (struct pci_vector_struct **vectors, int *count)
{ {
# ifdef CONFIG_ACPI struct pci_vector_struct *vector = NULL;
acpi_rsdt_t *rsdt; struct list_head *node = NULL;
acpi_desc_table_hdr_t *hdrp; struct acpi_prt_entry *entry = NULL;
long tables, i; int i = 0;
if (strncmp(rsdp->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { if (!vectors || !count)
printk("Uh-oh, ACPI RSDP signature incorrect!\n"); return -EINVAL;
return 0;
}
rsdt = __va(rsdp->rsdt); *vectors = NULL;
if (strncmp(rsdt->header.signature, ACPI_RSDT_SIG, ACPI_RSDT_SIG_LEN)) { *count = 0;
printk("Uh-oh, ACPI RDST signature incorrect!\n");
return 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, /* Allocate vectors */
rsdt->header.oem_revision >> 16, rsdt->header.oem_revision & 0xffff);
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; /* Convert PRT entries to IOSAPIC PCI vectors */
for (i = 0; i < tables; i++) {
hdrp = (acpi_desc_table_hdr_t *) __va(rsdt->entry_ptrs[i]);
/* Only interested int the MSAPIC table for now ... */ vector = *vectors;
if (strncmp(hdrp->signature, ACPI_SAPIC_SIG, ACPI_SAPIC_SIG_LEN) != 0)
continue;
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 int __init
if (available_cpus == 0) { acpi_get_interrupt_model (int *type)
printk("ACPI: Found 0 CPUS; assuming 1\n"); {
available_cpus = 1; /* We've got at least one of these, no? */ if (!type)
} return -EINVAL;
smp_boot_data.cpu_count = total_cpus;
# endif *type = ACPI_INT_MODEL_IOSAPIC;
# endif /* CONFIG_ACPI */
return 1; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment