aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-03-22 04:11:21 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-03-22 22:39:05 -0400
commitad62ca2bd89f72e9b80dfaffc463e87bec5e75cb (patch)
treeefc138bd5ab54d7dccb2394e8dde074a9b7ac46f /arch/i386
parent93c9a7ff50a5b39dbdf80129c5da89e6d6256bea (diff)
[PATCH] i386: disable local apic timer via command line or dmi quirk
The local APIC timer stops to work in deeper C-States. This is handled by the ACPI code and a broadcast mechanism in the clockevents / tick managment code. Some systems do not expose the deeper C-States to the kernel, but switch into deeper C-States behind the kernels back. This delays the local apic timer interrupts for ever and makes the systems unusable. Add a command line option to disable the local apic timer and a dmi quirk for known broken systems. Andi sayeth: While not wrong by itself i think it is still better to use some heuristic -- like "has battery in ACPI" With the DMI table if the problem is more wide spread we will just continue extending it. But anyways should be ok now for .21 although I'm not really happy with it. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Ingo Molnar <mingo@elte.hu> Cc: john stultz <johnstul@us.ibm.com> Grudgingly-acked-by: Andi Kleen <ak@suse.de> Cc: Len Brown <lenb@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/kernel/apic.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c
index 5cff7970911e..36825117835d 100644
--- a/arch/i386/kernel/apic.c
+++ b/arch/i386/kernel/apic.c
@@ -28,6 +28,7 @@
28#include <linux/clockchips.h> 28#include <linux/clockchips.h>
29#include <linux/acpi_pmtmr.h> 29#include <linux/acpi_pmtmr.h>
30#include <linux/module.h> 30#include <linux/module.h>
31#include <linux/dmi.h>
31 32
32#include <asm/atomic.h> 33#include <asm/atomic.h>
33#include <asm/smp.h> 34#include <asm/smp.h>
@@ -61,6 +62,8 @@ static int enable_local_apic __initdata = 0;
61 62
62/* Local APIC timer verification ok */ 63/* Local APIC timer verification ok */
63static int local_apic_timer_verify_ok; 64static int local_apic_timer_verify_ok;
65/* Disable local APIC timer from the kernel commandline or via dmi quirk */
66static int local_apic_timer_disabled;
64 67
65/* 68/*
66 * Debug level, exported for io_apic.c 69 * Debug level, exported for io_apic.c
@@ -266,6 +269,32 @@ static void __devinit setup_APIC_timer(void)
266} 269}
267 270
268/* 271/*
272 * Detect systems with known broken BIOS implementations
273 */
274static int __init lapic_check_broken_bios(struct dmi_system_id *d)
275{
276 printk(KERN_NOTICE "%s detected: disabling lapic timer.\n",
277 d->ident);
278 local_apic_timer_disabled = 1;
279 return 0;
280}
281
282static struct dmi_system_id __initdata broken_bios_dmi_table[] = {
283 {
284 /*
285 * BIOS exports only C1 state, but uses deeper power
286 * modes behind the kernels back.
287 */
288 .callback = lapic_check_broken_bios,
289 .ident = "HP nx6325",
290 .matches = {
291 DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"),
292 },
293 },
294 {}
295};
296
297/*
269 * In this functions we calibrate APIC bus clocks to the external timer. 298 * In this functions we calibrate APIC bus clocks to the external timer.
270 * 299 *
271 * We want to do the calibration only once since we want to have local timer 300 * We want to do the calibration only once since we want to have local timer
@@ -340,6 +369,22 @@ void __init setup_boot_APIC_clock(void)
340 long delta, deltapm; 369 long delta, deltapm;
341 int pm_referenced = 0; 370 int pm_referenced = 0;
342 371
372 /* Detect know broken systems */
373 dmi_check_system(broken_bios_dmi_table);
374
375 /*
376 * The local apic timer can be disabled via the kernel
377 * commandline or from the dmi quirk above. Register the lapic
378 * timer as a dummy clock event source on SMP systems, so the
379 * broadcast mechanism is used. On UP systems simply ignore it.
380 */
381 if (local_apic_timer_disabled) {
382 /* No broadcast on UP ! */
383 if (num_possible_cpus() > 1)
384 setup_APIC_timer();
385 return;
386 }
387
343 apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" 388 apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
344 "calibrating APIC timer ...\n"); 389 "calibrating APIC timer ...\n");
345 390
@@ -1179,6 +1224,13 @@ static int __init parse_nolapic(char *arg)
1179} 1224}
1180early_param("nolapic", parse_nolapic); 1225early_param("nolapic", parse_nolapic);
1181 1226
1227static int __init parse_disable_lapic_timer(char *arg)
1228{
1229 local_apic_timer_disabled = 1;
1230 return 0;
1231}
1232early_param("nolapic_timer", parse_disable_lapic_timer);
1233
1182static int __init apic_set_verbosity(char *str) 1234static int __init apic_set_verbosity(char *str)
1183{ 1235{
1184 if (strcmp("debug", str) == 0) 1236 if (strcmp("debug", str) == 0)