Re: [PATCH] i386: add command line option "local_apic_timer_c2_ok"

Andi Kleen
2007-03-29T20:51:21Z

> Reviewed but not tested. Needs to be wrapped in an AMD specific
> call.

Here's a patch. I don't have a system with C1E, so i only tested that
the apic timer still works on a older AMD box.

Would be good if someone with a Turion laptop, especially the HP nx6325
could test it with CONFIG_NO_HZ enabled.

-Andi

Disable local APIC timer use on AMD systems with C1E

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.

Signed-off-by: Andi Kleen <ak@suse.de>

Index: linux/arch/i386/kernel/apic.c
===================================================================
--- linux.orig/arch/i386/kernel/apic.c
+++ linux/arch/i386/kernel/apic.c
@@ -272,32 +272,6 @@ static void __devinit setup_APIC_timer(v
}

/*
- * 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.
*
* We want to do the calibration only once since we want to have local timer
@@ -357,6 +331,44 @@ static void __init lapic_cal_handler(str
}
}

+#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 __init 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 __init int apic_timer_broken(void)
+{
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ return amd_apic_timer_broken();
+ return 0;
+}
+
/*
* Setup the boot APIC
*
@@ -372,12 +384,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 (apic_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: linux/include/asm-i386/msr.h
===================================================================
--- linux.orig/include/asm-i386/msr.h
+++ linux/include/asm-i386/msr.h
@@ -275,6 +275,8 @@ static inline void wrmsr_on_cpu(unsigned
#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