aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-09-25 15:37:01 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-09-26 12:22:04 -0400
commite66485d747505e9d960b864fc6c37f8b2afafaf0 (patch)
treecf50f5de13363f37c19947e5ffce7dbee6624598
parent3dffec45c2742961dd27f55aba426cb9cf3f0dcd (diff)
x86-64: Disable local APIC timer use on AMD systems with C1E
commit 3556ddfa9284a86a59a9b78fe5894430f6ab4eef titled [PATCH] x86-64: Disable local APIC timer use on AMD systems with C1E solves a problem with AMD dual core laptops e.g. HP nx6325 (Turion 64 X2) with C1E enabled: When both cores go into idle at the same time, then the system switches into C1E state, which is basically the same as C3. This stops the local apic timer. This was debugged right after the dyntick merge on i386 and despite the patch title it fixes only the 32 bit path. x86_64 is still missing this fix. It seems that mainline is not really affected by this issue, as the PIT is running and keeps jiffies incrementing, but that's just waiting for trouble. -mm suffers from this problem due to the x86_64 high resolution timer patches. This is a quick and dirty port of the i386 code to x86_64. I spent quite a time with Rafael to debug the -mm / hrt wreckage until someone pointed us to this. I really had forgotten that we debugged this half a year ago already. Sigh, is it just me or is there something yelling arch/x86 into my ear? Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/x86_64/kernel/setup.c34
-rw-r--r--include/asm-x86_64/apic.h1
2 files changed, 35 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
index af838f6b0b7f..32054bf5ba40 100644
--- a/arch/x86_64/kernel/setup.c
+++ b/arch/x86_64/kernel/setup.c
@@ -546,6 +546,37 @@ static void __init amd_detect_cmp(struct cpuinfo_x86 *c)
546#endif 546#endif
547} 547}
548 548
549#define ENABLE_C1E_MASK 0x18000000
550#define CPUID_PROCESSOR_SIGNATURE 1
551#define CPUID_XFAM 0x0ff00000
552#define CPUID_XFAM_K8 0x00000000
553#define CPUID_XFAM_10H 0x00100000
554#define CPUID_XFAM_11H 0x00200000
555#define CPUID_XMOD 0x000f0000
556#define CPUID_XMOD_REV_F 0x00040000
557
558/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
559static __cpuinit int amd_apic_timer_broken(void)
560{
561 u32 lo, hi;
562 u32 eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
563 switch (eax & CPUID_XFAM) {
564 case CPUID_XFAM_K8:
565 if ((eax & CPUID_XMOD) < CPUID_XMOD_REV_F)
566 break;
567 case CPUID_XFAM_10H:
568 case CPUID_XFAM_11H:
569 rdmsr(MSR_K8_ENABLE_C1E, lo, hi);
570 if (lo & ENABLE_C1E_MASK)
571 return 1;
572 break;
573 default:
574 /* err on the side of caution */
575 return 1;
576 }
577 return 0;
578}
579
549static void __cpuinit init_amd(struct cpuinfo_x86 *c) 580static void __cpuinit init_amd(struct cpuinfo_x86 *c)
550{ 581{
551 unsigned level; 582 unsigned level;
@@ -617,6 +648,9 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
617 /* Family 10 doesn't support C states in MWAIT so don't use it */ 648 /* Family 10 doesn't support C states in MWAIT so don't use it */
618 if (c->x86 == 0x10 && !force_mwait) 649 if (c->x86 == 0x10 && !force_mwait)
619 clear_bit(X86_FEATURE_MWAIT, &c->x86_capability); 650 clear_bit(X86_FEATURE_MWAIT, &c->x86_capability);
651
652 if (amd_apic_timer_broken())
653 disable_apic_timer = 1;
620} 654}
621 655
622static void __cpuinit detect_ht(struct cpuinfo_x86 *c) 656static void __cpuinit detect_ht(struct cpuinfo_x86 *c)
diff --git a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h
index 85125ef3c414..e4580203dde2 100644
--- a/include/asm-x86_64/apic.h
+++ b/include/asm-x86_64/apic.h
@@ -20,6 +20,7 @@ extern int apic_verbosity;
20extern int apic_runs_main_timer; 20extern int apic_runs_main_timer;
21extern int ioapic_force; 21extern int ioapic_force;
22extern int apic_mapped; 22extern int apic_mapped;
23extern int disable_apic_timer;
23 24
24/* 25/*
25 * Define the default level of output to be very little 26 * Define the default level of output to be very little