2 * Local APIC handling, local APIC timers
4 * (c) 1999, 2000 Ingo Molnar <mingo@redhat.com>
7 * Maciej W. Rozycki : Bits for genuine 82489DX APICs;
8 * thanks to Eric Gilmore
10 * for testing these extensively.
11 * Maciej W. Rozycki : Various updates and fixes.
12 * Mikael Pettersson : Power Management for UP-APIC.
14 * Mikael Pettersson : PM converted to driver model.
17 #include <linux/init.h>
20 #include <linux/delay.h>
21 #include <linux/bootmem.h>
22 #include <linux/smp_lock.h>
23 #include <linux/interrupt.h>
24 #include <linux/mc146818rtc.h>
25 #include <linux/kernel_stat.h>
26 #include <linux/sysdev.h>
27 #include <linux/cpu.h>
28 #include <linux/module.h>
30 #include <asm/atomic.h>
33 #include <asm/mpspec.h>
35 #include <asm/arch_hooks.h>
37 #include <asm/i8253.h>
40 #include <mach_apic.h>
41 #include <mach_apicdef.h>
48 * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
49 * IPIs in place of local APIC timers
51 static cpumask_t timer_bcast_ipi;
55 * Knob to control our willingness to enable the local APIC.
57 static int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */
59 static inline void lapic_disable(void)
61 enable_local_apic = -1;
62 clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
65 static inline void lapic_enable(void)
67 enable_local_apic = 1;
75 static int modern_apic(void)
78 unsigned int lvr, version;
79 /* AMD systems use old APIC versions, so check the CPU */
80 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
81 boot_cpu_data.x86 >= 0xf)
83 lvr = apic_read(APIC_LVR);
84 version = GET_APIC_VERSION(lvr);
85 return version >= 0x14;
92 * 'what should we do if we get a hw irq event on an illegal vector'.
93 * each architecture has to answer this themselves.
95 void ack_bad_irq(unsigned int irq)
97 printk("unexpected IRQ trap at vector %02x\n", irq);
99 * Currently unexpected vectors happen only on SMP and APIC.
100 * We _must_ ack these because every local APIC has only N
101 * irq slots per priority level, and a 'hanging, unacked' IRQ
102 * holds up an irq slot - in excessive cases (when multiple
103 * unexpected vectors occur) that might lock up the APIC
105 * But only ack when the APIC is enabled -AK
112 void __init apic_intr_init(void)
117 /* self generated IPI for local APIC timer */
118 set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
120 /* IPI vectors for APIC spurious and error interrupts */
121 set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
122 set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
124 /* thermal monitor LVT interrupt */
125 #ifdef CONFIG_X86_MCE_P4THERMAL
126 set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
130 /* Using APIC to generate smp_local_timer_interrupt? */
131 int using_apic_timer __read_mostly = 0;
133 static int enabled_via_apicbase;
135 void enable_NMI_through_LVT0 (void * dummy)
139 ver = apic_read(APIC_LVR);
140 ver = GET_APIC_VERSION(ver);
141 v = APIC_DM_NMI; /* unmask and set to NMI */
142 if (!APIC_INTEGRATED(ver)) /* 82489DX */
143 v |= APIC_LVT_LEVEL_TRIGGER;
144 apic_write_around(APIC_LVT0, v);
146 #endif /* !CONFIG_XEN */
148 int get_physical_broadcast(void)
158 static void up_apic_timer_interrupt_call(void)
160 int cpu = smp_processor_id();
163 * the NMI deadlock-detector uses this.
165 per_cpu(irq_stat, cpu).apic_timer_irqs++;
167 smp_local_timer_interrupt();
171 void smp_send_timer_broadcast_ipi(void)
175 cpus_and(mask, cpu_online_map, timer_bcast_ipi);
176 if (!cpus_empty(mask)) {
178 send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
181 * We can directly call the apic timer interrupt handler
182 * in UP case. Minus all irq related functions
184 up_apic_timer_interrupt_call();
190 int setup_profiling_timer(unsigned int multiplier)
196 * This initializes the IO-APIC and APIC hardware if this is
199 int __init APIC_init_uniprocessor (void)
201 #ifdef CONFIG_X86_IO_APIC
202 if (smp_found_config)
203 if (!skip_ioapic_setup && nr_ioapics)
210 static int __init parse_lapic(char *arg)
215 early_param("lapic", parse_lapic);
217 static int __init parse_nolapic(char *arg)
222 early_param("nolapic", parse_nolapic);