generic-poky/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.32-acpi-cstate-fi...

174 lines
5.1 KiB
Diff

From edeae90d635501a632efa0c7fe0667aa2cbe29be Mon Sep 17 00:00:00 2001
From: Arjan van de Ven <arjan@linux.intel.com>
Date: Mon, 28 Sep 2009 15:14:04 +0200
Subject: [PATCH] acpi: Provide a set of tables to check the BIOS tables for correctness
Today, the BIOS provides us with latency information for each C state.
Unfortunately this information is sometimes put into the BIOS by
apprentice BIOS programmers in a hurry, and as a result, it occasionally
contains utter garbage.
This patch adds a table based verification; if the CPU is known in the table,
the values the BIOS provides to us are corrected for the apprentice-factor
so that the CPUIDLE code can rely on the latency and break-even values
to be reasonably sane.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
---
drivers/acpi/Makefile | 2 +-
drivers/acpi/processor_idle.c | 3 +
drivers/acpi/processor_mwait_table.c | 110 ++++++++++++++++++++++++++++++++++
include/acpi/processor.h | 3 +
4 files changed, 117 insertions(+), 1 deletions(-)
create mode 100644 drivers/acpi/processor_mwait_table.c
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 82cd49d..ab56b28 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -60,5 +60,5 @@ obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o
# processor has its own "processor." module_param namespace
processor-y := processor_core.o processor_throttling.o
-processor-y += processor_idle.o processor_thermal.o
+processor-y += processor_idle.o processor_thermal.o processor_mwait_table.o
processor-$(CONFIG_CPU_FREQ) += processor_perflib.o
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index cc61a62..db444a0 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -1088,6 +1088,9 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
state->target_residency = cx->latency * latency_factor;
state->power_usage = cx->power;
+ if (cx->entry_method == ACPI_CSTATE_FFH)
+ acpi_verify_mwait_data(state, cx);
+
state->flags = 0;
switch (cx->type) {
case ACPI_STATE_C1:
diff --git a/drivers/acpi/processor_mwait_table.c b/drivers/acpi/processor_mwait_table.c
new file mode 100644
index 0000000..f29c28c
--- /dev/null
+++ b/drivers/acpi/processor_mwait_table.c
@@ -0,0 +1,102 @@
+/*
+ * processor_mwait_table.c: BIOS table verification/correction
+ *
+ * (C) Copyright 2009 Intel Corporation
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <asm/processor.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#include <linux/cpuidle.h>
+
+
+#define ATLEAST 1
+#define ATMOST 2
+#define EXACTLY 3
+
+#define MAX_ENTRIES 12
+
+struct mwait_entry {
+ unsigned int mwait_value;
+ unsigned long exit_latency;
+ unsigned long break_even_point;
+ int compare_method;
+};
+
+struct cpu_entry {
+ int vendor;
+ int family;
+ int model;
+
+ struct mwait_entry entries[MAX_ENTRIES];
+};
+
+static struct cpu_entry mwait_entries[] =
+{
+ /* Intel "Atom" CPUs */
+ {.vendor = X86_VENDOR_INTEL, .family = 6, . model = 28,
+ .entries = {
+ {0x00, 1, 1, ATLEAST},
+ {0x10, 2, 20, ATLEAST},
+ {0x30, 57, 300, ATLEAST},
+ {0x50, 64, 4000, ATLEAST},
+ }
+ },
+
+
+};
+
+
+static unsigned long
+compare_and_set(unsigned long original, unsigned long new, int compare)
+{
+ if (compare == EXACTLY)
+ return new;
+ if (compare == ATLEAST && new > original)
+ return new;
+ if (compare == ATMOST && new < original)
+ return new;
+ return original;
+}
+
+
+void acpi_verify_mwait_data(struct cpuidle_state *state,
+ struct acpi_processor_cx *cx)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ int i;
+
+ struct cpuinfo_x86 *cpudata = &boot_cpu_data;
+
+
+ for (i = 0; i < ARRAY_SIZE(mwait_entries); i++) {
+ int j;
+ if (mwait_entries[i].vendor != cpudata->x86_vendor)
+ continue;
+ if (mwait_entries[i].family != cpudata->x86)
+ continue;
+ if (mwait_entries[i].model != cpudata->x86_model)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(mwait_entries[i].entries); j++) {
+ if (!mwait_entries[i].entries[j].compare_method)
+ continue;
+ if (mwait_entries[i].entries[j].mwait_value != cx->address)
+ continue;
+ state->exit_latency = compare_and_set(state->exit_latency,
+ mwait_entries[i].entries[j].exit_latency,
+ mwait_entries[i].entries[j].compare_method);
+ state->target_residency = compare_and_set(state->target_residency,
+ mwait_entries[i].entries[j].break_even_point,
+ mwait_entries[i].entries[j].compare_method);
+ break;
+ }
+ }
+#endif
+}
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 740ac3a..175e4d1 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -352,5 +352,8 @@ static inline void acpi_thermal_cpufreq_exit(void)
return;
}
#endif
+extern void acpi_verify_mwait_data(struct cpuidle_state *state,
+ struct acpi_processor_cx *cx);
+
#endif
--
1.6.2.5