#include "io_ports.h"
+/*
+ * Debug level
+ */
+int apic_verbosity;
+
+
static void apic_pm_activate(void);
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves.
+ */
+void ack_bad_irq(unsigned int irq)
+{
+ printk("unexpected IRQ trap at vector %02x\n", irq);
+ /*
+ * Currently unexpected vectors happen only on SMP and APIC.
+ * We _must_ ack these because every local APIC has only N
+ * irq slots per priority level, and a 'hanging, unacked' IRQ
+ * holds up an irq slot - in excessive cases (when multiple
+ * unexpected vectors occur) that might lock up the APIC
+ * completely.
+ */
+ ack_APIC_irq();
+}
+
void __init apic_intr_init(void)
{
#ifdef CONFIG_SMP
unsigned int lvr, version;
lvr = apic_read(APIC_LVR);
version = GET_APIC_VERSION(lvr);
- if (version >= 0x14)
+ if (!APIC_INTEGRATED(version) || version >= 0x14)
return 0xff;
else
return 0xf;
* PIC mode, enable APIC mode in the IMCR, i.e.
* connect BSP's local APIC to INT and NMI lines.
*/
- printk("leaving PIC mode, enabling APIC mode.\n");
+ apic_printk(APIC_VERBOSE, "leaving PIC mode, "
+ "enabling APIC mode.\n");
outb(0x70, 0x22);
outb(0x01, 0x23);
}
* interrupts, including IPIs, won't work beyond
* this point! The only exception are INIT IPIs.
*/
- printk("disabling APIC mode, entering PIC mode.\n");
+ apic_printk(APIC_VERBOSE, "disabling APIC mode, "
+ "entering PIC mode.\n");
outb(0x70, 0x22);
outb(0x00, 0x23);
}
* The version register is read-only in a real APIC.
*/
reg0 = apic_read(APIC_LVR);
- Dprintk("Getting VERSION: %x\n", reg0);
+ apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg0);
apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK);
reg1 = apic_read(APIC_LVR);
- Dprintk("Getting VERSION: %x\n", reg1);
+ apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg1);
/*
* The two version reads above should print the same
* The ID register is read/write in a real APIC.
*/
reg0 = apic_read(APIC_ID);
- Dprintk("Getting ID: %x\n", reg0);
+ apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0);
/*
* The next two are just to see if we have sane values.
* compatibility mode, but most boxes are anymore.
*/
reg0 = apic_read(APIC_LVT0);
- Dprintk("Getting LVT0: %x\n", reg0);
+ apic_printk(APIC_DEBUG, "Getting LVT0: %x\n", reg0);
reg1 = apic_read(APIC_LVT1);
- Dprintk("Getting LVT1: %x\n", reg1);
+ apic_printk(APIC_DEBUG, "Getting LVT1: %x\n", reg1);
return 1;
}
void __init sync_Arb_IDs(void)
{
+ /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */
+ unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR));
+ if (ver >= 0x14) /* P4 or higher */
+ return;
/*
* Wait for idle.
*/
apic_wait_icr_idle();
- Dprintk("Synchronizing Arb IDs.\n");
+ apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n");
apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG
| APIC_DM_INIT);
}
void __init setup_local_APIC (void)
{
- unsigned long value, ver, maxlvt;
+ unsigned long oldvalue, value, ver, maxlvt;
/* Pound the ESR really hard over the head with a big hammer - mbligh */
if (esr_disable) {
value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
if (!smp_processor_id() && (pic_mode || !value)) {
value = APIC_DM_EXTINT;
- printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
+ apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n",
+ smp_processor_id());
} else {
value = APIC_DM_EXTINT | APIC_LVT_MASKED;
- printk("masked ExtINT on CPU#%d\n", smp_processor_id());
+ apic_printk(APIC_VERBOSE, "masked ExtINT on CPU#%d\n",
+ smp_processor_id());
}
apic_write_around(APIC_LVT0, value);
maxlvt = get_maxlvt();
if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
apic_write(APIC_ESR, 0);
- value = apic_read(APIC_ESR);
- printk("ESR value before enabling vector: %08lx\n", value);
+ oldvalue = apic_read(APIC_ESR);
value = ERROR_APIC_VECTOR; // enables sending errors
apic_write_around(APIC_LVTERR, value);
if (maxlvt > 3)
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
- printk("ESR value after enabling vector: %08lx\n", value);
+ if (value != oldvalue)
+ apic_printk(APIC_VERBOSE, "ESR value before enabling "
+ "vector: 0x%08lx after: 0x%08lx\n",
+ oldvalue, value);
} else {
if (esr_disable)
/*
apic_pm_activate();
}
+/*
+ * If Linux enabled the LAPIC against the BIOS default
+ * disable it down before re-entering the BIOS on shutdown.
+ * Otherwise the BIOS may get confused and not power-off.
+ */
+void lapic_shutdown(void)
+{
+ if (!cpu_has_apic || !enabled_via_apicbase)
+ return;
+
+ local_irq_disable();
+ disable_local_APIC();
+ local_irq_enable();
+}
+
#ifdef CONFIG_PM
static struct {
- /* 'active' is true if the local APIC was enabled by us and
- not the BIOS; this signifies that we are also responsible
- for disabling it before entering apm/acpi suspend */
int active;
/* r/w apic fields */
unsigned int apic_id;
return 0;
}
+/*
+ * This device has no shutdown method - fully functioning local APICs
+ * are needed on every CPU up until machine_halt/restart/poweroff.
+ */
static struct sysdev_class lapic_sysclass = {
set_kset_name("lapic"),
}
__setup("lapic", lapic_enable);
+static int __init apic_set_verbosity(char *str)
+{
+ if (strcmp("debug", str) == 0)
+ apic_verbosity = APIC_DEBUG;
+ else if (strcmp("verbose", str) == 0)
+ apic_verbosity = APIC_VERBOSE;
+ else
+ printk(KERN_WARNING "APIC Verbosity level %s not recognised"
+ " use apic=verbose or apic=debug", str);
+
+ return 0;
+}
+
+__setup("apic=", apic_set_verbosity);
+
static int __init detect_init_APIC (void)
{
u32 h, l, features;
extern void get_cpu_vendor(struct cpuinfo_x86*);
- /* Disabled by DMI scan or kernel option? */
+ /* Disabled by kernel option? */
if (enable_local_apic < 0)
return -1;
break;
goto no_apic;
case X86_VENDOR_INTEL:
- if (boot_cpu_data.x86 == 6 ||
- (boot_cpu_data.x86 == 15 && (cpu_has_apic || enable_local_apic > 0)) ||
+ if (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15 ||
(boot_cpu_data.x86 == 5 && cpu_has_apic))
break;
goto no_apic;
}
if (!cpu_has_apic) {
+ /*
+ * Over-ride BIOS and try to enable the local
+ * APIC only if "lapic" specified.
+ */
+ if (enable_local_apic <= 0) {
+ printk("Local APIC disabled by BIOS -- "
+ "you can enable it with \"lapic\"\n");
+ return -1;
+ }
/*
* Some BIOSes disable the local APIC in the
* APIC_BASE MSR. This can only be done in
- * software for Intel P6 and AMD K7 (Model > 1).
+ * software for Intel P6 or later and AMD K7
+ * (Model > 1) or later.
*/
rdmsr(MSR_IA32_APICBASE, l, h);
if (!(l & MSR_IA32_APICBASE_ENABLE)) {
apic_phys = mp_lapic_addr;
set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
- Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
+ printk(KERN_DEBUG "mapped APIC to %08lx (%08lx)\n", APIC_BASE,
+ apic_phys);
/*
* Fetch the APIC ID of the BSP in case we have a
if (smp_found_config) {
ioapic_phys = mp_ioapics[i].mpc_apicaddr;
if (!ioapic_phys) {
- printk(KERN_ERR "WARNING: bogus zero IO-APIC address found in MPTABLE, disabling IO/APIC support!\n");
-
+ printk(KERN_ERR
+ "WARNING: bogus zero IO-APIC "
+ "address found in MPTABLE, "
+ "disabling IO/APIC support!\n");
smp_found_config = 0;
skip_ioapic_setup = 1;
goto fake_ioapic_page;
}
} else {
fake_ioapic_page:
- ioapic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+ ioapic_phys = (unsigned long)
+ alloc_bootmem_pages(PAGE_SIZE);
ioapic_phys = __pa(ioapic_phys);
}
set_fixmap_nocache(idx, ioapic_phys);
- Dprintk("mapped IOAPIC to %08lx (%08lx)\n",
- __fix_to_virt(idx), ioapic_phys);
+ printk(KERN_DEBUG "mapped IOAPIC to %08lx (%08lx)\n",
+ __fix_to_virt(idx), ioapic_phys);
idx++;
}
}
/* next tick in 8254 can be caught by catching timer wraparound */
static void __init wait_8254_wraparound(void)
{
- unsigned int curr_count, prev_count=~0;
- int delta;
+ unsigned int curr_count, prev_count;
curr_count = get_8254_timer_count();
-
do {
prev_count = curr_count;
curr_count = get_8254_timer_count();
- delta = curr_count-prev_count;
- /*
- * This limit for delta seems arbitrary, but it isn't, it's
- * slightly above the level of error a buggy Mercury/Neptune
- * chipset timer can cause.
- */
+ /* workaround for broken Mercury/Neptune */
+ if (prev_count >= curr_count + 0x100)
+ curr_count = get_8254_timer_count();
- } while (delta < 300);
+ } while (prev_count >= curr_count);
}
/*
* Default initialization for 8254 timers. If we use other timers like HPET,
* we override this later
*/
-void (*wait_timer_tick)(void) = wait_8254_wraparound;
+void (*wait_timer_tick)(void) __initdata = wait_8254_wraparound;
/*
* This function sets up the local APIC timer, with a timeout of
apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
}
-static void setup_APIC_timer(unsigned int clocks)
+static void __init setup_APIC_timer(unsigned int clocks)
{
unsigned long flags;
int i;
const int LOOPS = HZ/10;
- printk("calibrating APIC timer ...\n");
+ apic_printk(APIC_VERBOSE, "calibrating APIC timer ...\n");
/*
* Put whatever arbitrary (but long enough) timeout
result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
if (cpu_has_tsc)
- printk("..... CPU clock speed is %ld.%04ld MHz.\n",
+ apic_printk(APIC_VERBOSE, "..... CPU clock speed is "
+ "%ld.%04ld MHz.\n",
((long)(t2-t1)/LOOPS)/(1000000/HZ),
((long)(t2-t1)/LOOPS)%(1000000/HZ));
- printk("..... host bus clock speed is %ld.%04ld MHz.\n",
+ apic_printk(APIC_VERBOSE, "..... host bus clock speed is "
+ "%ld.%04ld MHz.\n",
result/(1000000/HZ),
result%(1000000/HZ));
void __init setup_boot_APIC_clock(void)
{
- printk("Using local APIC timer interrupts.\n");
+ apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n");
using_apic_timer = 1;
local_irq_disable();
void __init setup_secondary_APIC_clock(void)
{
- local_irq_disable(); /* FIXME: Do we need this? --RR */
setup_APIC_timer(calibration_result);
- local_irq_enable();
}
void __init disable_APIC_timer(void)
{
int cpu = smp_processor_id();
- x86_do_profile(regs);
-
+ profile_tick(CPU_PROFILING, regs);
if (--per_cpu(prof_counter, cpu) <= 0) {
/*
* The multiplier may have changed since the last time we got
* interrupt as well. Thus we cannot inline the local irq ... ]
*/
-void smp_apic_timer_interrupt(struct pt_regs regs)
+fastcall void smp_apic_timer_interrupt(struct pt_regs *regs)
{
int cpu = smp_processor_id();
* interrupt lock, which is the WrongThing (tm) to do.
*/
irq_enter();
- smp_local_timer_interrupt(®s);
+ smp_local_timer_interrupt(regs);
irq_exit();
}
/*
* This interrupt should _never_ happen with our APIC/SMP architecture
*/
-asmlinkage void smp_spurious_interrupt(void)
+fastcall void smp_spurious_interrupt(struct pt_regs *regs)
{
unsigned long v;
* This interrupt should never happen with our APIC/SMP architecture
*/
-asmlinkage void smp_error_interrupt(void)
+fastcall void smp_error_interrupt(struct pt_regs *regs)
{
unsigned long v, v1;
6: Received illegal vector
7: Illegal register address
*/
- printk (KERN_INFO "APIC error on CPU%d: %02lx(%02lx)\n",
+ printk (KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n",
smp_processor_id(), v , v1);
irq_exit();
}