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
This diff is collapsed.
......@@ -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*/
This diff is collapsed.
......@@ -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);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -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