Commit 6405f55a authored by Andy Grover's avatar Andy Grover

ACPI PCI IRQ Improvements:

We now can boot without MPS and PIRQ!
ACPI finds IO APICs, Local apics (CPUS), interrupt source overrides,
the works. (Paul Diefenbaugh) Mucho thanks to Dominik Brodowski and
the whole acpi-devel gang.

(Note, I think this conflicts slightly with Pavel's code in i386/kernel/acpi.c
but I'd like to get this applied, and then I'm working with Pavel to hopefully
get the ACPI and swsusp stuff working properly.)
parent 3bd5d3cd
/*
* acpi.c - Architecture-Specific Low-Level ACPI Support
*
* Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2001 Jun Nakajima <jun.nakajima@intel.com>
* Copyright (C) 2001 Patrick Mochel <mochel@osdl.org>
*
......@@ -44,16 +44,10 @@
#include <asm/pgalloc.h>
#include <asm/io_apic.h>
#include <asm/tlbflush.h>
#define ACPI_C
#include <asm/suspend.h>
#define PREFIX "ACPI: "
extern struct acpi_boot_flags acpi_boot;
int acpi_mp_config = 0;
/* --------------------------------------------------------------------------
Boot-time Configuration
......@@ -61,6 +55,9 @@ int acpi_mp_config = 0;
#ifdef CONFIG_ACPI_BOOT
enum acpi_irq_model_id acpi_irq_model;
/*
* Use reserved fixmap pages for physical-to-virtual mappings of ACPI tables.
* Note that the same range is used for each table, so tables that need to
......@@ -106,65 +103,58 @@ __acpi_map_table (
#ifdef CONFIG_X86_LOCAL_APIC
static int total_cpus __initdata = 0;
int acpi_lapic;
/* From mpparse.c */
extern void __init MP_processor_info(struct mpc_config_processor *);
extern void __init MP_ioapic_info (struct mpc_config_ioapic *);
extern void __init MP_lintsrc_info(struct mpc_config_lintsrc *);
static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
int __init
acpi_parse_lapic (
acpi_table_entry_header *header)
static int __init
acpi_parse_madt (
unsigned long phys_addr,
unsigned long size)
{
struct acpi_table_lapic *cpu = NULL;
struct mpc_config_processor processor;
struct acpi_table_madt *madt = NULL;
cpu = (struct acpi_table_lapic*) header;
if (!cpu)
if (!phys_addr || !size)
return -EINVAL;
acpi_table_print_madt_entry(header);
if (!cpu->flags.enabled) {
printk(KERN_INFO "Processor #%d disabled\n", cpu->id);
return 0;
}
if (cpu->id >= MAX_APICS) {
printk(KERN_WARNING "Processor #%d invalid (max %d)\n",
cpu->id, MAX_APICS);
madt = (struct acpi_table_madt *) __acpi_map_table(phys_addr, size);
if (!madt) {
printk(KERN_WARNING PREFIX "Unable to map MADT\n");
return -ENODEV;
}
/*
* Fill in the info we want to save. Not concerned about
* the processor ID. Processor features aren't present in
* the table.
*/
processor.mpc_type = MP_PROCESSOR;
processor.mpc_apicid = cpu->id;
processor.mpc_cpuflag = CPU_ENABLED;
if (cpu->id == boot_cpu_physical_apicid) {
/* TBD: Circular reference trying to establish BSP */
processor.mpc_cpuflag |= CPU_BOOTPROCESSOR;
}
processor.mpc_cpufeature = (boot_cpu_data.x86 << 8)
| (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask;
processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
processor.mpc_reserved[0] = 0;
processor.mpc_reserved[1] = 0;
processor.mpc_apicver = 0x10; /* Integrated APIC */
if (madt->lapic_address)
acpi_lapic_addr = (u64) madt->lapic_address;
printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n",
madt->lapic_address);
return 0;
}
static int __init
acpi_parse_lapic (
acpi_table_entry_header *header)
{
struct acpi_table_lapic *processor = NULL;
MP_processor_info(&processor);
processor = (struct acpi_table_lapic*) header;
if (!processor)
return -EINVAL;
acpi_table_print_madt_entry(header);
total_cpus++;
mp_register_lapic (
processor->id, /* APIC ID */
processor->flags.enabled); /* Enabled? */
return 0;
}
int __init
static int __init
acpi_parse_lapic_addr_ovr (
acpi_table_entry_header *header)
{
......@@ -174,118 +164,80 @@ acpi_parse_lapic_addr_ovr (
if (!lapic_addr_ovr)
return -EINVAL;
/* TBD: Support local APIC address override entries */
acpi_lapic_addr = lapic_addr_ovr->address;
return 0;
}
int __init
static int __init
acpi_parse_lapic_nmi (
acpi_table_entry_header *header)
{
struct acpi_table_lapic_nmi *lacpi_nmi = NULL;
struct acpi_table_lapic_nmi *lapic_nmi = NULL;
lacpi_nmi = (struct acpi_table_lapic_nmi*) header;
if (!lacpi_nmi)
lapic_nmi = (struct acpi_table_lapic_nmi*) header;
if (!lapic_nmi)
return -EINVAL;
acpi_table_print_madt_entry(header);
/* TBD: Support lapic_nmi entries */
if (lapic_nmi->lint != 1)
printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n");
return 0;
}
#endif /*CONFIG_X86_LOCAL_APIC*/
#ifdef CONFIG_X86_IO_APIC
int __init
int acpi_ioapic;
static int __init
acpi_parse_ioapic (
acpi_table_entry_header *header)
{
struct acpi_table_ioapic *ioapic = NULL;
/*
struct mpc_config_ioapic mp_ioapic;
struct IO_APIC_reg_01 reg_01;
*/
ioapic = (struct acpi_table_ioapic*) header;
if (!ioapic)
return -EINVAL;
acpi_table_print_madt_entry(header);
/*
* Cobble up an entry for the IOAPIC (just as we do for LAPIC entries).
* Note that we aren't doing anything with ioapic->vector, and
* mpc_apicver gets read directly from ioapic.
*/
/*
* TBD: Complete I/O APIC support.
*
mp_ioapic.mpc_type = MP_IOAPIC;
mp_ioapic.mpc_apicid = ioapic->id;
mp_ioapic.mpc_flags = MPC_APIC_USABLE;
mp_ioapic.mpc_apicaddr = ioapic->address;
set_fixmap_nocache(nr_ioapics + FIX_IO_APIC_BASE_0,
mp_ioapic.mpc_apicaddr);
printk("mapped IOAPIC to %08lx (%08lx)\n",
__fix_to_virt(nr_ioapics), mp_ioapic.mpc_apicaddr);
*(int *)&reg_01 = io_apic_read(nr_ioapics, 1);
mp_ioapic.mpc_apicver = reg_01.version;
MP_ioapic_info(&mp_ioapic);
*/
mp_register_ioapic (
ioapic->id,
ioapic->address,
ioapic->global_irq_base);
return 0;
}
int __init
static int __init
acpi_parse_int_src_ovr (
acpi_table_entry_header *header)
{
struct acpi_table_int_src_ovr *int_src_ovr = NULL;
/*
struct mpc_config_intsrc my_intsrc;
int i = 0;
*/
struct acpi_table_int_src_ovr *intsrc = NULL;
int_src_ovr = (struct acpi_table_int_src_ovr*) header;
if (!int_src_ovr)
intsrc = (struct acpi_table_int_src_ovr*) header;
if (!intsrc)
return -EINVAL;
acpi_table_print_madt_entry(header);
/*
* TBD: Complete I/O APIC support.
*
my_intsrc.mpc_type = MP_INTSRC;
my_intsrc.mpc_irqtype = mp_INT;
my_intsrc.mpc_irqflag = *(unsigned short*)(&(int_src_ovr->flags));
my_intsrc.mpc_srcbus = int_src_ovr->bus;
my_intsrc.mpc_srcbusirq = int_src_ovr->bus_irq;
my_intsrc.mpc_dstapic = 0;
my_intsrc.mpc_dstirq = int_src_ovr->global_irq;
for (i = 0; i < mp_irq_entries; i++) {
if (mp_irqs[i].mpc_srcbusirq == my_intsrc.mpc_srcbusirq) {
mp_irqs[i] = my_intsrc;
break;
}
}
*/
mp_override_legacy_irq (
intsrc->bus_irq,
intsrc->flags.polarity,
intsrc->flags.trigger,
intsrc->global_irq);
return 0;
}
int __init
static int __init
acpi_parse_nmi_src (
acpi_table_entry_header *header)
{
......@@ -297,45 +249,14 @@ acpi_parse_nmi_src (
acpi_table_print_madt_entry(header);
/* TBD: Support nimsrc entries */
/* TBD: Support nimsrc entries? */
return 0;
}
#endif /*CONFIG_X86_IO_APIC*/
int __init
acpi_parse_madt (
unsigned long phys_addr,
unsigned long size)
{
struct acpi_table_madt *madt = NULL;
if (!phys_addr || !size)
return -EINVAL;
madt = (struct acpi_table_madt *) __acpi_map_table(phys_addr, size);
if (!madt) {
printk(KERN_WARNING PREFIX "Unable to map MADT\n");
return -ENODEV;
}
#ifdef CONFIG_X86_LOCAL_APIC
if (madt->lapic_address)
mp_lapic_addr = madt->lapic_address;
else
mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
#endif /*CONFIG_X86_LOCAL_APIC*/
printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n",
madt->lapic_address);
return 0;
}
static unsigned long __init
acpi_scan_rsdp (
unsigned long start,
......@@ -349,7 +270,7 @@ acpi_scan_rsdp (
* RSDP signature.
*/
for (offset = 0; offset < length; offset += 16) {
if (0 != strncmp((char *) (start + offset), "RSD PTR ", sig_len))
if (strncmp((char *) (start + offset), "RSD PTR ", sig_len))
continue;
return (start + offset);
}
......@@ -358,25 +279,20 @@ acpi_scan_rsdp (
}
int __init
acpi_find_rsdp (
unsigned long *rsdp_phys)
unsigned long __init
acpi_find_rsdp (void)
{
if (!rsdp_phys)
return -EINVAL;
unsigned long rsdp_phys = 0;
/*
* Scan memory looking for the RSDP signature. First search EBDA (low
* memory) paragraphs and then search upper memory (E0000-FFFFF).
*/
(*rsdp_phys) = acpi_scan_rsdp (0, 0x400);
if (!(*rsdp_phys))
(*rsdp_phys) = acpi_scan_rsdp (0xE0000, 0xFFFFF);
if (!(*rsdp_phys))
return -ENODEV;
rsdp_phys = acpi_scan_rsdp (0, 0x400);
if (!rsdp_phys)
rsdp_phys = acpi_scan_rsdp (0xE0000, 0xFFFFF);
return 0;
return rsdp_phys;
}
......@@ -386,13 +302,20 @@ acpi_boot_init (
{
int result = 0;
/* Initialize the ACPI boot-time table parser */
/*
* The default interrupt routing model is PIC (8259). This gets
* overriden if IOAPICs are enumerated (below).
*/
acpi_irq_model = ACPI_IRQ_MODEL_PIC;
/*
* Initialize the ACPI boot-time table parser.
*/
result = acpi_table_init(cmdline);
if (0 != result)
if (result)
return result;
#ifdef CONFIG_X86_LOCAL_APIC
#ifdef CONFIG_X86_IO_APIC
/*
* MADT
......@@ -402,86 +325,100 @@ acpi_boot_init (
* information -- the successor to MPS tables.
*/
if (!acpi_boot.madt) {
printk(KERN_INFO PREFIX "MADT parsing disabled via command-line\n");
return 0;
}
result = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
if (0 == result) {
if (!result) {
printk(KERN_WARNING PREFIX "MADT not present\n");
return 0;
}
else if (0 > result) {
else if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing MADT\n");
return result;
}
else if (1 < result)
else if (result > 1)
printk(KERN_WARNING PREFIX "Multiple MADT tables exist\n");
/* Local APIC */
/*
* Local APIC
* ----------
* Note that the LAPIC address is obtained from the MADT (32-bit value)
* and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
*/
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
if (0 > result) {
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
return result;
}
mp_register_lapic_address(acpi_lapic_addr);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
if (1 > result) {
printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n");
if (!result) {
printk(KERN_ERR PREFIX "No LAPIC entries present\n");
/* TBD: Cleanup to allow fallback to MPS */
return -ENODEV;
}
else if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
if (0 > result) {
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
/* I/O APIC */
acpi_lapic = 1;
#endif /*CONFIG_X86_LOCAL_APIC*/
#ifdef CONFIG_X86_IO_APIC
/*
* I/O APIC
* --------
*/
result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic);
if (1 > result) {
printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n");
if (!result) {
printk(KERN_ERR PREFIX "No IOAPIC entries present\n");
return -ENODEV;
}
else if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n");
return result;
}
acpi_mp_config = 1;
/*
* TBD: Complete I/O APIC support.
*
construct_default_ACPI_table();
*/
/* Build a default routing table for legacy (ISA) interrupts. */
mp_config_acpi_legacy_irqs();
result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
if (0 > result) {
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
if (0 > result) {
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
/* Make boot-up look pretty */
printk("%d CPUs total\n", total_cpus);
acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;
acpi_ioapic = 1;
#endif /*CONFIG_X86_IO_APIC*/
#endif /*CONFIG_X86_LOCAL_APIC*/
#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 (ACPI Bus driver) 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_X86_LOCAL_APIC
if (acpi_lapic && acpi_ioapic)
smp_found_config = 1;
#endif
return 0;
}
......@@ -489,23 +426,6 @@ acpi_boot_init (
#endif /*CONFIG_ACPI_BOOT*/
int __init
acpi_get_interrupt_model (
int *type)
{
if (!type)
return -EINVAL;
#ifdef CONFIG_X86_IO_APIC
*type = ACPI_INT_MODEL_IOAPIC;
#else
*type = ACPI_INT_MODEL_PIC;
#endif
return 0;
}
/* --------------------------------------------------------------------------
Low-Level Sleep Support
-------------------------------------------------------------------------- */
......@@ -625,33 +545,4 @@ void __init acpi_reserve_bootmem(void)
printk(KERN_DEBUG "ACPI: have wakeup address 0x%8.8lx\n", acpi_wakeup_address);
}
/*
* (KG): Since we affect stack here, we make this function as flat and easy
* as possible in order to not provoke gcc to use local variables on the stack.
* Note that on resume, all (expect nosave) variables will have the state from
* the time of writing (suspend_save_image) and the registers (including the
* stack pointer, but excluding the instruction pointer) will be loaded with
* the values saved at save_processor_context() time.
*/
void do_suspend_magic(int resume)
{
/* DANGER WILL ROBINSON!
*
* If this function is too difficult for gcc to optimize, it will crash and burn!
* see above.
*
* DO NOT TOUCH.
*/
if (!resume) {
save_processor_context();
acpi_save_register_state((unsigned long)&&acpi_sleep_done);
acpi_enter_sleep_state(3);
return;
}
acpi_sleep_done:
restore_processor_context();
printk("CPU context restored...\n");
}
#endif /*CONFIG_ACPI_SLEEP*/
......@@ -17,6 +17,7 @@
* thanks to Eric Gilmore
* and Rolf G. Tews
* for testing these extensively
* Paul Diefenbaugh : Added full ACPI support
*/
#include <linux/mm.h>
......@@ -29,6 +30,7 @@
#include <linux/smp_lock.h>
#include <linux/mc146818rtc.h>
#include <linux/compiler.h>
#include <linux/acpi.h>
#include <asm/io.h>
#include <asm/smp.h>
......@@ -1111,6 +1113,10 @@ static void __init setup_ioapic_ids_from_mpc (void)
unsigned char old_id;
unsigned long flags;
if (acpi_ioapic)
/* This gets done during IOAPIC enumeration for ACPI. */
return;
if (clustered_apic_mode)
/* We don't have a good way to do this yet - hack */
phys_id_present_map = (u_long) 0xf;
......@@ -1699,8 +1705,7 @@ void __init setup_IO_APIC(void)
printk("ENABLING IO-APIC IRQs\n");
/*
* Set up the IO-APIC IRQ routing table by parsing the MP-BIOS
* mptable:
* Set up IO-APIC IRQ routing.
*/
setup_ioapic_ids_from_mpc();
sync_Arb_IDs();
......@@ -1709,3 +1714,159 @@ void __init setup_IO_APIC(void)
check_timer();
print_IO_APIC();
}
/* --------------------------------------------------------------------------
ACPI-based IOAPIC Configuration
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_BOOT
#define IO_APIC_MAX_ID 15
int __init io_apic_get_unique_id (int ioapic, int apic_id)
{
struct IO_APIC_reg_00 reg_00;
static unsigned long apic_id_map = 0;
unsigned long flags;
int i = 0;
/*
* The P4 platform supports up to 256 APIC IDs on two separate APIC
* buses (one for LAPICs, one for IOAPICs), where predecessors only
* supports up to 16 on one shared APIC bus.
*
* TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full
* advantage of new APIC bus architecture.
*/
if (!apic_id_map)
apic_id_map = phys_cpu_present_map;
spin_lock_irqsave(&ioapic_lock, flags);
*(int *)&reg_00 = io_apic_read(ioapic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
if (apic_id >= IO_APIC_MAX_ID) {
printk(KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying "
"%d\n", ioapic, apic_id, reg_00.ID);
apic_id = reg_00.ID;
}
/*
* Every APIC in a system must have a unique ID or we get lots of nice
* 'stuck on smp_invalidate_needed IPI wait' messages.
*/
if (apic_id_map & (1 << apic_id)) {
for (i = 0; i < IO_APIC_MAX_ID; i++) {
if (!(apic_id_map & (1 << i)))
break;
}
if (i == IO_APIC_MAX_ID)
panic("Max apic_id exceeded!\n");
printk(KERN_WARNING "IOAPIC[%d]: apic_id %d already used, "
"trying %d\n", ioapic, apic_id, i);
apic_id = i;
}
apic_id_map |= (1 << apic_id);
if (reg_00.ID != apic_id) {
reg_00.ID = apic_id;
spin_lock_irqsave(&ioapic_lock, flags);
io_apic_write(ioapic, 0, *(int *)&reg_00);
*(int *)&reg_00 = io_apic_read(ioapic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
/* Sanity check */
if (reg_00.ID != apic_id)
panic("IOAPIC[%d]: Unable change apic_id!\n", ioapic);
}
printk(KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id);
return apic_id;
}
int __init io_apic_get_version (int ioapic)
{
struct IO_APIC_reg_01 reg_01;
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
*(int *)&reg_01 = io_apic_read(ioapic, 1);
spin_unlock_irqrestore(&ioapic_lock, flags);
return reg_01.version;
}
int __init io_apic_get_redir_entries (int ioapic)
{
struct IO_APIC_reg_01 reg_01;
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
*(int *)&reg_01 = io_apic_read(ioapic, 1);
spin_unlock_irqrestore(&ioapic_lock, flags);
return reg_01.entries;
}
int io_apic_set_pci_routing (int ioapic, int pin, int irq)
{
struct IO_APIC_route_entry entry;
unsigned long flags;
if (!IO_APIC_IRQ(irq)) {
printk(KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0/n",
ioapic);
return -EINVAL;
}
/*
* Generate a PCI IRQ routing entry and program the IOAPIC accordingly.
* Note that we mask (disable) IRQs now -- these get enabled when the
* corresponding device driver registers for this IRQ.
*/
memset(&entry,0,sizeof(entry));
entry.delivery_mode = dest_LowestPrio;
entry.dest_mode = INT_DELIVERY_MODE;
entry.dest.logical.logical_dest = TARGET_CPUS;
entry.mask = 1; /* Disabled (masked) */
entry.trigger = 1; /* Level sensitive */
entry.polarity = 1; /* Low active */
add_pin_to_irq(irq, ioapic, pin);
entry.vector = assign_irq_vector(irq);
printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> "
"IRQ %d)\n", ioapic,
mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq);
irq_desc[irq].handler = &ioapic_level_irq_type;
set_intr_gate(entry.vector, interrupt[irq]);
if (!ioapic && (irq < 16))
disable_8259A_irq(irq);
spin_lock_irqsave(&ioapic_lock, flags);
io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1));
io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0));
spin_unlock_irqrestore(&ioapic_lock, flags);
return entry.vector;
}
#endif /*CONFIG_ACPI_BOOT*/
......@@ -9,12 +9,14 @@
* Erich Boleyn : MP v1.4 and additional changes.
* Alan Cox : Added EBDA scanning
* Ingo Molnar : various cleanups and rewrites
* Maciej W. Rozycki : Bits for default MP configurations
* Maciej W. Rozycki: Bits for default MP configurations
* Paul Diefenbaugh: Added full ACPI support
*/
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/config.h>
#include <linux/bootmem.h>
......@@ -27,6 +29,7 @@
#include <asm/mtrr.h>
#include <asm/mpspec.h>
#include <asm/pgalloc.h>
#include <asm/io_apic.h>
/* Have we found an MP table */
int smp_found_config;
......@@ -426,10 +429,10 @@ static int __init smp_read_mpc(struct mp_config_table *mpc)
printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
/*
* Save the local APIC address (it might be non-default), but only
* if we're not using the ACPI tables.
* Save the local APIC address (it might be non-default) -- but only
* if we're not using ACPI.
*/
if (!acpi_mp_config)
if (!acpi_lapic)
mp_lapic_addr = mpc->mpc_lapic;
if (clustered_apic_mode && mpc->mpc_oemptr) {
......@@ -448,9 +451,8 @@ static int __init smp_read_mpc(struct mp_config_table *mpc)
{
struct mpc_config_processor *m=
(struct mpc_config_processor *)mpt;
/* ACPI may already have provided this one for us */
if (!acpi_mp_config)
/* ACPI may have already provided this data */
if (!acpi_lapic)
MP_processor_info(m);
mpt += sizeof(*m);
count += sizeof(*m);
......@@ -672,6 +674,19 @@ void __init get_smp_config (void)
{
struct intel_mp_floating *mpf = mpf_found;
/*
* ACPI may be used to obtain the entire SMP configuration or just to
* enumerate/configure processors (CONFIG_ACPI_HT_ONLY). Note that
* 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");
return;
}
else if (acpi_lapic)
printk(KERN_INFO "Using ACPI for processor (LAPIC) configuration information\n");
printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
if (mpf->mpf_feature2 & (1<<7)) {
printk(" IMCR and PIC compatibility mode.\n");
......@@ -834,3 +849,315 @@ void __init find_smp_config (void)
#endif
}
/* --------------------------------------------------------------------------
ACPI-based MP Configuration
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_BOOT
void __init mp_register_lapic_address (
u64 address)
{
mp_lapic_addr = (unsigned long) address;
set_fixmap_nocache(FIX_APIC_BASE, mp_lapic_addr);
if (boot_cpu_physical_apicid == -1U)
boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
Dprintk("Boot CPU = %d\n", boot_cpu_physical_apicid);
}
void __init mp_register_lapic (
u8 id,
u8 enabled)
{
struct mpc_config_processor processor;
int boot_cpu = 0;
if (id >= MAX_APICS) {
printk(KERN_WARNING "Processor #%d invalid (max %d)\n",
id, MAX_APICS);
return;
}
if (id == boot_cpu_physical_apicid)
boot_cpu = 1;
processor.mpc_type = MP_PROCESSOR;
processor.mpc_apicid = id;
processor.mpc_apicver = 0x10; /* TBD: lapic version */
processor.mpc_cpuflag = (enabled ? CPU_ENABLED : 0);
processor.mpc_cpuflag |= (boot_cpu ? CPU_BOOTPROCESSOR : 0);
processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) |
(boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask;
processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
processor.mpc_reserved[0] = 0;
processor.mpc_reserved[1] = 0;
MP_processor_info(&processor);
}
#ifdef CONFIG_X86_IO_APIC
#define MP_ISA_BUS 0
#define MP_MAX_IOAPIC_PIN 127
struct mp_ioapic_routing {
int apic_id;
int irq_start;
int irq_end;
u32 pin_programmed[4];
} mp_ioapic_routing[MAX_IO_APICS];
static int __init mp_find_ioapic (
int irq)
{
int i = 0;
/* Find the IOAPIC that manages this IRQ. */
for (i = 0; i < nr_ioapics; i++) {
if ((irq >= mp_ioapic_routing[i].irq_start)
&& (irq <= mp_ioapic_routing[i].irq_end))
return i;
}
printk(KERN_ERR "ERROR: Unable to locate IOAPIC for IRQ %d/n", irq);
return -1;
}
void __init mp_register_ioapic (
u8 id,
u32 address,
u32 irq_base)
{
int idx = 0;
if (nr_ioapics >= MAX_IO_APICS) {
printk(KERN_ERR "ERROR: Max # of I/O APICs (%d) exceeded "
"(found %d)\n", MAX_IO_APICS, nr_ioapics);
panic("Recompile kernel with bigger MAX_IO_APICS!\n");
}
if (!address) {
printk(KERN_ERR "WARNING: Bogus (zero) I/O APIC address"
" found in MADT table, skipping!\n");
return;
}
idx = nr_ioapics++;
mp_ioapics[idx].mpc_type = MP_IOAPIC;
mp_ioapics[idx].mpc_flags = MPC_APIC_USABLE;
mp_ioapics[idx].mpc_apicaddr = address;
set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
mp_ioapics[idx].mpc_apicid = io_apic_get_unique_id(idx, id);
mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx);
/*
* Build basic IRQ lookup table to facilitate irq->io_apic lookups
* and to prevent reprogramming of IOAPIC pins (PCI IRQs).
*/
mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mpc_apicid;
mp_ioapic_routing[idx].irq_start = irq_base;
mp_ioapic_routing[idx].irq_end = irq_base +
io_apic_get_redir_entries(idx);
printk("IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, "
"IRQ %d-%d\n", idx, mp_ioapics[idx].mpc_apicid,
mp_ioapics[idx].mpc_apicver, mp_ioapics[idx].mpc_apicaddr,
mp_ioapic_routing[idx].irq_start,
mp_ioapic_routing[idx].irq_end);
return;
}
void __init mp_override_legacy_irq (
u8 bus_irq,
u8 polarity,
u8 trigger,
u32 global_irq)
{
struct mpc_config_intsrc intsrc;
int i = 0;
int found = 0;
int ioapic = -1;
int pin = -1;
/*
* Convert 'global_irq' to 'ioapic.pin'.
*/
ioapic = mp_find_ioapic(global_irq);
if (ioapic < 0)
return;
pin = global_irq - mp_ioapic_routing[ioapic].irq_start;
/*
* TBD: This check is for faulty timer entries, where the override
* erroneously sets the trigger to level, resulting in a HUGE
* increase of timer interrupts!
*/
if ((bus_irq == 0) && (global_irq == 2) && (trigger == 3))
trigger = 1;
intsrc.mpc_type = MP_INTSRC;
intsrc.mpc_irqtype = mp_INT;
intsrc.mpc_irqflag = (trigger << 2) | polarity;
intsrc.mpc_srcbus = MP_ISA_BUS;
intsrc.mpc_srcbusirq = bus_irq; /* IRQ */
intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; /* APIC ID */
intsrc.mpc_dstirq = pin; /* INTIN# */
Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, %d-%d\n",
intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3,
(intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus,
intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq);
/*
* If an existing [IOAPIC.PIN -> IRQ] routing entry exists we override it.
* Otherwise create a new entry (e.g. global_irq == 2).
*/
for (i = 0; i < mp_irq_entries; i++) {
if ((mp_irqs[i].mpc_dstapic == intsrc.mpc_dstapic)
&& (mp_irqs[i].mpc_dstirq == intsrc.mpc_dstirq)) {
mp_irqs[i] = intsrc;
found = 1;
break;
}
}
if (!found) {
mp_irqs[mp_irq_entries] = intsrc;
if (++mp_irq_entries == MAX_IRQ_SOURCES)
panic("Max # of irq sources exceeded!\n");
}
return;
}
void __init mp_config_acpi_legacy_irqs (void)
{
struct mpc_config_intsrc intsrc;
int i = 0;
int ioapic = -1;
/*
* Fabricate the legacy ISA bus (bus #31).
*/
mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA;
Dprintk("Bus #%d is ISA\n", MP_ISA_BUS);
/*
* Locate the IOAPIC that manages the ISA IRQs (0-15).
*/
ioapic = mp_find_ioapic(0);
if (ioapic < 0)
return;
intsrc.mpc_type = MP_INTSRC;
intsrc.mpc_irqflag = 0; /* Conforming */
intsrc.mpc_srcbus = MP_ISA_BUS;
intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid;
/*
* Use the default configuration for the IRQs 0-15. These may be
* overriden by (MADT) interrupt source override entries.
*/
for (i = 0; i < 16; i++) {
if (i == 2) continue; /* Don't connect IRQ2 */
intsrc.mpc_irqtype = i ? mp_INT : mp_ExtINT; /* 8259A to #0 */
intsrc.mpc_srcbusirq = i; /* Identity mapped */
intsrc.mpc_dstirq = i;
Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, "
"%d-%d\n", intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3,
(intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus,
intsrc.mpc_srcbusirq, intsrc.mpc_dstapic,
intsrc.mpc_dstirq);
mp_irqs[mp_irq_entries] = intsrc;
if (++mp_irq_entries == MAX_IRQ_SOURCES)
panic("Max # of irq sources exceeded!\n");
}
return;
}
#endif /*CONFIG_X86_IO_APIC*/
#ifdef CONFIG_ACPI_PCI
void __init mp_parse_prt (void)
{
struct list_head *node = NULL;
struct acpi_prt_entry *entry = NULL;
int vector = 0;
int ioapic = -1;
int ioapic_pin = 0;
int irq = 0;
int idx, bit = 0;
/*
* Parsing through the PCI Interrupt Routing Table (PRT) and program
* routing for all static (IOAPIC-direct) entries.
*/
list_for_each(node, &acpi_prt.entries) {
entry = list_entry(node, struct acpi_prt_entry, node);
/* We're only interested in static (non-link) entries. */
if (entry->link.handle)
continue;
irq = entry->link.index;
ioapic = mp_find_ioapic(irq);
if (ioapic < 0)
continue;
ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start;
/*
* Avoid pin reprogramming. PRTs typically include entries
* with redundant pin->irq mappings (but unique PCI devices);
* we only only program the IOAPIC on the first.
*/
bit = ioapic_pin % 32;
idx = (ioapic_pin < 32) ? 0 : (ioapic_pin / 32);
if (idx > 3) {
printk(KERN_ERR "Invalid reference to IOAPIC pin "
"%d-%d\n", mp_ioapic_routing[ioapic].apic_id,
ioapic_pin);
continue;
}
if ((1<<bit) & mp_ioapic_routing[ioapic].pin_programmed[idx]) {
printk(KERN_DEBUG "Pin %d-%d already programmed\n",
mp_ioapic_routing[ioapic].apic_id, ioapic_pin);
entry->irq = irq;
continue;
}
mp_ioapic_routing[ioapic].pin_programmed[idx] |= (1<<bit);
vector = io_apic_set_pci_routing(ioapic, ioapic_pin, irq);
if (vector)
entry->irq = irq;
printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> vector 0x%02x"
" -> IRQ %d\n", entry->id.segment, entry->id.bus,
entry->id.device, ('A' + entry->pin),
mp_ioapic_routing[ioapic].apic_id, ioapic_pin, vector,
entry->irq);
}
return;
}
#endif /*CONFIG_ACPI_PCI*/
#endif /*CONFIG_ACPI_BOOT*/
......@@ -916,17 +916,11 @@ void __init setup_arch(char **cmdline_p)
paging_init();
#ifdef CONFIG_ACPI_BOOT
/*
* Initialize the ACPI boot-time table parser (gets the RSDP and SDT).
* Must do this after paging_init (due to reliance on fixmap, and thus
* the bootmem allocator) but before get_smp_config (to allow parsing
* of MADT).
* Parse the ACPI tables for possible boot-time SMP configuration.
*/
acpi_boot_init(*cmdline_p);
#endif
#ifdef CONFIG_X86_LOCAL_APIC
/*
* get boot-time SMP configuration:
*/
if (smp_found_config)
get_smp_config();
#endif
......
/*
* pci_bind.c - ACPI PCI Device Binding ($Revision: 2 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pci.h>
#include <linux/acpi.h>
#include "acpi_bus.h"
#include "acpi_drivers.h"
#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME ("pci_bind")
#define PREFIX "ACPI: "
struct acpi_pci_data {
acpi_pci_id id;
struct pci_bus *bus;
struct pci_dev *dev;
};
void
acpi_pci_data_handler (
acpi_handle handle,
u32 function,
void *context)
{
ACPI_FUNCTION_TRACE("acpi_pci_data_handler");
/* TBD: Anything we need to do here? */
return_VOID;
}
/**
* acpi_os_get_pci_id
* ------------------
* This function is used by the ACPI Interpreter (a.k.a. Core Subsystem)
* to resolve PCI information for ACPI-PCI devices defined in the namespace.
* This typically occurs when resolving PCI operation region information.
*/
acpi_status
acpi_os_get_pci_id (
acpi_handle handle,
acpi_pci_id *id)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_device *device = NULL;
struct acpi_pci_data *data = NULL;
ACPI_FUNCTION_TRACE("acpi_os_get_pci_id");
if (!id)
return_ACPI_STATUS(AE_BAD_PARAMETER);
result = acpi_bus_get_device(handle, &device);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI Bus context for device %s\n",
acpi_device_bid(device)));
return_ACPI_STATUS(AE_NOT_EXIST);
}
status = acpi_get_data(handle, acpi_pci_data_handler, (void**) &data);
if (ACPI_FAILURE(status) || !data || !data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI-PCI context for device %s\n",
acpi_device_bid(device)));
return_ACPI_STATUS(status);
}
*id = data->id;
/*
id->segment = data->id.segment;
id->bus = data->id.bus;
id->device = data->id.device;
id->function = data->id.function;
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device %s has PCI address %02x:%02x:%02x.%02x\n",
acpi_device_bid(device), id->segment, id->bus,
id->device, id->function));
return_ACPI_STATUS(AE_OK);
}
int
acpi_pci_bind (
struct acpi_device *device)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_pci_data *data = NULL;
struct acpi_pci_data *pdata = NULL;
char pathname[PATHNAME_MAX] = {0};
acpi_buffer buffer = {PATHNAME_MAX, pathname};
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_bind");
if (!device || !device->parent)
return_VALUE(-EINVAL);
data = kmalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
if (!data)
return_VALUE(-ENOMEM);
memset(data, 0, sizeof(struct acpi_pci_data));
acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n",
pathname));
/*
* Segment & Bus
* -------------
* These are obtained via the parent device's ACPI-PCI context.
*/
status = acpi_get_data(device->parent->handle, acpi_pci_data_handler,
(void**) &pdata);
if (ACPI_FAILURE(status) || !pdata || !pdata->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI-PCI context for parent device %s\n",
acpi_device_bid(device->parent)));
result = -ENODEV;
goto end;
}
data->id.segment = pdata->id.segment;
data->id.bus = pdata->bus->number;
/*
* Device & Function
* -----------------
* These are simply obtained from the device's _ADR method. Note
* that a value of zero is valid.
*/
data->id.device = device->pnp.bus_address >> 16;
data->id.function = device->pnp.bus_address & 0xFFFF;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %02x:%02x:%02x.%02x\n",
data->id.segment, data->id.bus, data->id.device,
data->id.function));
/*
* TBD: Support slot devices (e.g. function=0xFFFF).
*/
/*
* Locate PCI Device
* -----------------
* Locate matching device in PCI namespace. If it doesn't exist
* this typically means that the device isn't currently inserted
* (e.g. docking station, port replicator, etc.).
*/
data->dev = pci_find_slot(data->id.bus, PCI_DEVFN(data->id.device, data->id.function));
if (!data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device %02x:%02x:%02x.%02x not present in PCI namespace\n",
data->id.segment, data->id.bus,
data->id.device, data->id.function));
result = -ENODEV;
goto end;
}
if (!data->dev->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device %02x:%02x:%02x.%02x has invalid 'bus' field\n",
data->id.segment, data->id.bus,
data->id.device, data->id.function));
result = -ENODEV;
goto end;
}
/*
* PCI Bridge?
* -----------
* If so, set the 'bus' field and install the 'bind' function to
* facilitate callbacks for all of its children.
*/
if (data->dev->subordinate) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device %02x:%02x:%02x.%02x is a PCI bridge\n",
data->id.segment, data->id.bus,
data->id.device, data->id.function));
data->bus = data->dev->subordinate;
device->ops.bind = acpi_pci_bind;
}
/*
* Attach ACPI-PCI Context
* -----------------------
* Thus binding the ACPI and PCI devices.
*/
status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to attach ACPI-PCI context to device %s\n",
acpi_device_bid(device)));
result = -ENODEV;
goto end;
}
/*
* PCI Routing Table
* -----------------
* Evaluate and parse _PRT, if exists. This code is independent of
* PCI bridges (above) to allow parsing of _PRT objects within the
* scope of non-bridge devices. Note that _PRTs within the scope of
* a PCI bridge assume the bridge's subordinate bus number.
*
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
*/
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
if (ACPI_SUCCESS(status)) {
if (data->bus) /* PCI-PCI bridge */
acpi_pci_irq_add_prt(device->handle, data->id.segment,
data->bus->number);
else /* non-bridge PCI device */
acpi_pci_irq_add_prt(device->handle, data->id.segment,
data->id.bus);
}
end:
if (result)
kfree(data);
return_VALUE(result);
}
int
acpi_pci_bind_root (
struct acpi_device *device,
acpi_pci_id *id,
struct pci_bus *bus)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_pci_data *data = NULL;
char pathname[PATHNAME_MAX] = {0};
acpi_buffer buffer = {PATHNAME_MAX, pathname};
ACPI_FUNCTION_TRACE("acpi_pci_bind_root");
if (!device || !id || !bus)
return_VALUE(-EINVAL);
data = kmalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
if (!data)
return_VALUE(-ENOMEM);
memset(data, 0, sizeof(struct acpi_pci_data));
data->id = *id;
data->bus = bus;
device->ops.bind = acpi_pci_bind;
acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to "
"%02x:%02x\n", pathname, id->segment, id->bus));
status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to attach ACPI-PCI context to device %s\n",
pathname));
result = -ENODEV;
goto end;
}
end:
if (result != 0)
kfree(data);
return_VALUE(result);
}
/*
* pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 7 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pci.h>
#include <linux/acpi.h>
#include "acpi_bus.h"
#include "acpi_drivers.h"
#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME ("pci_irq")
#define PREFIX "PCI: "
struct acpi_prt_list acpi_prt;
#ifdef CONFIG_X86
extern void eisa_set_level_irq(unsigned int irq);
#endif
/* --------------------------------------------------------------------------
PCI IRQ Routing Table (PRT) Support
-------------------------------------------------------------------------- */
static struct acpi_prt_entry *
acpi_pci_irq_find_prt_entry (
int segment,
int bus,
int device,
int pin)
{
struct list_head *node = NULL;
struct acpi_prt_entry *entry = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_irq_find_prt_entry");
/*
* Parse through all PRT entries looking for a match on the specified
* PCI device's segment, bus, device, and pin (don't care about func).
*
* TBD: Acquire/release lock
*/
list_for_each(node, &acpi_prt.entries) {
entry = list_entry(node, struct acpi_prt_entry, node);
if ((segment == entry->id.segment)
&& (bus == entry->id.bus)
&& (device == entry->id.device)
&& (pin == entry->pin)) {
return_PTR(entry);
}
}
return_PTR(NULL);
}
static int
acpi_pci_irq_add_entry (
acpi_handle handle,
int segment,
int bus,
acpi_pci_routing_table *prt)
{
struct acpi_prt_entry *entry = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_irq_add_entry");
if (!prt)
return_VALUE(-EINVAL);
entry = kmalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
if (!entry)
return_VALUE(-ENOMEM);
memset(entry, 0, sizeof(struct acpi_prt_entry));
entry->id.segment = segment;
entry->id.bus = bus;
entry->id.device = (prt->address >> 16) & 0xFFFF;
entry->id.function = prt->address & 0xFFFF;
entry->pin = prt->pin;
/*
* Type 1: Dynamic
* ---------------
* The 'source' field specifies the PCI interrupt link device used to
* configure the IRQ assigned to this slot|dev|pin. The 'source_index'
* indicates which resource descriptor in the resource template (of
* the link device) this interrupt is allocated from.
*
* NOTE: Don't query the Link Device for IRQ information at this time
* because Link Device enumeration may not have occurred yet
* (e.g. exists somewhere 'below' this _PRT entry in the ACPI
* namespace).
*/
if (prt->source[0]) {
acpi_get_handle(handle, prt->source, &entry->link.handle);
entry->link.index = prt->source_index;
}
/*
* Type 2: Static
* --------------
* The 'source' field is NULL, and the 'source_index' field specifies
* the IRQ value, which is hardwired to specific interrupt inputs on
* the interrupt controller.
*/
else
entry->link.index = prt->source_index;
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
" %02X:%02X:%02X[%c] -> %s[%d]\n",
entry->id.segment, entry->id.bus, entry->id.device,
('A' + entry->pin), prt->source, entry->link.index));
/* TBD: Acquire/release lock */
list_add_tail(&entry->node, &acpi_prt.entries);
acpi_prt.count++;
return_VALUE(0);
}
int
acpi_pci_irq_add_prt (
acpi_handle handle,
int segment,
int bus)
{
acpi_status status = AE_OK;
char pathname[PATHNAME_MAX] = {0};
acpi_buffer buffer = {0, NULL};
acpi_pci_routing_table *prt = NULL;
acpi_pci_routing_table *entry = NULL;
static int first_time = 1;
ACPI_FUNCTION_TRACE("acpi_pci_irq_add_prt");
if (first_time) {
acpi_prt.count = 0;
INIT_LIST_HEAD(&acpi_prt.entries);
first_time = 0;
}
/*
* NOTE: We're given a 'handle' to the _PRT object's parent device
* (either a PCI root bridge or PCI-PCI bridge).
*/
buffer.length = sizeof(pathname);
buffer.pointer = pathname;
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n",
pathname);
/*
* Evaluate this _PRT and add its entries to our global list (acpi_prt).
*/
buffer.length = 0;
buffer.pointer = NULL;
status = acpi_get_irq_routing_table(handle, &buffer);
if (status != AE_BUFFER_OVERFLOW) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n",
acpi_format_exception(status)));
return_VALUE(-ENODEV);
}
prt = kmalloc(buffer.length, GFP_KERNEL);
if (!prt)
return_VALUE(-ENOMEM);
memset(prt, 0, buffer.length);
buffer.pointer = prt;
status = acpi_get_irq_routing_table(handle, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n",
acpi_format_exception(status)));
kfree(buffer.pointer);
return_VALUE(-ENODEV);
}
entry = prt;
while (entry && (entry->length > 0)) {
acpi_pci_irq_add_entry(handle, segment, bus, entry);
entry = (acpi_pci_routing_table *)
((unsigned long) entry + entry->length);
}
kfree(prt);
return_VALUE(0);
}
/* --------------------------------------------------------------------------
PCI Interrupt Routing Support
-------------------------------------------------------------------------- */
static int
acpi_pci_irq_lookup (
int segment,
int bus,
int device,
int pin)
{
struct acpi_prt_entry *entry = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup");
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Searching for PRT entry for %02x:%02x:%02x[%c]\n",
segment, bus, device, ('A' + pin)));
entry = acpi_pci_irq_find_prt_entry(segment, bus, device, pin);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PRT entry not found\n"));
return_VALUE(0);
}
if (!entry->irq && entry->link.handle)
entry->irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index);
else if (!entry->irq) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid static routing entry (IRQ 0)\n"));
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", entry->irq));
return_VALUE(entry->irq);
}
static int
acpi_pci_irq_derive (
struct pci_dev *dev,
int pin)
{
struct pci_dev *bridge = dev;
int irq = 0;
ACPI_FUNCTION_TRACE("acpi_pci_irq_derive");
if (!dev)
return_VALUE(-EINVAL);
/*
* Attempt to derive an IRQ for this device from a parent bridge's
* PCI interrupt routing entry (a.k.a. the "bridge swizzle").
*/
while (!irq && (bridge = bridge->bus->self)) {
pin = (pin + PCI_SLOT(bridge->devfn)) % 4;
irq = acpi_pci_irq_lookup(0, bridge->bus->number, PCI_SLOT(bridge->devfn), pin);
};
if (!irq) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to derive IRQ for device %s\n", dev->slot_name));
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived IRQ %d\n", irq));
return_VALUE(irq);
}
int
acpi_pci_irq_enable (
struct pci_dev *dev)
{
int irq = 0;
u8 pin = 0;
static u16 irq_mask = 0;
ACPI_FUNCTION_TRACE("acpi_pci_irq_enable");
if (!dev)
return_VALUE(-EINVAL);
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No interrupt pin configured for device %s\n", dev->slot_name));
return_VALUE(0);
}
pin--;
if (!dev->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) 'bus' field\n"));
return_VALUE(-ENODEV);
}
/*
* First we check the PCI IRQ routing table (PRT) for an IRQ. PRT
* values override any BIOS-assinged IRQs set during boot.
*/
irq = acpi_pci_irq_lookup(0, dev->bus->number, PCI_SLOT(dev->devfn), pin);
if (irq)
dev->irq = irq;
/*
* If no PRT entry was found and the device wasn't assigned an IRQ
* during boot we'll try to derive an IRQ from the device's parent
* bridge.
*/
if (!dev->irq && dev->bus->self) {
irq = acpi_pci_irq_derive(dev, pin);
if (irq)
dev->irq = irq;
}
if (!dev->irq) {
printk(KERN_WARNING PREFIX "No IRQ known for interrupt pin %c of device %s\n", ('A' + pin), dev->slot_name);
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %s using IRQ %d\n", dev->slot_name, dev->irq));
/*
* Make sure all (legacy) PCI IRQs are set as level-triggered.
*/
#ifdef CONFIG_X86
if ((dev->irq < 16) && !((1 << dev->irq) & irq_mask)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Setting IRQ %d as level-triggered\n", dev->irq));
irq_mask |= (1 << dev->irq);
eisa_set_level_irq(dev->irq);
}
#endif
return_VALUE(dev->irq);
}
int __init
acpi_pci_irq_init (void)
{
struct pci_dev *dev = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_irq_init");
if (!acpi_prt.count) {
printk(KERN_WARNING PREFIX "ACPI tables contain no PCI IRQ "
"routing entries\n");
return_VALUE(-ENODEV);
}
/* Make sure all link devices have a valid IRQ. */
acpi_pci_link_check();
#ifdef CONFIG_X86_IO_APIC
/* Program IOAPICs using data from PRT entries. */
if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC)
mp_parse_prt();
#endif
pci_for_each_dev(dev)
acpi_pci_irq_enable(dev);
return_VALUE(0);
}
/*
* acpi_pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 22 $)
* pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 31 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
......@@ -23,7 +24,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* TBD:
* 1. Support more than one IRQ resource entry per link device.
* 1. Support more than one IRQ resource entry per link device (index).
* 2. Implement start/stop mechanism and use ACPI Bus Driver facilities
* for IRQ management (e.g. start()->_SRS).
*/
......@@ -41,17 +42,13 @@
#include "acpi_drivers.h"
#define _COMPONENT ACPI_PCI_LINK_COMPONENT
ACPI_MODULE_NAME ("acpi_pci_link")
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION(ACPI_PCI_DRIVER_NAME);
MODULE_LICENSE("GPL");
#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME ("pci_link")
#define PREFIX "ACPI: "
#define ACPI_PCI_LINK_MAX_IRQS 16
#define ACPI_PCI_LINK_MAX_POSSIBLE 16
static int acpi_pci_link_add (struct acpi_device *device);
static int acpi_pci_link_remove (struct acpi_device *device, int type);
......@@ -69,23 +66,21 @@ static struct acpi_driver acpi_pci_link_driver = {
struct acpi_pci_link_irq {
u8 active; /* Current IRQ */
u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_IRQS];
struct {
u8 valid:1;
u8 enabled:1;
u8 shareable:1; /* 0 = Exclusive */
u8 polarity:1; /* 0 = Active-High */
u8 trigger:1; /* 0 = Level-Triggered */
u8 producer:1; /* 0 = Consumer-Only */
u8 reserved:2;
} flags;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
};
struct acpi_pci_link {
struct list_head node;
struct acpi_device *device;
acpi_handle handle;
struct acpi_pci_link_irq irq;
};
static struct {
int count;
struct list_head entries;
} acpi_link;
/* --------------------------------------------------------------------------
PCI Link Device Management
......@@ -119,45 +114,38 @@ acpi_pci_link_get_possible (
case ACPI_RSTYPE_IRQ:
{
acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->number_of_interrupts) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Blank IRQ resource\n"));
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n"));
result = -ENODEV;
goto end;
}
for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_IRQS); i++) {
for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i]));
continue;
}
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
link->irq.flags.trigger = p->edge_level;
link->irq.flags.polarity = p->active_high_low;
link->irq.flags.shareable = p->shared_exclusive;
break;
}
case ACPI_RSTYPE_EXT_IRQ:
{
acpi_resource_ext_irq *p = &resource->data.extended_irq;
if (!p || !p->number_of_interrupts) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Blank IRQ resource\n"));
result = -ENODEV;
goto end;
}
for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_IRQS); i++) {
for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i]));
continue;
}
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
link->irq.flags.trigger = p->edge_level;
link->irq.flags.polarity = p->active_high_low;
link->irq.flags.shareable = p->shared_exclusive;
break;
}
default:
......@@ -186,6 +174,7 @@ acpi_pci_link_get_current (
acpi_status status = AE_OK;
acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
acpi_resource *resource = NULL;
int irq = 0;
ACPI_FUNCTION_TRACE("acpi_pci_link_get_current");
......@@ -194,65 +183,80 @@ acpi_pci_link_get_current (
link->irq.active = 0;
/* Make sure the link is enabled (no use querying if it isn't). */
result = acpi_bus_get_status(link->device);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));
goto end;
}
if (!link->device->status.enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n"));
return_VALUE(0);
}
/*
* Query and parse _CRS to get the current IRQ assignment.
*/
status = acpi_get_current_resources(link->handle, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n"));
result = -ENODEV;
goto end;
}
resource = (acpi_resource *) buffer.pointer;
switch (resource->id) {
case ACPI_RSTYPE_IRQ:
{
acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->number_of_interrupts) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Blank IRQ resource\n"));
result = -ENODEV;
goto end;
}
link->irq.active = p->interrupts[0];
irq = p->interrupts[0];
break;
}
case ACPI_RSTYPE_EXT_IRQ:
{
acpi_resource_ext_irq *p = &resource->data.extended_irq;
if (!p || !p->number_of_interrupts) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Blank IRQ resource\n"));
result = -ENODEV;
goto end;
}
link->irq.active = p->interrupts[0];
irq = p->interrupts[0];
break;
}
default:
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Resource is not an IRQ entry\n"));
break;
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Resource isn't an IRQ\n"));
result = -ENODEV;
goto end;
}
if (!link->irq.active) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid IRQ %d\n", link->irq.active));
if (!irq) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid use of IRQ 0\n"));
result = -ENODEV;
goto end;
}
/*
* Note that we don't validate that the current IRQ (_CRS) exists
* within the possible IRQs (_PRS): we blindly assume that whatever
* IRQ a boot-enabled Link device is set to is the correct one.
* (Required to support systems such as the Toshiba 5005-S504.)
*/
link->irq.active = irq;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active));
end:
kfree(buffer.pointer);
if (0 == result)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n",
link->irq.active));
return_VALUE(result);
}
......@@ -262,25 +266,43 @@ acpi_pci_link_set (
struct acpi_pci_link *link,
int irq)
{
int result = 0;
acpi_status status = AE_OK;
struct {
acpi_resource res;
acpi_resource end;
} resource;
acpi_buffer buffer = {sizeof(resource)+1, &resource};
int i = 0;
int valid = 0;
ACPI_FUNCTION_TRACE("acpi_pci_link_set");
if (!link || !irq)
return_VALUE(-EINVAL);
/* See if we're already at the target IRQ. */
if (irq == link->irq.active)
return_VALUE(0);
/* Make sure the target IRQ in the list of possible IRQs. */
for (i=0; i<link->irq.possible_count; i++) {
if (irq == link->irq.possible[i])
valid = 1;
}
if (!valid) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq));
return_VALUE(-EINVAL);
}
memset(&resource, 0, sizeof(resource));
/* NOTE: PCI interrupts are always level / active_low / shared. */
resource.res.id = ACPI_RSTYPE_IRQ;
resource.res.length = sizeof(acpi_resource);
resource.res.data.irq.edge_level = link->irq.flags.trigger;
resource.res.data.irq.active_high_low = link->irq.flags.polarity;
resource.res.data.irq.shared_exclusive = link->irq.flags.shareable;
resource.res.data.irq.edge_level = ACPI_LEVEL_SENSITIVE;
resource.res.data.irq.active_high_low = ACPI_ACTIVE_LOW;
resource.res.data.irq.shared_exclusive = ACPI_SHARED;
resource.res.data.irq.number_of_interrupts = 1;
resource.res.data.irq.interrupts[0] = irq;
resource.end.id = ACPI_RSTYPE_END_TAG;
......@@ -291,210 +313,171 @@ acpi_pci_link_set (
return_VALUE(-ENODEV);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", irq));
return_VALUE(0);
}
int
acpi_pci_link_get_irq (
struct acpi_prt_entry *entry,
int *irq)
{
int result = -ENODEV;
struct acpi_device *device = NULL;
struct acpi_pci_link *link = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq");
if (!entry || !entry->source.handle || !irq)
return_VALUE(-EINVAL);
/* TBD: Support multiple index values (not just first). */
if (0 != entry->source.index) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported resource index [%d]\n",
entry->source.index));
return_VALUE(-ENODEV);
}
result = acpi_bus_get_device(entry->source.handle, &device);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ invalid\n"));
return_VALUE(-ENODEV);
/* Make sure the device is enabled. */
result = acpi_bus_get_status(link->device);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));
return_VALUE(result);
}
link = (struct acpi_pci_link *) acpi_driver_data(device);
if (!link) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
if (!link->device->status.enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
return_VALUE(-ENODEV);
}
if (!link->irq.flags.valid) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ invalid\n"));
return_VALUE(-ENODEV);
/* Make sure the active IRQ is the one we requested. */
result = acpi_pci_link_get_current(link);
if (result) {
return_VALUE(result);
}
/* TBD: Support multiple index (IRQ) entries per Link Device */
if (0 != entry->source.index) {
if (link->irq.active != irq) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported IRQ resource index [%d]\n",
entry->source.index));
return_VALUE(-EFAULT);
"Attempt to enable at IRQ %d resulted in IRQ %d\n",
irq, link->irq.active));
link->irq.active = 0;
return_VALUE(-ENODEV);
}
*irq = link->irq.active;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active));
return_VALUE(0);
}
/* --------------------------------------------------------------------------
PCI Link IRQ Management
-------------------------------------------------------------------------- */
#define ACPI_MAX_IRQS 256
#define ACPI_MAX_ISA_IRQ 16
/*
* IRQ penalties are used to promote PCI IRQ balancing. We set each ISA-
* possible IRQ (0-15) with a default penalty relative to its feasibility
* for PCI's use:
*
* Never use: 0, 1, 2 (timer, keyboard, and cascade)
* Avoid using: 13, 14, and 15 (FP error and IDE)
* Penalize: 3, 4, 6, 7, 12 (known ISA uses)
*
* Thus we're left with IRQs 5, 9, 10, 11, and everything above 15 (IO[S]APIC)
* as 'best bets' for PCI use.
*/
static int acpi_irq_penalty[ACPI_MAX_IRQS] = {
1000000, 1000000, 1000000, 10000,
10000, 0, 10000, 10000,
10000, 0, 0, 0,
10000, 100000, 100000, 100000,
};
int
acpi_pci_link_set_irq (
struct acpi_prt_entry *entry,
int irq)
acpi_pci_link_check (void)
{
int result = 0;
struct list_head *node = NULL;
struct acpi_pci_link *link = NULL;
int i = 0;
int valid = 0;
struct acpi_device *device = NULL;
struct acpi_pci_link *link = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_link_set_irq");
if (!entry || !entry->source.handle || !irq)
return_VALUE(-EINVAL);
ACPI_FUNCTION_TRACE("acpi_pci_link_check");
/* TBD: Support multiple index (IRQ) entries per Link Device */
if (0 != entry->source.index) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported resource index [%d]\n",
entry->source.index));
return_VALUE(-ENODEV);
}
/*
* Pass #1: Update penalties to facilitate IRQ balancing.
*/
list_for_each(node, &acpi_link.entries) {
result = acpi_bus_get_device(entry->source.handle, &device);
if (0 != result)
return_VALUE(result);
link = list_entry(node, struct acpi_pci_link, node);
if (!link) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
continue;
}
link = (struct acpi_pci_link *) acpi_driver_data(device);
if (!link) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
return_VALUE(-ENODEV);
if (link->irq.active)
acpi_irq_penalty[link->irq.active] += 100;
else {
int penalty = 100 / link->irq.possible_count;
for (i=0; i<link->irq.possible_count; i++) {
if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ)
acpi_irq_penalty[link->irq.possible[i]] += penalty;
}
}
}
if (!link->irq.flags.valid) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ invalid\n"));
return_VALUE(-ENODEV);
}
/*
* Pass #2: Enable boot-disabled Links at 'best' IRQ.
*/
list_for_each(node, &acpi_link.entries) {
int irq = 0;
int i = 0;
link = list_entry(node, struct acpi_pci_link, node);
if (!link) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
continue;
}
/* Is the target IRQ the same as the currently enabled IRQ? */
if (link->irq.flags.enabled && (irq == link->irq.active)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link already at IRQ %d\n",
irq));
return_VALUE(0);
}
if (link->irq.active)
continue;
/* Is the target IRQ in the list of possible IRQs? */
for (i=0; i<link->irq.possible_count; i++) {
if (irq == link->irq.possible[i])
valid = 1;
}
if (!valid) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ invalid\n"));
return_VALUE(-EINVAL);
}
irq = link->irq.possible[0];
/* TBD: Do we need to disable this link device before resetting? */
/*
* Select the best IRQ. This is done in reverse to promote
* the use of IRQs 9, 10, 11, and >15.
*/
for (i=(link->irq.possible_count-1); i>0; i--) {
if (acpi_irq_penalty[irq] > acpi_irq_penalty[link->irq.possible[i]])
irq = link->irq.possible[i];
}
/* Set the new IRQ */
result = acpi_pci_link_set(link, irq);
if (0 != result)
return_VALUE(result);
/* Enable the link device at this IRQ. */
acpi_pci_link_set(link, irq);
link->irq.active = irq;
acpi_irq_penalty[link->irq.active] += 100;
return_VALUE(result);
printk(PREFIX "%s [%s] enabled at IRQ %d\n",
acpi_device_name(link->device),
acpi_device_bid(link->device), irq);
}
return_VALUE(0);
}
static int
acpi_pci_link_enable (
struct acpi_device *device,
struct acpi_pci_link *link)
int
acpi_pci_link_get_irq (
acpi_handle handle,
int index)
{
int result = -ENODEV;
ACPI_FUNCTION_TRACE("acpi_pci_link_enable");
if (!device || !link)
return_VALUE(-EINVAL);
int result = 0;
struct acpi_device *device = NULL;
struct acpi_pci_link *link = NULL;
result = acpi_pci_link_get_possible(link);
if (0 != result)
return_VALUE(result);
ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq");
result = acpi_bus_get_status(device);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));
return_VALUE(-ENODEV);
result = acpi_bus_get_device(handle, &device);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n"));
return_VALUE(0);
}
/*
* If this link device isn't enabled (_STA bit 1) then we enable it
* by setting an IRQ.
*/
if (!device->status.enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Attempting to enable at IRQ [%d]\n",
link->irq.possible[0]));
result = acpi_pci_link_set(link, link->irq.possible[0]);
if (0 != result)
return_VALUE(result);
result = acpi_bus_get_status(device);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to read status\n"));
return_VALUE(-ENODEV);
}
if (!device->status.enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Enable failed\n"));
return_VALUE(-ENODEV);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link enabled at IRQ %d\n",
link->irq.possible[0]));
link = (struct acpi_pci_link *) acpi_driver_data(device);
if (!link) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
return_VALUE(0);
}
/*
* Now we get the current IRQ just to make sure everything is kosher.
*/
result = acpi_pci_link_get_current(link);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Current IRQ invalid, setting to default\n"));
result = acpi_pci_link_set(link, link->irq.possible[0]);
if (0 != result)
return_VALUE(result);
result = acpi_pci_link_get_current(link);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to read current IRQ\n"));
return_VALUE(result);
}
/* TBD: Support multiple index (IRQ) entries per Link Device */
if (index) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid index %d\n", index));
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Using IRQ %d\n", link->irq.active));
link->irq.flags.valid = 1;
if (!link->irq.active) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
return_VALUE(0);
}
return_VALUE(0);
return_VALUE(link->irq.active);
}
......@@ -509,6 +492,7 @@ acpi_pci_link_add (
int result = 0;
struct acpi_pci_link *link = NULL;
int i = 0;
int found = 0;
ACPI_FUNCTION_TRACE("acpi_pci_link_add");
......@@ -520,25 +504,39 @@ acpi_pci_link_add (
return_VALUE(-ENOMEM);
memset(link, 0, sizeof(struct acpi_pci_link));
link->device = device;
link->handle = device->handle;
sprintf(acpi_device_name(device), "%s", ACPI_PCI_LINK_DEVICE_NAME);
sprintf(acpi_device_class(device), "%s", ACPI_PCI_LINK_CLASS);
acpi_driver_data(device) = link;
result = acpi_pci_link_enable(device, link);
if (0 != result)
result = acpi_pci_link_get_possible(link);
if (result)
goto end;
printk(PREFIX "%s [%s] (IRQs",
acpi_device_name(device), acpi_device_bid(device));
for (i = 0; i < link->irq.possible_count; i++)
printk("%s%d",
(link->irq.active==link->irq.possible[i])?" *":" ",
link->irq.possible[i]);
acpi_pci_link_get_current(link);
printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), acpi_device_bid(device));
for (i = 0; i < link->irq.possible_count; i++) {
if (link->irq.active == link->irq.possible[i]) {
printk(" *%d", link->irq.possible[i]);
found = 1;
}
else
printk(" %d", link->irq.possible[i]);
}
if (!link->irq.active)
printk(", disabled");
else if (!found)
printk(", enabled at IRQ %d", link->irq.active);
printk(")\n");
/* TBD: Acquire/release lock */
list_add_tail(&link->node, &acpi_link.entries);
acpi_link.count++;
end:
if (0 != result)
if (result)
kfree(link);
return_VALUE(result);
......@@ -559,6 +557,9 @@ acpi_pci_link_remove (
link = (struct acpi_pci_link *) acpi_driver_data(device);
/* TBD: Acquire/release lock */
list_del(&link->node);
kfree(link);
return_VALUE(0);
......@@ -570,19 +571,11 @@ acpi_pci_link_init (void)
{
ACPI_FUNCTION_TRACE("acpi_pci_link_init");
if (0 > acpi_bus_register_driver(&acpi_pci_link_driver))
acpi_link.count = 0;
INIT_LIST_HEAD(&acpi_link.entries);
if (acpi_bus_register_driver(&acpi_pci_link_driver) < 0)
return_VALUE(-ENODEV);
return_VALUE(0);
}
void __exit
acpi_pci_link_exit (void)
{
ACPI_FUNCTION_TRACE("acpi_pci_link_init");
acpi_bus_unregister_driver(&acpi_pci_link_driver);
return_VOID;
}
/*
* acpi_pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 30 $)
* pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 37 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
......@@ -36,19 +36,15 @@
#include "acpi_drivers.h"
#define _COMPONENT ACPI_PCI_ROOT_COMPONENT
#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME ("pci_root")
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION(ACPI_PCI_ROOT_DRIVER_NAME);
extern struct pci_ops *pci_root_ops;
#define PREFIX "ACPI: "
static int acpi_pci_root_add (struct acpi_device *device);
static int acpi_pci_root_remove (struct acpi_device *device, int type);
static int acpi_pci_root_bind (struct acpi_device *device);
static struct acpi_driver acpi_pci_root_driver = {
name: ACPI_PCI_ROOT_DRIVER_NAME,
......@@ -57,503 +53,15 @@ static struct acpi_driver acpi_pci_root_driver = {
ops: {
add: acpi_pci_root_add,
remove: acpi_pci_root_remove,
bind: acpi_pci_root_bind,
},
};
struct acpi_pci_data {
acpi_pci_id id;
struct pci_dev *dev;
};
struct acpi_pci_root {
acpi_handle handle;
struct acpi_pci_data data;
acpi_pci_id id;
struct pci_bus *bus;
};
struct acpi_prt_list acpi_prts;
/* --------------------------------------------------------------------------
PCI Routing Table (PRT) Support
-------------------------------------------------------------------------- */
static int
acpi_prt_find_entry (
acpi_pci_id *id,
u8 pin,
struct acpi_prt_entry **entry)
{
struct list_head *node = NULL;
ACPI_FUNCTION_TRACE("acpi_prt_find_entry");
if (!id || !entry)
return_VALUE(-ENODEV);
/* TBD: Locking */
list_for_each(node, &acpi_prts.entries) {
(*entry) = list_entry(node, struct acpi_prt_entry, node);
/* TBD: Include check for segment when supported by pci_dev */
if ((id->bus == (*entry)->id.bus)
&& (id->device == (*entry)->id.dev)
&& (pin == (*entry)->id.pin)) {
return_VALUE(0);
}
}
(*entry) = NULL;
return_VALUE(-ENODEV);
}
int
acpi_prt_get_irq (
struct pci_dev *dev,
u8 pin,
int *irq)
{
int result = 0;
struct acpi_prt_entry *entry = NULL;
acpi_pci_id id = {0, 0, 0, 0};
ACPI_FUNCTION_TRACE("acpi_prt_get_irq");
if (!dev || !irq)
return_VALUE(-ENODEV);
if (!dev->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device has invalid 'bus' field\n"));
return_VALUE(-EFAULT);
}
id.segment = 0;
id.bus = dev->bus->number;
id.device = PCI_SLOT(dev->devfn);
id.function = PCI_FUNC(dev->devfn);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Resolving IRQ for %02x:%02x:%02x.%02x[%c]\n",
id.segment, id.bus, id.device, id.function, ('A'+pin)));
result = acpi_prt_find_entry(&id, pin, &entry);
if (0 != result)
return_VALUE(result);
/* Type 1: Dynamic (e.g. PCI Link Device) */
if (entry->source.handle)
result = acpi_pci_link_get_irq(entry, irq);
/* Type 2: Static (e.g. I/O [S]APIC Direct) */
else {
if (entry->source.index)
*irq = entry->source.index;
else
result = -ENODEV;
}
if (0 == result)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", *irq));
else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to reslove IRQ\n"));
return_VALUE(0);
}
int
acpi_prt_set_irq (
struct pci_dev *dev,
u8 pin,
int irq)
{
int result = 0;
struct acpi_prt_entry *entry = NULL;
acpi_pci_id id = {0, 0, 0, 0};
ACPI_FUNCTION_TRACE("acpi_pci_set_irq");
if (!dev || !irq)
return_VALUE(-EINVAL);
if (!dev->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device has invalid 'bus' field\n"));
return_VALUE(-EFAULT);
}
id.segment = 0;
id.bus = dev->bus->number;
id.device = PCI_SLOT(dev->devfn);
id.function = PCI_FUNC(dev->devfn);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Setting %02x:%02x:%02x.%02x[%c] to IRQ%d\n",
id.segment, id.bus, id.device, id.function, ('A'+pin), irq));
result = acpi_prt_find_entry(&id, pin, &entry);
if (0 != result)
return_VALUE(result);
/* Type 1: Dynamic (e.g. PCI Link Device) */
if (entry->source.handle)
result = acpi_pci_link_set_irq(entry, irq);
/* Type 2: Static (e.g. I/O [S]APIC Direct) */
else
result = -EFAULT;
if (0 == result)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "IRQ set\n"));
else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set IRQ\n"));
return_VALUE(result);
}
static int
acpi_prt_add_entry (
acpi_handle handle,
u8 seg,
u8 bus,
acpi_pci_routing_table *prt)
{
struct acpi_prt_entry *entry = NULL;
ACPI_FUNCTION_TRACE("acpi_prt_add_entry");
if (!prt)
return_VALUE(-EINVAL);
entry = kmalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
if (!entry)
return_VALUE(-ENOMEM);
memset(entry, 0, sizeof(struct acpi_prt_entry));
entry->id.seg = seg;
entry->id.bus = bus;
entry->id.dev = prt->address >> 16;
entry->id.pin = prt->pin;
/*
* Type 1: Dynamic
* ---------------
* The 'source' field specifies the PCI interrupt link device used to
* configure the IRQ assigned to this slot|dev|pin. The 'source_index'
* indicates which resource descriptor in the resource template (of
* the link device) this interrupt is allocated from.
*/
if (prt->source)
acpi_get_handle(handle, prt->source, &entry->source.handle);
/*
* Type 2: Static
* --------------
* The 'source' field is NULL, and the 'source_index' field specifies
* the IRQ value, which is hardwired to specific interrupt inputs on
* the interrupt controller.
*/
else
entry->source.handle = NULL;
entry->source.index = prt->source_index;
/*
* NOTE: Don't query the Link Device for IRQ information at this time
* because Link Device enumeration may not have occurred yet
* (e.g. exists somewhere 'below' this _PRT entry in the ACPI
* namespace).
*/
ACPI_DEBUG_PRINT_RAW((ACPI_DB_OK, " %02X:%02X:%02X[%c] -> %s[%d]\n",
entry->id.seg, entry->id.bus, entry->id.dev,
('A' + entry->id.pin), prt->source, entry->source.index));
/* TBD: Acquire/release lock */
list_add_tail(&entry->node, &acpi_prts.entries);
acpi_prts.count++;
return_VALUE(0);
}
static int
acpi_prt_parse (
acpi_handle handle,
u8 seg,
u8 bus)
{
acpi_status status = AE_OK;
char pathname[PATHNAME_MAX] = {0};
acpi_buffer buffer = {0, NULL};
acpi_pci_routing_table *prt = NULL;
ACPI_FUNCTION_TRACE("acpi_prt_parse");
buffer.length = sizeof(pathname);
buffer.pointer = pathname;
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
printk(KERN_INFO PREFIX "%s [%s._PRT]\n", ACPI_PCI_PRT_DEVICE_NAME,
pathname);
/*
* Evaluate this _PRT and add all entries to our global list.
*/
buffer.length = 0;
buffer.pointer = NULL;
status = acpi_get_irq_routing_table(handle, &buffer);
if (status != AE_BUFFER_OVERFLOW) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error evaluating _PRT [%s]\n",
acpi_format_exception(status)));
return_VALUE(-ENODEV);
}
prt = kmalloc(buffer.length, GFP_KERNEL);
if (!prt)
return_VALUE(-ENOMEM);
memset(prt, 0, buffer.length);
buffer.pointer = prt;
status = acpi_get_irq_routing_table(handle, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error evaluating _PRT [%s]\n",
acpi_format_exception(status)));
kfree(buffer.pointer);
return_VALUE(-ENODEV);
}
while (prt && (prt->length > 0)) {
acpi_prt_add_entry(handle, seg, bus, prt);
prt = (acpi_pci_routing_table*)((unsigned long)prt + prt->length);
}
return_VALUE(0);
}
/* --------------------------------------------------------------------------
PCI Device Binding
-------------------------------------------------------------------------- */
static void
acpi_pci_data_handler (
acpi_handle handle,
u32 function,
void *context)
{
ACPI_FUNCTION_TRACE("acpi_pci_data_handler");
/* TBD: Anything we need to do here? */
return_VOID;
}
/**
* acpi_os_get_pci_id
* ------------------
* This function gets used by the ACPI Interpreter (a.k.a. Core Subsystem)
* to resolve PCI information for ACPI-PCI devices defined in the namespace.
*/
acpi_status
acpi_os_get_pci_id (
acpi_handle handle,
acpi_pci_id *id)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_device *device = NULL;
struct acpi_pci_data *data = NULL;
ACPI_FUNCTION_TRACE("acpi_os_get_pci_id");
if (!id)
return_ACPI_STATUS(AE_BAD_PARAMETER);
result = acpi_bus_get_device(handle, &device);
if (0 != result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI Bus context for device %s\n",
acpi_device_bid(device)));
return_ACPI_STATUS(AE_NOT_EXIST);
}
status = acpi_get_data(handle, acpi_pci_data_handler, (void**) &data);
if (ACPI_FAILURE(status) || !data || !data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI-PCI context for device %s\n",
acpi_device_bid(device)));
return_ACPI_STATUS(status);
}
id->segment = data->id.segment;
id->bus = data->id.bus;
id->device = data->id.device;
id->function = data->id.function;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device %s has PCI address %02x:%02x:%02x.%02x\n",
acpi_device_bid(device), id->segment, id->bus,
id->device, id->function));
return_ACPI_STATUS(AE_OK);
}
static int
acpi_pci_root_bind (
struct acpi_device *device)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_pci_data *data = NULL;
struct acpi_pci_data *parent_data = NULL;
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_root_bind");
if (!device || !device->parent)
return_VALUE(-EINVAL);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Attempting to bind PCI device %s.%s\n",
acpi_device_bid(device->parent), acpi_device_bid(device)));
data = kmalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
if (!data)
return_VALUE(-ENOMEM);
memset(data, 0, sizeof(struct acpi_pci_data));
/*
* Segment & Bus
* -------------
* These are obtained via the parent device's ACPI-PCI context..
* Note that PCI root bridge devices don't have a 'dev->subordinate'.
*/
status = acpi_get_data(device->parent->handle, acpi_pci_data_handler,
(void**) &parent_data);
if (ACPI_FAILURE(status) || !parent_data || !parent_data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid ACPI-PCI context for parent device %s\n",
acpi_device_bid(device->parent)));
result = -ENODEV;
goto end;
}
data->id.segment = parent_data->id.segment;
if (parent_data->dev->subordinate) /* e.g. PCI-PCI bridge */
data->id.bus = parent_data->dev->subordinate->number;
else if (parent_data->dev->bus) /* PCI root bridge */
data->id.bus = parent_data->dev->bus->number;
else {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Parent device %s is not a PCI bridge\n",
acpi_device_bid(device->parent)));
result = -ENODEV;
goto end;
}
/*
* Device & Function
* -----------------
* These are simply obtained from the device's _ADR method. Note
* that a value of zero is valid.
*/
data->id.device = device->pnp.bus_address >> 16;
data->id.function = device->pnp.bus_address & 0xFFFF;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Binding device %s.%s to %02x:%02x:%02x.%02x\n",
acpi_device_bid(device->parent), acpi_device_bid(device),
data->id.segment, data->id.bus, data->id.device,
data->id.function));
/*
* Locate PCI Device
* -----------------
* Locate matching device in PCI namespace. If it doesn't exist
* this typically means that the device isn't currently inserted
* (e.g. docking station, port replicator, etc.).
*/
data->dev = pci_find_slot(data->id.bus,
PCI_DEVFN(data->id.device, data->id.function));
if (!data->dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device %02x:%02x:%02x.%02x not present in PCI namespace\n",
data->id.segment, data->id.bus,
data->id.device, data->id.function));
result = -ENODEV;
goto end;
}
if (!data->dev->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device %02x:%02x:%02x.%02x has invalid 'bus' field\n",
data->id.segment, data->id.bus,
data->id.device, data->id.function));
result = -ENODEV;
goto end;
}
/*
* Attach ACPI-PCI Context
* -----------------------
* Thus binding the ACPI and PCI devices.
*/
status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to attach ACPI-PCI context to device %s\n",
acpi_device_bid(device)));
result = -ENODEV;
goto end;
}
/*
* PCI Bridge?
* -----------
* If so, install the 'bind' function to facilitate callbacks for
* all of its children.
*/
if (data->dev->subordinate)
device->ops.bind = acpi_pci_root_bind;
/*
* PCI Routing Table
* -----------------
* Evaluate and parse _PRT, if exists. This code is independent of
* PCI bridges (above) to allow parsing of _PRT objects within the
* scope of non-bridge devices. Note that _PRTs within the scope of
* a PCI bridge assume the bridge's subordinate bus number.
*
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
*/
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
if (ACPI_SUCCESS(status)) {
if (data->dev->subordinate) /* PCI-PCI bridge */
acpi_prt_parse(device->handle, data->id.segment,
data->dev->subordinate->number);
else /* non-bridge PCI device */
acpi_prt_parse(device->handle, data->id.segment,
data->id.bus);
}
end:
if (0 != result)
kfree(data);
return_VALUE(result);
}
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
static int
acpi_pci_root_add (
......@@ -563,6 +71,7 @@ acpi_pci_root_add (
struct acpi_pci_root *root = NULL;
acpi_status status = AE_OK;
unsigned long value = 0;
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_root_add");
......@@ -582,7 +91,7 @@ acpi_pci_root_add (
/*
* TBD: Doesn't the bus driver automatically set this?
*/
device->ops.bind = acpi_pci_root_bind;
device->ops.bind = acpi_pci_bind;
/*
* Segment
......@@ -593,11 +102,14 @@ acpi_pci_root_add (
&value);
switch (status) {
case AE_OK:
root->data.id.segment = (u16) value;
root->id.segment = (u16) value;
printk("_SEG exists! Unsupported. Abort.\n");
BUG();
break;
case AE_NOT_FOUND:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming segment 0 (no _SEG)\n"));
root->data.id.segment = 0;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Assuming segment 0 (no _SEG)\n"));
root->id.segment = 0;
break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SEG\n"));
......@@ -614,11 +126,11 @@ acpi_pci_root_add (
&value);
switch (status) {
case AE_OK:
root->data.id.bus = (u16) value;
root->id.bus = (u16) value;
break;
case AE_NOT_FOUND:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n"));
root->data.id.bus = 0;
root->id.bus = 0;
break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BBN\n"));
......@@ -631,18 +143,17 @@ acpi_pci_root_add (
* -----------------
* Obtained from _ADR (which has already been evaluated for us).
*/
root->data.id.device = device->pnp.bus_address >> 16;
root->data.id.function = device->pnp.bus_address & 0xFFFF;
root->id.device = device->pnp.bus_address >> 16;
root->id.function = device->pnp.bus_address & 0xFFFF;
/*
* TBD: Evaluate _CRS to get root bridge resources
* TBD: Need PCI interface for enumeration/configuration of roots.
*/
printk(KERN_INFO PREFIX "%s [%s] (%02x:%02x:%02x.%02x)\n",
printk(KERN_INFO PREFIX "%s [%s] (%02x:%02x)\n",
acpi_device_name(device), acpi_device_bid(device),
root->data.id.segment, root->data.id.bus,
root->data.id.device, root->data.id.function);
root->id.segment, root->id.bus);
/*
* Scan the Root Bridge
......@@ -651,20 +162,11 @@ acpi_pci_root_add (
* PCI namespace does not get created until this call is made (and
* thus the root bridge's pci_dev does not exist).
*/
pci_scan_bus(root->data.id.bus, pci_root_ops, NULL);
/*
* Locate PCI Device
* -----------------
* Locate the matching PCI root bridge device in the PCI namespace.
*/
root->data.dev = pci_find_slot(root->data.id.bus,
PCI_DEVFN(root->data.id.device, root->data.id.function));
if (!root->data.dev) {
root->bus = pcibios_scan_root(root->id.bus);
if (!root->bus) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Device %02x:%02x:%02x.%02x not present\n",
root->data.id.segment, root->data.id.bus,
root->data.id.device, root->data.id.function));
"Bus %02x:%02x not present in PCI namespace\n",
root->id.segment, root->id.bus));
result = -ENODEV;
goto end;
}
......@@ -674,27 +176,22 @@ acpi_pci_root_add (
* -----------------------
* Thus binding the ACPI and PCI devices.
*/
status = acpi_attach_data(root->handle, acpi_pci_data_handler,
&root->data);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to attach ACPI-PCI context to device %s\n",
acpi_device_bid(device)));
result = -ENODEV;
result = acpi_pci_bind_root(device, &root->id, root->bus);
if (result)
goto end;
}
/*
* PCI Routing Table
* -----------------
* Evaluate and parse _PRT, if exists. Note that root bridges
* must have a _PRT (optional for subordinate bridges).
* Evaluate and parse _PRT, if exists.
*/
result = acpi_prt_parse(device->handle, root->data.id.segment,
root->data.id.bus);
status = acpi_get_handle(root->handle, METHOD_NAME__PRT, &handle);
if (ACPI_SUCCESS(status))
result = acpi_pci_irq_add_prt(root->handle, root->id.segment,
root->id.bus);
end:
if (0 != result)
if (result)
kfree(root);
return_VALUE(result);
......@@ -726,10 +223,12 @@ acpi_pci_root_init (void)
{
ACPI_FUNCTION_TRACE("acpi_pci_root_init");
acpi_prts.count = 0;
INIT_LIST_HEAD(&acpi_prts.entries);
/* DEBUG:
acpi_dbg_layer = ACPI_PCI_COMPONENT;
acpi_dbg_level = 0xFFFFFFFF;
*/
if (0 > acpi_bus_register_driver(&acpi_pci_root_driver))
if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0)
return_VALUE(-ENODEV);
return_VALUE(0);
......
......@@ -141,6 +141,13 @@ extern int skip_ioapic_setup;
*/
#define io_apic_assign_pci_irqs (mp_irq_entries && !skip_ioapic_setup)
#ifdef CONFIG_ACPI_BOOT
extern int io_apic_get_unique_id (int ioapic, int apic_id);
extern int io_apic_get_version (int ioapic);
extern int io_apic_get_redir_entries (int ioapic);
extern int io_apic_set_pci_routing (int ioapic, int pin, int irq);
#endif /*CONFIG_ACPI_BOOT*/
#else /* !CONFIG_X86_IO_APIC */
#define io_apic_assign_pci_irqs 0
#endif
......
......@@ -220,5 +220,14 @@ extern unsigned long mp_lapic_addr;
extern int pic_mode;
extern int using_apic_timer;
#ifdef CONFIG_ACPI_BOOT
extern void mp_register_lapic (u8 id, u8 enabled);
extern void mp_register_lapic_address (u64 address);
extern void mp_register_ioapic (u8 id, u32 address, u32 irq_base);
extern void mp_override_legacy_irq (u8 bus_irq, u8 polarity, u8 trigger, u32 global_irq);
extern void mp_config_acpi_legacy_irqs (void);
extern void mp_parse_prt (void);
#endif /*CONFIG_ACPI_BOOT*/
#endif
......@@ -29,22 +29,28 @@
#define _LINUX
#endif
#include <linux/list.h>
/*
* YES this is ugly.
* But, moving all of ACPI's private headers to include/acpi isn't the right
* answer either.
* Please just ignore it for now.
* Yes this is ugly, but moving all of ACPI's private headers to include/acpi
* isn't the right answer either. Please just ignore it for now.
*/
#include "../../drivers/acpi/include/acpi.h"
#include <asm/acpi.h>
/* --------------------------------------------------------------------------
Boot-Time Table Parsing
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_BOOT
enum acpi_irq_model_id {
ACPI_IRQ_MODEL_PIC = 0,
ACPI_IRQ_MODEL_IOAPIC,
ACPI_IRQ_MODEL_IOSAPIC,
ACPI_IRQ_MODEL_COUNT
};
extern enum acpi_irq_model_id acpi_irq_model;
/* Root System Description Pointer (RSDP) */
struct acpi_table_rsdp {
......@@ -327,10 +333,9 @@ extern acpi_table_handler acpi_table_ops[ACPI_TABLE_COUNT];
typedef int (*acpi_madt_entry_handler) (acpi_table_entry_header *header);
struct acpi_boot_flags {
u8 madt:1;
u8 reserved:7;
};
char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
unsigned long acpi_find_rsdp (void);
int acpi_boot_init (char *cmdline);
int acpi_table_init (char *cmdline);
int acpi_table_parse (enum acpi_table_id, acpi_table_handler);
......@@ -338,31 +343,26 @@ int acpi_table_parse_madt (enum acpi_table_id, acpi_madt_entry_handler);
void acpi_table_print (struct acpi_table_header *, unsigned long);
void acpi_table_print_madt_entry (acpi_table_entry_header *);
#endif /*CONFIG_ACPI_BOOT*/
extern int acpi_mp_config;
#else /*!CONFIG_ACPI_BOOT*/
/* --------------------------------------------------------------------------
PCI Interrupt Routing (PRT)
-------------------------------------------------------------------------- */
#define acpi_mp_config 0
#ifdef CONFIG_ACPI_PCI
#endif /*CONFIG_ACPI_BOOT*/
#define ACPI_INT_MODEL_PIC 0
#define ACPI_INT_MODEL_IOAPIC 1
#define ACPI_INT_MODEL_IOSAPIC 2
#ifdef CONFIG_ACPI_PCI
struct acpi_prt_entry {
struct list_head node;
struct {
u8 seg;
u8 bus;
u8 dev;
u8 pin;
} id;
acpi_pci_id id;
u8 pin;
struct {
acpi_handle handle;
u32 index;
} source;
} link;
u32 irq;
};
struct acpi_prt_list {
......@@ -370,25 +370,21 @@ struct acpi_prt_list {
struct list_head entries;
};
extern struct acpi_prt_list acpi_prts;
extern struct acpi_prt_list acpi_prt;
struct pci_dev;
int acpi_prt_get_irq (struct pci_dev *dev, u8 pin, int *irq);
int acpi_prt_set_irq (struct pci_dev *dev, u8 pin, int irq);
int acpi_pci_irq_enable (struct pci_dev *dev);
int acpi_pci_irq_init (void);
#endif /*CONFIG_ACPI_PCI*/
/* --------------------------------------------------------------------------
ACPI Interpreter (Core)
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_INTERPRETER
#ifdef CONFIG_ACPI
int acpi_init(void);
#endif /*CONFIG_ACPI_INTERPRETER*/
#endif /*CONFIG_ACPI*/
#endif /*_LINUX_ACPI_H*/
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