summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2009-06-09 04:17:36 +0000
committerariff <ariff@FreeBSD.org>2009-06-09 04:17:36 +0000
commit9df3e8cc3afb415d1db7a64a9ba048a8c427e98e (patch)
tree3349f1ba3fd3eb54fae14b784c8da6adff0514b6 /sys/amd64
parent300fc50c56b4d3969585b2e03250f989251b23ff (diff)
downloadFreeBSD-src-9df3e8cc3afb415d1db7a64a9ba048a8c427e98e.zip
FreeBSD-src-9df3e8cc3afb415d1db7a64a9ba048a8c427e98e.tar.gz
Move C1E workaround into its own idle function. Previous workaround works
only during initial booting process, while there are laptops/BIOSes that tend to act 'smarter' by force enabling C1E if the main power adapter being pulled out, rendering previous workaround ineffective. Given the fact that we still rely on local APIC to drive timer interrupt, this workaround should keep all Turion (probably Phenom too) X\d+ alive whether its on battery power or not. URL: http://lists.freebsd.org/pipermail/freebsd-acpi/2008-April/004858.html http://lists.freebsd.org/pipermail/freebsd-acpi/2008-May/004888.html Tested by: Peter Jeremy <peterjeremy at optushome d com d au>
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/amd64/local_apic.c23
-rw-r--r--sys/amd64/amd64/machdep.c73
2 files changed, 73 insertions, 23 deletions
diff --git a/sys/amd64/amd64/local_apic.c b/sys/amd64/amd64/local_apic.c
index 0a102ee..6ed6c9c 100644
--- a/sys/amd64/amd64/local_apic.c
+++ b/sys/amd64/amd64/local_apic.c
@@ -329,29 +329,6 @@ lapic_setup(int boot)
/* XXX: Error and thermal LVTs */
- if (cpu_vendor_id == CPU_VENDOR_AMD) {
- /*
- * Detect the presence of C1E capability mostly on latest
- * dual-cores (or future) k8 family. This feature renders
- * the local APIC timer dead, so we disable it by reading
- * the Interrupt Pending Message register and clearing both
- * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
- *
- * Reference:
- * "BIOS and Kernel Developer's Guide for AMD NPT
- * Family 0Fh Processors"
- * #32559 revision 3.00
- */
- if ((cpu_id & 0x00000f00) == 0x00000f00 &&
- (cpu_id & 0x0fff0000) >= 0x00040000) {
- uint64_t msr;
-
- msr = rdmsr(0xc0010055);
- if (msr & 0x18000000)
- wrmsr(0xc0010055, msr & ~0x18000000ULL);
- }
- }
-
intr_restore(eflags);
}
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 6993dea..de89c3b 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -600,6 +600,69 @@ cpu_idle_acpi(int busy)
__asm __volatile("sti; hlt");
}
+static int cpu_ident_amdc1e = 0;
+
+static int
+cpu_probe_amdc1e(void)
+{
+ int i;
+
+ /*
+ * Forget it, if we're not using local APIC timer.
+ */
+ if (resource_disabled("apic", 0) ||
+ (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0))
+ return (0);
+
+ /*
+ * Detect the presence of C1E capability mostly on latest
+ * dual-cores (or future) k8 family.
+ */
+ if (cpu_vendor_id == CPU_VENDOR_AMD &&
+ (cpu_id & 0x00000f00) == 0x00000f00 &&
+ (cpu_id & 0x0fff0000) >= 0x00040000) {
+ cpu_ident_amdc1e = 1;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * C1E renders the local APIC timer dead, so we disable it by
+ * reading the Interrupt Pending Message register and clearing
+ * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
+ *
+ * Reference:
+ * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors"
+ * #32559 revision 3.00+
+ */
+#define MSR_AMDK8_IPM 0xc0010055
+#define AMDK8_SMIONCMPHALT (1ULL << 27)
+#define AMDK8_C1EONCMPHALT (1ULL << 28)
+#define AMDK8_CMPHALT (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT)
+
+static void
+cpu_idle_amdc1e(int busy)
+{
+
+ disable_intr();
+ if (sched_runnable())
+ enable_intr();
+ else {
+ uint64_t msr;
+
+ msr = rdmsr(MSR_AMDK8_IPM);
+ if (msr & AMDK8_CMPHALT)
+ wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT);
+
+ if (cpu_idle_hook)
+ cpu_idle_hook();
+ else
+ __asm __volatile("sti; hlt");
+ }
+}
+
static void
cpu_idle_spin(int busy)
{
@@ -697,6 +760,7 @@ struct {
{ cpu_idle_spin, "spin" },
{ cpu_idle_mwait, "mwait" },
{ cpu_idle_mwait_hlt, "mwait_hlt" },
+ { cpu_idle_amdc1e, "amdc1e" },
{ cpu_idle_hlt, "hlt" },
{ cpu_idle_acpi, "acpi" },
{ NULL, NULL }
@@ -715,6 +779,9 @@ idle_sysctl_available(SYSCTL_HANDLER_ARGS)
if (strstr(idle_tbl[i].id_name, "mwait") &&
(cpu_feature2 & CPUID2_MON) == 0)
continue;
+ if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+ cpu_ident_amdc1e == 0)
+ continue;
p += sprintf(p, "%s, ", idle_tbl[i].id_name);
}
error = sysctl_handle_string(oidp, avail, 0, req);
@@ -745,6 +812,9 @@ idle_sysctl(SYSCTL_HANDLER_ARGS)
if (strstr(idle_tbl[i].id_name, "mwait") &&
(cpu_feature2 & CPUID2_MON) == 0)
continue;
+ if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+ cpu_ident_amdc1e == 0)
+ continue;
if (strcmp(idle_tbl[i].id_name, buf))
continue;
cpu_idle_fn = idle_tbl[i].id_fn;
@@ -1593,6 +1663,9 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
}
#endif
+ if (cpu_probe_amdc1e())
+ cpu_idle_fn = cpu_idle_amdc1e;
+
/* Location of kernel stack for locore */
return ((u_int64_t)thread0.td_pcb);
}
OpenPOWER on IntegriCloud