From edeae90d635501a632efa0c7fe0667aa2cbe29be Mon Sep 17 00:00:00 2001 From: Arjan van de Ven 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 --- 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 + * + * 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 +#include +#include +#include + + +#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