1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
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
|