Commit 5e1c701f authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[PATCH] x86-64 ACPI

ACPI updates for x86-64/2.5.42

ACPI finally works now.
parent 80ce6897
/*
* acpi.c - Architecture-Specific Low-Level ACPI Support
*
* 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>
* Copyright (C) 2002 Andi Kleen, SuSE Labs (x86-64 port)
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/acpi.h>
#include <asm/mpspec.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/apicdef.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/io_apic.h>
extern int acpi_disabled;
#define PREFIX "ACPI: "
/* --------------------------------------------------------------------------
Boot-time Configuration
-------------------------------------------------------------------------- */
#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
* persist should be memcpy'd.
*/
extern unsigned long end_pfn;
/* rely on all ACPI tables being in the direct mapping */
char *
__acpi_map_table (
unsigned long phys_addr,
unsigned long size)
{
if (!phys_addr || !size)
return NULL;
if (phys_addr < (end_pfn << PAGE_SHIFT))
return __va(phys_addr);
printk("acpi mapping beyond end_pfn: %lx > %lx\n", phys_addr, end_pfn<<PAGE_SHIFT);
return NULL;
}
#ifdef CONFIG_X86_LOCAL_APIC
int acpi_lapic;
static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
static 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;
}
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;
processor = (struct acpi_table_lapic*) header;
if (!processor)
return -EINVAL;
acpi_table_print_madt_entry(header);
mp_register_lapic (
processor->id, /* APIC ID */
processor->flags.enabled); /* Enabled? */
return 0;
}
static int __init
acpi_parse_lapic_addr_ovr (
acpi_table_entry_header *header)
{
struct acpi_table_lapic_addr_ovr *lapic_addr_ovr = NULL;
lapic_addr_ovr = (struct acpi_table_lapic_addr_ovr*) header;
if (!lapic_addr_ovr)
return -EINVAL;
acpi_lapic_addr = lapic_addr_ovr->address;
return 0;
}
static int __init
acpi_parse_lapic_nmi (
acpi_table_entry_header *header)
{
struct acpi_table_lapic_nmi *lapic_nmi = NULL;
lapic_nmi = (struct acpi_table_lapic_nmi*) header;
if (!lapic_nmi)
return -EINVAL;
acpi_table_print_madt_entry(header);
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 acpi_ioapic;
static int __init
acpi_parse_ioapic (
acpi_table_entry_header *header)
{
struct acpi_table_ioapic *ioapic = NULL;
ioapic = (struct acpi_table_ioapic*) header;
if (!ioapic)
return -EINVAL;
acpi_table_print_madt_entry(header);
mp_register_ioapic (
ioapic->id,
ioapic->address,
ioapic->global_irq_base);
return 0;
}
static int __init
acpi_parse_int_src_ovr (
acpi_table_entry_header *header)
{
struct acpi_table_int_src_ovr *intsrc = NULL;
intsrc = (struct acpi_table_int_src_ovr*) header;
if (!intsrc)
return -EINVAL;
acpi_table_print_madt_entry(header);
mp_override_legacy_irq (
intsrc->bus_irq,
intsrc->flags.polarity,
intsrc->flags.trigger,
intsrc->global_irq);
return 0;
}
static int __init
acpi_parse_nmi_src (
acpi_table_entry_header *header)
{
struct acpi_table_nmi_src *nmi_src = NULL;
nmi_src = (struct acpi_table_nmi_src*) header;
if (!nmi_src)
return -EINVAL;
acpi_table_print_madt_entry(header);
/* TBD: Support nimsrc entries? */
return 0;
}
#endif /*CONFIG_X86_IO_APIC*/
#ifdef CONFIG_HPET_TIMER
static int __init
acpi_parse_hpet (
unsigned long phys_addr,
unsigned long size)
{
struct acpi_table_hpet *hpet_tbl;
hpet_tbl = __va(phys_addr);
if (hpet_tbl->addr.space_id != ACPI_SPACE_MEM) {
printk(KERN_WARNING "acpi: HPET timers must be located in memory.\n");
return -1;
}
hpet.address = hpet_tbl->addr.addrl | ((long) hpet_tbl->addr.addrh << 32);
printk(KERN_INFO "acpi: HPET id: %#x base: %#lx\n", hpet_tbl->id, hpet.address);
return 0;
}
#endif
static unsigned long __init
acpi_scan_rsdp (
unsigned long start,
unsigned long length)
{
unsigned long offset = 0;
unsigned long sig_len = sizeof("RSD PTR ") - 1;
/*
* Scan all 16-byte boundaries of the physical memory region for the
* RSDP signature.
*/
for (offset = 0; offset < length; offset += 16) {
if (strncmp((char *) (start + offset), "RSD PTR ", sig_len))
continue;
return (start + offset);
}
return 0;
}
unsigned long __init
acpi_find_rsdp (void)
{
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);
return rsdp_phys;
}
int __init
acpi_boot_init (
char *cmdline)
{
int result = 0;
/*
* 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 (result)
return result;
result = acpi_blacklisted();
if (result) {
acpi_disabled = 1;
return result;
} else
printk(KERN_NOTICE PREFIX "BIOS passes blacklist\n");
#ifdef CONFIG_X86_LOCAL_APIC
/*
* MADT
* ----
* Parse the Multiple APIC Description Table (MADT), if exists.
* Note that this table provides platform SMP configuration
* information -- the successor to MPS tables.
*/
result = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
if (!result) {
printk(KERN_WARNING PREFIX "MADT not present\n");
return 0;
}
else if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing MADT\n");
return result;
}
else if (result > 1)
printk(KERN_WARNING PREFIX "Multiple MADT tables exist\n");
/*
* 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 (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 (!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 (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
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 (!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;
}
/* 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 (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 (result < 0) {
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;
acpi_ioapic = 1;
#endif /*CONFIG_X86_IO_APIC*/
#ifdef CONFIG_X86_LOCAL_APIC
if (acpi_lapic && acpi_ioapic)
smp_found_config = 1;
#endif
#ifdef CONFIG_HPET_TIMER
result = acpi_table_parse(ACPI_HPET, acpi_parse_hpet);
if (result < 0)
printk("ACPI: no HPET table found (%d).\n", result);
#endif
return 0;
}
#endif /*CONFIG_ACPI_BOOT*/
/* --------------------------------------------------------------------------
Low-Level Sleep Support
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_SLEEP
#error not ported to x86-64 yet
#define DEBUG
#ifdef DEBUG
#include <linux/serial.h>
#endif
/* address in low memory of the wakeup routine. */
unsigned long acpi_wakeup_address = 0;
/* new page directory that we will be using */
static pmd_t *pmd;
/* saved page directory */
static pmd_t saved_pmd;
/* page which we'll use for the new page directory */
static pte_t *ptep;
extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));
/*
* acpi_create_identity_pmd
*
* Create a new, identity mapped pmd.
*
* Do this by creating new page directory, and marking all the pages as R/W
* Then set it as the new Page Middle Directory.
* And, of course, flush the TLB so it takes effect.
*
* We save the address of the old one, for later restoration.
*/
static void acpi_create_identity_pmd (void)
{
pgd_t *pgd;
int i;
ptep = (pte_t*)__get_free_page(GFP_KERNEL);
/* fill page with low mapping */
for (i = 0; i < PTRS_PER_PTE; i++)
set_pte(ptep + i, mk_pte_phys(i << PAGE_SHIFT, PAGE_SHARED));
pgd = pgd_offset(current->active_mm, 0);
pmd = pmd_alloc(current->mm,pgd, 0);
/* save the old pmd */
saved_pmd = *pmd;
/* set the new one */
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(ptep)));
/* flush the TLB */
local_flush_tlb();
}
/*
* acpi_restore_pmd
*
* Restore the old pmd saved by acpi_create_identity_pmd and
* free the page that said function alloc'd
*/
static void acpi_restore_pmd (void)
{
set_pmd(pmd, saved_pmd);
local_flush_tlb();
free_page((unsigned long)ptep);
}
/**
* acpi_save_state_mem - save kernel state
*
* Create an identity mapped page table and copy the wakeup routine to
* low memory.
*/
int acpi_save_state_mem (void)
{
acpi_create_identity_pmd();
acpi_copy_wakeup_routine(acpi_wakeup_address);
return 0;
}
/**
* acpi_save_state_disk - save kernel state to disk
*
*/
int acpi_save_state_disk (void)
{
return 1;
}
/*
* acpi_restore_state
*/
void acpi_restore_state_mem (void)
{
acpi_restore_pmd();
}
/**
* acpi_reserve_bootmem - do _very_ early ACPI initialisation
*
* We allocate a page in low memory for the wakeup
* routine for when we come back from a sleep state. The
* runtime allocator allows specification of <16M pages, but not
* <1M pages.
*/
void __init acpi_reserve_bootmem(void)
{
acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
printk(KERN_DEBUG "ACPI: have wakeup address 0x%8.8lx\n", acpi_wakeup_address);
}
#endif /*CONFIG_ACPI_SLEEP*/
void acpi_pci_link_exit(void) {}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* thanks to Eric Gilmore * thanks to Eric Gilmore
* and Rolf G. Tews * and Rolf G. Tews
* for testing these extensively * for testing these extensively
* Paul Diefenbaugh : Added full ACPI support
*/ */
#include <linux/mm.h> #include <linux/mm.h>
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/acpi.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/smp.h> #include <asm/smp.h>
...@@ -746,7 +748,8 @@ void __init print_IO_APIC(void) ...@@ -746,7 +748,8 @@ void __init print_IO_APIC(void)
(reg_01.entries != 0x1f) && /* dual Xeon boards */ (reg_01.entries != 0x1f) && /* dual Xeon boards */
(reg_01.entries != 0x22) && /* bigger Xeon boards */ (reg_01.entries != 0x22) && /* bigger Xeon boards */
(reg_01.entries != 0x2E) && (reg_01.entries != 0x2E) &&
(reg_01.entries != 0x3F) (reg_01.entries != 0x3F) &&
(reg_01.entries != 0x03)
) )
UNEXPECTED_IO_APIC(); UNEXPECTED_IO_APIC();
...@@ -1014,6 +1017,8 @@ static void __init setup_ioapic_ids_from_mpc (void) ...@@ -1014,6 +1017,8 @@ static void __init setup_ioapic_ids_from_mpc (void)
unsigned char old_id; unsigned char old_id;
unsigned long flags; unsigned long flags;
if (acpi_ioapic) return; /* ACPI does that already */
/* /*
* Set the IOAPIC ID to the value stored in the MPC table. * Set the IOAPIC ID to the value stored in the MPC table.
*/ */
...@@ -1104,7 +1109,7 @@ static int __init timer_irq_works(void) ...@@ -1104,7 +1109,7 @@ static int __init timer_irq_works(void)
{ {
unsigned int t1 = jiffies; unsigned int t1 = jiffies;
sti(); local_irq_enable();
/* Let ten ticks pass... */ /* Let ten ticks pass... */
mdelay((10 * 1000) / HZ); mdelay((10 * 1000) / HZ);
...@@ -1117,7 +1122,6 @@ static int __init timer_irq_works(void) ...@@ -1117,7 +1122,6 @@ static int __init timer_irq_works(void)
*/ */
if (jiffies - t1 > 4) if (jiffies - t1 > 4)
return 1; return 1;
return 0; return 0;
} }
...@@ -1376,7 +1380,7 @@ static struct hw_interrupt_type lapic_irq_type = { ...@@ -1376,7 +1380,7 @@ static struct hw_interrupt_type lapic_irq_type = {
end_lapic_irq end_lapic_irq
}; };
static void enable_NMI_through_LVT0 (void * dummy) void enable_NMI_through_LVT0 (void * dummy)
{ {
unsigned int v, ver; unsigned int v, ver;
...@@ -1401,7 +1405,6 @@ static void setup_nmi (void) ...@@ -1401,7 +1405,6 @@ static void setup_nmi (void)
*/ */
printk(KERN_INFO "activating NMI Watchdog ..."); printk(KERN_INFO "activating NMI Watchdog ...");
smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1);
enable_NMI_through_LVT0(NULL); enable_NMI_through_LVT0(NULL);
printk(" done.\n"); printk(" done.\n");
...@@ -1477,7 +1480,6 @@ static inline void unlock_ExtINT_logic(void) ...@@ -1477,7 +1480,6 @@ static inline void unlock_ExtINT_logic(void)
*/ */
static inline void check_timer(void) static inline void check_timer(void)
{ {
extern int timer_ack;
int pin1, pin2; int pin1, pin2;
int vector; int vector;
...@@ -1497,7 +1499,6 @@ static inline void check_timer(void) ...@@ -1497,7 +1499,6 @@ static inline void check_timer(void)
*/ */
apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
init_8259A(1); init_8259A(1);
timer_ack = 1;
enable_8259A_irq(0); enable_8259A_irq(0);
pin1 = find_isa_irq_pin(0, mp_INT); pin1 = find_isa_irq_pin(0, mp_INT);
...@@ -1605,8 +1606,7 @@ void __init setup_IO_APIC(void) ...@@ -1605,8 +1606,7 @@ void __init setup_IO_APIC(void)
printk("ENABLING IO-APIC IRQs\n"); printk("ENABLING IO-APIC IRQs\n");
/* /*
* Set up the IO-APIC IRQ routing table by parsing the MP-BIOS * Set up the IO-APIC IRQ routing table.
* mptable:
*/ */
setup_ioapic_ids_from_mpc(); setup_ioapic_ids_from_mpc();
sync_Arb_IDs(); sync_Arb_IDs();
...@@ -1615,3 +1615,175 @@ void __init setup_IO_APIC(void) ...@@ -1615,3 +1615,175 @@ void __init setup_IO_APIC(void)
check_timer(); check_timer();
print_IO_APIC(); print_IO_APIC();
} }
/* Ensure the ACPI SCI interrupt level is active low, edge-triggered */
void __init mp_config_ioapic_for_sci(int irq)
{
#if 0 /* fixme */
int ioapic;
int ioapic_pin;
ioapic = mp_find_ioapic(irq);
ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start;
io_apic_set_pci_routing(ioapic, ioapic_pin, irq);
#endif
}
/* --------------------------------------------------------------------------
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*/
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* Alan Cox : Added EBDA scanning * Alan Cox : Added EBDA scanning
* Ingo Molnar : various cleanups and rewrites * 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/mm.h>
...@@ -21,11 +22,13 @@ ...@@ -21,11 +22,13 @@
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/acpi.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/mpspec.h> #include <asm/mpspec.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/io_apic.h>
/* Have we found an MP table */ /* Have we found an MP table */
int smp_found_config; int smp_found_config;
...@@ -262,7 +265,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) ...@@ -262,7 +265,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc)
printk("APIC at: 0x%X\n",mpc->mpc_lapic); printk("APIC at: 0x%X\n",mpc->mpc_lapic);
/* save the local APIC address, it might be non-default */ /* save the local APIC address, it might be non-default */
if (!acpi_found_madt) if (!acpi_lapic)
mp_lapic_addr = mpc->mpc_lapic; mp_lapic_addr = mpc->mpc_lapic;
/* /*
...@@ -274,7 +277,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) ...@@ -274,7 +277,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc)
{ {
struct mpc_config_processor *m= struct mpc_config_processor *m=
(struct mpc_config_processor *)mpt; (struct mpc_config_processor *)mpt;
if (!acpi_found_madt) if (!acpi_lapic)
MP_processor_info(m); MP_processor_info(m);
mpt += sizeof(*m); mpt += sizeof(*m);
count += sizeof(*m); count += sizeof(*m);
...@@ -490,19 +493,18 @@ void __init get_smp_config (void) ...@@ -490,19 +493,18 @@ void __init get_smp_config (void)
{ {
struct intel_mp_floating *mpf = mpf_found; struct intel_mp_floating *mpf = mpf_found;
#ifdef CONFIG_ACPI_BOOT
/* /*
* Check if the MADT exists, and if so, use it to get processor * ACPI may be used to obtain the entire SMP configuration or just to
* information (ACPI_MADT_LAPIC). The MADT supports the concept * enumerate/configure processors (CONFIG_ACPI_HT_ONLY). Note that
* of both logical (e.g. HT) and physical processor(s); where the * ACPI supports both logical (e.g. Hyper-Threading) and physical
* MPS only supports physical. * processors, where MPS only supports physical.
*/ */
if (acpi_boot.madt) { if (acpi_lapic && acpi_ioapic) {
acpi_found_madt = acpi_table_parse(ACPI_APIC, acpi_parse_madt); printk(KERN_INFO "Using ACPI (MADT) for SMP configuration information\n");
if (acpi_found_madt > 0) return;
acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
} }
#endif /*CONFIG_ACPI_BOOT*/ 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); printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
if (mpf->mpf_feature2 & (1<<7)) { if (mpf->mpf_feature2 & (1<<7)) {
...@@ -664,3 +666,315 @@ void __init find_smp_config (void) ...@@ -664,3 +666,315 @@ void __init find_smp_config (void)
#endif #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_id == -1U)
boot_cpu_id = 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;
}
#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_X86_IO_APIC*/
#endif /*CONFIG_ACPI_BOOT*/
/*
* asm-x86_64/acpi.h
*
* Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2001 Patrick Mochel <mochel@osdl.org>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef _ASM_ACPI_H
#define _ASM_ACPI_H
#ifdef __KERNEL__
#define COMPILER_DEPENDENT_INT64 long long
#define COMPILER_DEPENDENT_UINT64 unsigned long long
/*
* Calling conventions:
*
* ACPI_SYSTEM_XFACE - Interfaces to host OS (handlers, threads)
* ACPI_EXTERNAL_XFACE - External ACPI interfaces
* ACPI_INTERNAL_XFACE - Internal ACPI interfaces
* ACPI_INTERNAL_VAR_XFACE - Internal variable-parameter list interfaces
*/
#define ACPI_SYSTEM_XFACE
#define ACPI_EXTERNAL_XFACE
#define ACPI_INTERNAL_XFACE
#define ACPI_INTERNAL_VAR_XFACE
/* Asm macros */
#define ACPI_ASM_MACROS
#define BREAKPOINT3
#define ACPI_DISABLE_IRQS() __cli()
#define ACPI_ENABLE_IRQS() __sti()
#define ACPI_FLUSH_CPU_CACHE() wbinvd()
/*
* A brief explanation as GNU inline assembly is a bit hairy
* %0 is the output parameter in RAX ("=a")
* %1 and %2 are the input parameters in RCX ("c")
* and an immediate value ("i") respectively
* All actual register references are preceded with "%%" as in "%%edx"
* Immediate values in the assembly are preceded by "$" as in "$0x1"
* The final asm parameter are the operation altered non-output registers.
*/
#define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) \
do { \
unsigned long dummy; \
asm("1: movl (%2),%%eax;" \
"movl %%eax,%%edx;" \
"andq %2,%%rdx;" \
"btsl $0x1,%%edx;" \
"adcl $0x0,%%edx;" \
"lock; cmpxchgl %%edx,(%1);" \
"jnz 1b;" \
"cmpb $0x3,%%dl;" \
"sbbl %%eax,%%eax" \
:"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~1L):"dx"); \
} while(0)
#define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) \
do { \
unsigned long dummy; \
asm("1: movl (%2),%%eax;" \
"movl %%eax,%%edx;" \
"andq %2,%%rdx;" \
"lock; cmpxchgl %%edx,(%1);" \
"jnz 1b;" \
"andl $0x1,%%eax" \
:"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~3L):"dx"); \
} while(0)
/*
* Math helper asm macros
*/
#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \
asm("divl %2;" \
:"=a"(q32), "=d"(r32) \
:"r"(d32), \
"0"(n_lo), "1"(n_hi))
#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \
asm("shrl $1,%2;" \
"rcrl $1,%3;" \
:"=r"(n_hi), "=r"(n_lo) \
:"0"(n_hi), "1"(n_lo))
#ifndef CONFIG_ACPI_BOOT
#define acpi_lapic 0
#define acpi_ioapic 0
#else
#ifdef CONFIG_X86_LOCAL_APIC
extern int acpi_lapic;
#else
#define acpi_lapic 0
#endif
#ifdef CONFIG_X86_IO_APIC
extern int acpi_ioapic;
#else
#define acpi_ioapic 0
#endif
/* Fixmap pages to reserve for ACPI boot-time tables (see fixmap.h) */
#define FIX_ACPI_PAGES 4
#endif /*CONFIG_ACPI_BOOT*/
#ifdef CONFIG_ACPI_SLEEP
/* routines for saving/restoring kernel state */
extern int acpi_save_state_mem(void);
extern int acpi_save_state_disk(void);
extern void acpi_restore_state_mem(void);
extern unsigned long acpi_wakeup_address;
/* early initialization routine */
extern void acpi_reserve_bootmem(void);
#endif /*CONFIG_ACPI_SLEEP*/
#define boot_cpu_physical_apicid boot_cpu_id
extern int acpi_disabled;
#endif /*__KERNEL__*/
#endif /*_ASM_ACPI_H*/
...@@ -141,6 +141,14 @@ extern int skip_ioapic_setup; ...@@ -141,6 +141,14 @@ extern int skip_ioapic_setup;
*/ */
#define io_apic_assign_pci_irqs (mp_irq_entries && !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
#else /* !CONFIG_X86_IO_APIC */ #else /* !CONFIG_X86_IO_APIC */
#define io_apic_assign_pci_irqs 0 #define io_apic_assign_pci_irqs 0
#endif #endif
......
...@@ -187,5 +187,17 @@ extern unsigned long mp_lapic_addr; ...@@ -187,5 +187,17 @@ extern unsigned long mp_lapic_addr;
extern int pic_mode; extern int pic_mode;
extern int using_apic_timer; 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);
#ifdef CONFIG_X86_IO_APIC
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_X86_IO_APIC*/
#endif
#endif #endif
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