patch-2.3.99-pre6 linux/arch/i386/kernel/apic.c
Next file: linux/arch/i386/kernel/entry.S
Previous file: linux/arch/i386/kernel/acpi.c
Back to the patch index
Back to the overall index
- Lines: 445
- Date:
Wed Apr 12 09:33:19 2000
- Orig file:
v2.3.99-pre5/linux/arch/i386/kernel/apic.c
- Orig date:
Sat Feb 12 11:22:10 2000
diff -u --recursive --new-file v2.3.99-pre5/linux/arch/i386/kernel/apic.c linux/arch/i386/kernel/apic.c
@@ -4,7 +4,9 @@
* (c) 1999, 2000 Ingo Molnar <mingo@redhat.com>
*
* Fixes
- * Maciej W. Rozycki : Bits for genuine 82489DX timers
+ * Maciej W. Rozycki : Bits for genuine 82489DX APICs;
+ * thanks to Eric Gilmore for
+ * testing these extensively
*/
#include <linux/config.h>
@@ -44,32 +46,96 @@
return maxlvt;
}
-void disable_local_APIC (void)
+static void clear_local_APIC(void)
{
- unsigned long value;
- int maxlvt;
+ int maxlvt;
+ unsigned long v;
+
+ maxlvt = get_maxlvt();
/*
- * Disable APIC
+ * Careful: we have to set masks only first to deassert
+ * any level-triggered sources.
*/
- value = apic_read(APIC_SPIV);
- value &= ~(1<<8);
- apic_write(APIC_SPIV,value);
+ v = apic_read(APIC_LVTT);
+ apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
+ v = apic_read(APIC_LVT0);
+ apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
+ v = apic_read(APIC_LVT1);
+ apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
+ if (maxlvt >= 3) {
+ v = apic_read(APIC_LVTERR);
+ apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
+ }
+ if (maxlvt >= 4) {
+ v = apic_read(APIC_LVTPC);
+ apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
+ }
/*
* Clean APIC state for other OSs:
*/
- value = apic_read(APIC_SPIV);
- value &= ~(1<<8);
- apic_write(APIC_SPIV,value);
- maxlvt = get_maxlvt();
- apic_write_around(APIC_LVTT, 0x00010000);
- apic_write_around(APIC_LVT0, 0x00010000);
- apic_write_around(APIC_LVT1, 0x00010000);
+ apic_write_around(APIC_LVTT, APIC_LVT_MASKED);
+ apic_write_around(APIC_LVT0, APIC_LVT_MASKED);
+ apic_write_around(APIC_LVT1, APIC_LVT_MASKED);
if (maxlvt >= 3)
- apic_write_around(APIC_LVTERR, 0x00010000);
+ apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
if (maxlvt >= 4)
- apic_write_around(APIC_LVTPC, 0x00010000);
+ apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+}
+
+void __init connect_bsp_APIC(void)
+{
+ if (pic_mode) {
+ /*
+ * Do not trust the local APIC being empty at bootup.
+ */
+ clear_local_APIC();
+ /*
+ * PIC mode, enable symmetric IO mode in the IMCR,
+ * i.e. connect BSP's local APIC to INT and NMI lines.
+ */
+ printk("leaving PIC mode, enabling symmetric IO mode.\n");
+ outb(0x70, 0x22);
+ outb(0x01, 0x23);
+ }
+}
+
+void disconnect_bsp_APIC(void)
+{
+ if (pic_mode) {
+ /*
+ * Put the board back into PIC mode (has an effect
+ * only on certain older boards). Note that APIC
+ * interrupts, including IPIs, won't work beyond
+ * this point! The only exception are INIT IPIs.
+ */
+ printk("disabling symmetric IO mode, entering PIC mode.\n");
+ outb(0x70, 0x22);
+ outb(0x00, 0x23);
+ }
+}
+
+void disable_local_APIC(void)
+{
+ unsigned long value;
+
+ clear_local_APIC();
+
+ /*
+ * Disable APIC (implies clearing of registers
+ * for 82489DX!).
+ */
+ value = apic_read(APIC_SPIV);
+ value &= ~(1<<8);
+ apic_write_around(APIC_SPIV, value);
+}
+
+void __init sync_Arb_IDs(void)
+{
+ Dprintk("Synchronizing Arb IDs.\n");
+ apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG
+ | APIC_DM_INIT);
}
extern void __error_in_apic_c (void);
@@ -78,6 +144,9 @@
{
unsigned long value, ver, maxlvt;
+ value = apic_read(APIC_LVR);
+ ver = GET_APIC_VERSION(value);
+
if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)
__error_in_apic_c();
@@ -87,11 +156,12 @@
if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
BUG();
- value = apic_read(APIC_SPIV);
+ value = apic_read(APIC_SPIV);
+ value &= ~APIC_VECTOR_MASK;
/*
* Enable APIC
*/
- value |= (1<<8);
+ value |= (1<<8);
/*
* Some unknown Intel IO/APIC (or APIC) errata is biting us with
@@ -108,7 +178,7 @@
*/
#if 0
/* Enable focus processor (bit==0) */
- value &= ~(1<<9);
+ value &= ~(1<<9);
#else
/* Disable focus processor (bit==1) */
value |= (1<<9);
@@ -117,7 +187,7 @@
* Set spurious IRQ vector
*/
value |= SPURIOUS_APIC_VECTOR;
- apic_write(APIC_SPIV,value);
+ apic_write_around(APIC_SPIV, value);
/*
* Set up LVT0, LVT1:
@@ -126,48 +196,44 @@
* strictly necessery in pure symmetric-IO mode, but sometimes
* we delegate interrupts to the 8259A.
*/
- if (!smp_processor_id()) {
- value = 0x00000700;
+ /*
+ * TODO: set up through-local-APIC from through-I/O-APIC? --macro
+ */
+ 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());
} else {
- value = 0x00010700;
+ value = APIC_DM_EXTINT | APIC_LVT_MASKED;
printk("masked ExtINT on CPU#%d\n", smp_processor_id());
}
- apic_write_around(APIC_LVT0,value);
+ apic_write_around(APIC_LVT0, value);
/*
* only the BP should see the LINT1 NMI signal, obviously.
*/
if (!smp_processor_id())
- value = 0x00000400; // unmask NMI
+ value = APIC_DM_NMI;
else
- value = 0x00010400; // mask NMI
- apic_write_around(APIC_LVT1,value);
+ value = APIC_DM_NMI | APIC_LVT_MASKED;
+ if (!APIC_INTEGRATED(ver)) /* 82489DX */
+ value |= APIC_LVT_LEVEL_TRIGGER;
+ apic_write_around(APIC_LVT1, value);
- value = apic_read(APIC_LVR);
- ver = GET_APIC_VERSION(value);
if (APIC_INTEGRATED(ver)) { /* !82489DX */
maxlvt = get_maxlvt();
- /*
- * Due to the Pentium erratum 3AP.
- */
- if (maxlvt > 3) {
- apic_readaround(APIC_SPIV); // not strictly necessery
+ 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);
- value = apic_read(APIC_LVTERR);
value = ERROR_APIC_VECTOR; // enables sending errors
- apic_write(APIC_LVTERR,value);
+ apic_write_around(APIC_LVTERR, value);
/*
* spec says clear errors after enabling vector.
*/
- if (maxlvt != 3) {
- apic_readaround(APIC_SPIV);
+ if (maxlvt > 3)
apic_write(APIC_ESR, 0);
- }
value = apic_read(APIC_ESR);
printk("ESR value after enabling vector: %08lx\n", value);
} else
@@ -177,22 +243,23 @@
* Set Task Priority to 'accept all'. We never change this
* later on.
*/
- value = apic_read(APIC_TASKPRI);
- value &= ~APIC_TPRI_MASK;
- apic_write(APIC_TASKPRI,value);
+ value = apic_read(APIC_TASKPRI);
+ value &= ~APIC_TPRI_MASK;
+ apic_write_around(APIC_TASKPRI, value);
/*
* Set up the logical destination ID and put the
* APIC into flat delivery mode.
*/
- value = apic_read(APIC_LDR);
+ value = apic_read(APIC_LDR);
value &= ~APIC_LDR_MASK;
value |= (1<<(smp_processor_id()+24));
- apic_write(APIC_LDR,value);
+ apic_write_around(APIC_LDR, value);
- value = apic_read(APIC_DFR);
- value |= SET_APIC_DFR(0xf);
- apic_write(APIC_DFR, value);
+ /*
+ * Must be "all ones" explicitly for 82489DX.
+ */
+ apic_write_around(APIC_DFR, 0xffffffff);
}
void __init init_apic_mappings(void)
@@ -214,6 +281,13 @@
set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
+ /*
+ * Fetch the APIC ID of the BSP in case we have a
+ * default configuration (or the MP table is broken).
+ */
+ if (boot_cpu_id == -1U)
+ boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID));
+
#ifdef CONFIG_X86_IO_APIC
{
unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
@@ -285,7 +359,7 @@
* chipset timer can cause.
*/
- } while (delta<300);
+ } while (delta < 300);
}
/*
@@ -305,21 +379,19 @@
{
unsigned int lvtt1_value, tmp_value;
- tmp_value = apic_read(APIC_LVTT);
lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
- apic_write(APIC_LVTT, lvtt1_value);
+ apic_write_around(APIC_LVTT, lvtt1_value);
/*
* Divide PICLK by 16
*/
tmp_value = apic_read(APIC_TDCR);
- apic_write(APIC_TDCR, (tmp_value
+ apic_write_around(APIC_TDCR, (tmp_value
& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
| APIC_TDR_DIV_16);
- tmp_value = apic_read(APIC_TMICT);
- apic_write(APIC_TMICT, clocks/APIC_DIVISOR);
+ apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
}
void setup_APIC_timer(void * data)
@@ -353,6 +425,12 @@
t0 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
do {
+ /*
+ * It looks like the 82489DX cannot handle
+ * consecutive reads of the TMCCT register well;
+ * this dummy read prevents it from a lockup.
+ */
+ apic_read(APIC_SPIV);
t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
} while (delta < 0);
@@ -490,6 +568,41 @@
#undef APIC_DIVISOR
+#ifdef CONFIG_SMP
+static inline void handle_smp_time (int user, int cpu)
+{
+ int system = !user;
+ struct task_struct * p = current;
+ /*
+ * After doing the above, we need to make like
+ * a normal interrupt - otherwise timer interrupts
+ * ignore the global interrupt lock, which is the
+ * WrongThing (tm) to do.
+ */
+
+ irq_enter(cpu, 0);
+ update_one_process(p, 1, user, system, cpu);
+ if (p->pid) {
+ p->counter -= 1;
+ if (p->counter <= 0) {
+ p->counter = 0;
+ p->need_resched = 1;
+ }
+ if (p->priority < DEF_PRIORITY) {
+ kstat.cpu_nice += user;
+ kstat.per_cpu_nice[cpu] += user;
+ } else {
+ kstat.cpu_user += user;
+ kstat.per_cpu_user[cpu] += user;
+ }
+ kstat.cpu_system += system;
+ kstat.per_cpu_system[cpu] += system;
+
+ }
+ irq_exit(cpu, 0);
+}
+#endif
+
/*
* Local timer interrupt handler. It does both profiling and
* process statistics/rescheduling.
@@ -502,7 +615,6 @@
inline void smp_local_timer_interrupt(struct pt_regs * regs)
{
- int user = (user_mode(regs) != 0);
int cpu = smp_processor_id();
/*
@@ -511,13 +623,8 @@
* updated with atomic operations). This is especially
* useful with a profiling multiplier != 1
*/
- if (!user)
- x86_do_profile(regs->eip);
if (--prof_counter[cpu] <= 0) {
- int system = 1 - user;
- struct task_struct * p = current;
-
/*
* The multiplier may have changed since the last time we got
* to this point as a result of the user writing to
@@ -532,33 +639,9 @@
prof_old_multiplier[cpu] = prof_counter[cpu];
}
- /*
- * After doing the above, we need to make like
- * a normal interrupt - otherwise timer interrupts
- * ignore the global interrupt lock, which is the
- * WrongThing (tm) to do.
- */
-
- irq_enter(cpu, 0);
- update_one_process(p, 1, user, system, cpu);
- if (p->pid) {
- p->counter -= 1;
- if (p->counter <= 0) {
- p->counter = 0;
- p->need_resched = 1;
- }
- if (p->priority < DEF_PRIORITY) {
- kstat.cpu_nice += user;
- kstat.per_cpu_nice[cpu] += user;
- } else {
- kstat.cpu_user += user;
- kstat.per_cpu_user[cpu] += user;
- }
- kstat.cpu_system += system;
- kstat.per_cpu_system[cpu] += system;
-
- }
- irq_exit(cpu, 0);
+#ifdef CONFIG_SMP
+ handle_smp_time(user_mode(regs), cpu);
+#endif
}
/*
@@ -603,7 +686,17 @@
*/
asmlinkage void smp_spurious_interrupt(void)
{
- ack_APIC_irq();
+ unsigned long v;
+
+ /*
+ * Check if this really is a spurious interrupt and ACK it
+ * if it is a vectored one. Just in case...
+ * Spurious interrupts should not be ACKed.
+ */
+ v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1));
+ if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
+ ack_APIC_irq();
+
/* see sw-dev-man vol 3, chapter 7.4.13.5 */
printk("spurious APIC interrupt on CPU#%d, should never happen.\n",
smp_processor_id());
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)