[PATCH] x86-64: Disable local APIC timer use on AMD systems with C1E
authorAndi Kleen <ak@suse.de>
Mon, 2 Apr 2007 10:14:12 +0000 (12:14 +0200)
committerAndi Kleen <andi@basil.nowhere.org>
Mon, 2 Apr 2007 10:14:12 +0000 (12:14 +0200)
AMD dual core laptops with C1E do not run the APIC timer correctly
when they go idle. Previously the code assumed this only happened
on C2 or deeper.  But not all of these systems report support C2.

Use a AMD supplied snippet to detect C1E being enabled and then disable
local apic timer use.

This supercedes an earlier workaround using DMI detection of specific systems.

Thanks to Mark Langsdorf for the detection snippet.

Signed-off-by: Andi Kleen <ak@suse.de>
arch/i386/kernel/apic.c
arch/i386/kernel/cpu/amd.c
include/asm-i386/cpufeature.h
include/asm-i386/msr.h

index e88415282a6f0ebf3024c2a5b1165e95502686e2..93aa911646ad5140310ceeb78c8b410322f4768a 100644 (file)
@@ -271,32 +271,6 @@ static void __devinit setup_APIC_timer(void)
        clockevents_register_device(levt);
 }
 
-/*
- * Detect systems with known broken BIOS implementations
- */
-static int __init lapic_check_broken_bios(struct dmi_system_id *d)
-{
-       printk(KERN_NOTICE "%s detected: disabling lapic timer.\n",
-                      d->ident);
-       local_apic_timer_disabled = 1;
-       return 0;
-}
-
-static struct dmi_system_id __initdata broken_bios_dmi_table[] = {
-       {
-               /*
-                * BIOS exports only C1 state, but uses deeper power
-                * modes behind the kernels back.
-                */
-                 .callback = lapic_check_broken_bios,
-                 .ident = "HP nx6325",
-                 .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"),
-                 },
-        },
-        {}
-};
-
 /*
  * In this functions we calibrate APIC bus clocks to the external timer.
  *
@@ -372,12 +346,12 @@ void __init setup_boot_APIC_clock(void)
        long delta, deltapm;
        int pm_referenced = 0;
 
-       /* Detect know broken systems */
-       dmi_check_system(broken_bios_dmi_table);
+       if (boot_cpu_has(X86_FEATURE_LAPIC_TIMER_BROKEN))
+               local_apic_timer_disabled = 1;
 
        /*
         * The local apic timer can be disabled via the kernel
-        * commandline or from the dmi quirk above. Register the lapic
+        * commandline or from the test above. Register the lapic
         * timer as a dummy clock event source on SMP systems, so the
         * broadcast mechanism is used. On UP systems simply ignore it.
         */
index 41cfea57232bb290d7a81061b7f397c53984d23b..2d47db48297218ee61c6b7b09576aa538561e97a 100644 (file)
 extern void vide(void);
 __asm__(".align 4\nvide: ret");
 
+#define ENABLE_C1E_MASK         0x18000000
+#define CPUID_PROCESSOR_SIGNATURE       1
+#define CPUID_XFAM              0x0ff00000
+#define CPUID_XFAM_K8           0x00000000
+#define CPUID_XFAM_10H          0x00100000
+#define CPUID_XFAM_11H          0x00200000
+#define CPUID_XMOD              0x000f0000
+#define CPUID_XMOD_REV_F        0x00040000
+
+/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
+static __cpuinit int amd_apic_timer_broken(void)
+{
+       u32 lo, hi;
+       u32 eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+       switch (eax & CPUID_XFAM) {
+       case CPUID_XFAM_K8:
+               if ((eax & CPUID_XMOD) < CPUID_XMOD_REV_F)
+                       break;
+       case CPUID_XFAM_10H:
+       case CPUID_XFAM_11H:
+               rdmsr(MSR_K8_ENABLE_C1E, lo, hi);
+               if (lo & ENABLE_C1E_MASK)
+                       return 1;
+                break;
+        default:
+                /* err on the side of caution */
+               return 1;
+        }
+       return 0;
+}
+
 static void __cpuinit init_amd(struct cpuinfo_x86 *c)
 {
        u32 l, h;
@@ -241,6 +272,9 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
 
        if (cpuid_eax(0x80000000) >= 0x80000006)
                num_cache_leaves = 3;
+
+       if (amd_apic_timer_broken())
+               set_bit(X86_FEATURE_LAPIC_TIMER_BROKEN, c->x86_capability);
 }
 
 static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 * c, unsigned int size)
index 3f92b94e0d75893e2e82e8de090cbdaff80a905f..d1b8e4ab6c1a93e3f0130b2d6b70fae88c229868 100644 (file)
@@ -75,6 +75,7 @@
 #define X86_FEATURE_ARCH_PERFMON (3*32+11) /* Intel Architectural PerfMon */
 #define X86_FEATURE_PEBS       (3*32+12)  /* Precise-Event Based Sampling */
 #define X86_FEATURE_BTS                (3*32+13)  /* Branch Trace Store */
+#define X86_FEATURE_LAPIC_TIMER_BROKEN (3*32+ 14) /* lapic timer broken in C1 */
 
 /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
 #define X86_FEATURE_XMM3       (4*32+ 0) /* Streaming SIMD Extensions-3 */
index ec3b6803fd36dc33472c23e63d2b0ee487ecf087..2ad3f30b1a684f3405b8569bcaf3b5d3232d8dcc 100644 (file)
@@ -275,6 +275,8 @@ static inline void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h)
 #define MSR_K7_FID_VID_CTL             0xC0010041
 #define MSR_K7_FID_VID_STATUS          0xC0010042
 
+#define MSR_K8_ENABLE_C1E              0xC0010055
+
 /* extended feature register */
 #define MSR_EFER                       0xc0000080