Rev 770 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
582 | mauro | 1 | /* |
2 | * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium |
||
3 | * M (part of the Centrino chipset). |
||
4 | * |
||
5 | * Despite the "SpeedStep" in the name, this is almost entirely unlike |
||
6 | * traditional SpeedStep. |
||
7 | * |
||
8 | * Modelled on speedstep.c |
||
9 | * |
||
10 | * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org> |
||
11 | * |
||
12 | * WARNING WARNING WARNING |
||
13 | * |
||
14 | * This driver manipulates the PERF_CTL MSR, which is only somewhat |
||
15 | * documented. While it seems to work on my laptop, it has not been |
||
16 | * tested anywhere else, and it may not work for you, do strange |
||
17 | * things or simply crash. |
||
18 | */ |
||
19 | |||
20 | #include <linuxcomp.h> |
||
21 | |||
22 | #include <linux/kernel.h> |
||
23 | #include <linux/module.h> |
||
24 | #include <linux/init.h> |
||
25 | #include <linux/cpufreq.h> |
||
26 | |||
27 | #include <asm/msr.h> |
||
28 | #include <asm/processor.h> |
||
29 | #include <asm/cpufeature.h> |
||
30 | |||
31 | #define PFX "speedstep-centrino: " |
||
32 | #define MAINTAINER "Jeremy Fitzhardinge <jeremy@goop.org>" |
||
33 | |||
775 | mauro | 34 | //#define CENTRINO_DEBUG |
582 | mauro | 35 | |
36 | #ifdef CENTRINO_DEBUG |
||
37 | #define dprintk(msg...) printk(msg) |
||
38 | #else |
||
39 | #define dprintk(msg...) do { } while(0) |
||
40 | #endif |
||
41 | |||
42 | struct cpu_model |
||
43 | { |
||
44 | const char *model_name; |
||
45 | unsigned max_freq; /* max clock in kHz */ |
||
46 | |||
47 | struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */ |
||
48 | }; |
||
49 | |||
50 | /* Operating points for current CPU */ |
||
51 | static const struct cpu_model *centrino_model; |
||
52 | |||
53 | /* Computes the correct form for IA32_PERF_CTL MSR for a particular |
||
54 | frequency/voltage operating point; frequency in MHz, volts in mV. |
||
55 | This is stored as "index" in the structure. */ |
||
56 | #define OP(mhz, mv) \ |
||
57 | { \ |
||
58 | .frequency = (mhz) * 1000, \ |
||
59 | .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \ |
||
60 | } |
||
61 | |||
62 | /* |
||
63 | * These voltage tables were derived from the Intel Pentium M |
||
64 | * datasheet, document 25261202.pdf, Table 5. I have verified they |
||
65 | * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium |
||
66 | * M. |
||
67 | */ |
||
68 | |||
69 | /* Ultra Low Voltage Intel Pentium M processor 900MHz */ |
||
70 | static struct cpufreq_frequency_table op_900[] = |
||
71 | { |
||
72 | OP(600, 844), |
||
73 | OP(800, 988), |
||
74 | OP(900, 1004), |
||
75 | { .frequency = CPUFREQ_TABLE_END } |
||
76 | }; |
||
77 | |||
78 | /* Low Voltage Intel Pentium M processor 1.10GHz */ |
||
79 | static struct cpufreq_frequency_table op_1100[] = |
||
80 | { |
||
81 | OP( 600, 956), |
||
82 | OP( 800, 1020), |
||
83 | OP( 900, 1100), |
||
84 | OP(1000, 1164), |
||
85 | OP(1100, 1180), |
||
86 | { .frequency = CPUFREQ_TABLE_END } |
||
87 | }; |
||
88 | |||
89 | |||
90 | /* Low Voltage Intel Pentium M processor 1.20GHz */ |
||
91 | static struct cpufreq_frequency_table op_1200[] = |
||
92 | { |
||
93 | OP( 600, 956), |
||
94 | OP( 800, 1004), |
||
95 | OP( 900, 1020), |
||
96 | OP(1000, 1100), |
||
97 | OP(1100, 1164), |
||
98 | OP(1200, 1180), |
||
99 | { .frequency = CPUFREQ_TABLE_END } |
||
100 | }; |
||
101 | |||
102 | /* Intel Pentium M processor 1.30GHz */ |
||
103 | static struct cpufreq_frequency_table op_1300[] = |
||
104 | { |
||
105 | OP( 600, 956), |
||
106 | OP( 800, 1260), |
||
107 | OP(1000, 1292), |
||
108 | OP(1200, 1356), |
||
109 | OP(1300, 1388), |
||
110 | { .frequency = CPUFREQ_TABLE_END } |
||
111 | }; |
||
112 | |||
113 | /* Intel Pentium M processor 1.40GHz */ |
||
114 | static struct cpufreq_frequency_table op_1400[] = |
||
115 | { |
||
116 | OP( 600, 956), |
||
117 | OP( 800, 1180), |
||
118 | OP(1000, 1308), |
||
119 | OP(1200, 1436), |
||
120 | OP(1400, 1484), |
||
121 | { .frequency = CPUFREQ_TABLE_END } |
||
122 | }; |
||
123 | |||
124 | /* Intel Pentium M processor 1.50GHz */ |
||
125 | static struct cpufreq_frequency_table op_1500[] = |
||
126 | { |
||
127 | OP( 600, 956), |
||
128 | OP( 800, 1116), |
||
129 | OP(1000, 1228), |
||
130 | OP(1200, 1356), |
||
131 | OP(1400, 1452), |
||
132 | OP(1500, 1484), |
||
133 | { .frequency = CPUFREQ_TABLE_END } |
||
134 | }; |
||
135 | |||
136 | /* Intel Pentium M processor 1.60GHz */ |
||
137 | static struct cpufreq_frequency_table op_1600[] = |
||
138 | { |
||
139 | OP( 600, 956), |
||
140 | OP( 800, 1036), |
||
141 | OP(1000, 1164), |
||
142 | OP(1200, 1276), |
||
143 | OP(1400, 1420), |
||
144 | OP(1600, 1484), |
||
145 | { .frequency = CPUFREQ_TABLE_END } |
||
146 | }; |
||
147 | |||
148 | /* Intel Pentium M processor 1.70GHz */ |
||
149 | static struct cpufreq_frequency_table op_1700[] = |
||
150 | { |
||
151 | OP( 600, 956), |
||
152 | OP( 800, 1004), |
||
153 | OP(1000, 1116), |
||
154 | OP(1200, 1228), |
||
155 | OP(1400, 1308), |
||
156 | OP(1700, 1484), |
||
157 | { .frequency = CPUFREQ_TABLE_END } |
||
158 | }; |
||
159 | #undef OP |
||
160 | |||
161 | #define _CPU(max, name) \ |
||
162 | { "Intel(R) Pentium(R) M processor " name "MHz", (max)*1000, op_##max } |
||
163 | #define CPU(max) _CPU(max, #max) |
||
164 | |||
165 | /* CPU models, their operating frequency range, and freq/voltage |
||
166 | operating points */ |
||
167 | static const struct cpu_model models[] = |
||
168 | { |
||
169 | _CPU( 900, " 900"), |
||
170 | CPU(1100), |
||
171 | CPU(1200), |
||
172 | CPU(1300), |
||
173 | CPU(1400), |
||
174 | CPU(1500), |
||
175 | CPU(1600), |
||
176 | CPU(1700), |
||
177 | { 0, } |
||
178 | }; |
||
179 | #undef CPU |
||
180 | |||
181 | /* Extract clock in kHz from PERF_CTL value */ |
||
182 | static unsigned extract_clock(unsigned msr) |
||
183 | { |
||
184 | msr = (msr >> 8) & 0xff; |
||
185 | return msr * 100000; |
||
186 | } |
||
187 | |||
188 | /* Return the current CPU frequency in kHz */ |
||
189 | static unsigned get_cur_freq(void) |
||
190 | { |
||
191 | unsigned l, h; |
||
192 | |||
193 | rdmsr(MSR_IA32_PERF_STATUS, l, h); |
||
194 | return extract_clock(l); |
||
195 | } |
||
196 | |||
197 | static int centrino_cpu_init(struct cpufreq_policy *policy) |
||
198 | { |
||
199 | unsigned freq; |
||
200 | |||
201 | if (policy->cpu != 0 || centrino_model == NULL) |
||
202 | return -ENODEV; |
||
203 | |||
204 | freq = get_cur_freq(); |
||
205 | |||
206 | policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR; |
||
207 | policy->cpuinfo.transition_latency = 10; /* 10uS transition latency */ |
||
208 | policy->cur = freq; |
||
209 | |||
210 | dprintk(KERN_INFO PFX "centrino_cpu_init: policy=%d cur=%dkHz\n", |
||
211 | policy->policy, policy->cur); |
||
770 | mauro | 212 | |
213 | /* Added by Nino */ |
||
214 | cpufreq_frequency_table_get_attr(centrino_model->op_points, policy->cpu); |
||
215 | |||
582 | mauro | 216 | return cpufreq_frequency_table_cpuinfo(policy, centrino_model->op_points); |
217 | } |
||
218 | |||
219 | /** |
||
220 | * centrino_verify - verifies a new CPUFreq policy |
||
221 | * @freq: new policy |
||
222 | * |
||
223 | * Limit must be within this model's frequency range at least one |
||
224 | * border included. |
||
225 | */ |
||
226 | static int centrino_verify (struct cpufreq_policy *policy) |
||
227 | { |
||
228 | return cpufreq_frequency_table_verify(policy, centrino_model->op_points); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * centrino_setpolicy - set a new CPUFreq policy |
||
233 | * @policy: new policy |
||
234 | * |
||
235 | * Sets a new CPUFreq policy. |
||
236 | */ |
||
237 | static int centrino_target (struct cpufreq_policy *policy, |
||
238 | unsigned int target_freq, |
||
239 | unsigned int relation) |
||
240 | { |
||
241 | unsigned int newstate = 0; |
||
242 | unsigned int msr, oldmsr, h; |
||
243 | struct cpufreq_freqs freqs; |
||
244 | |||
245 | if (centrino_model == NULL) |
||
246 | return -ENODEV; |
||
247 | |||
248 | if (cpufreq_frequency_table_target(policy, centrino_model->op_points, target_freq, |
||
249 | relation, &newstate)) |
||
250 | return -EINVAL; |
||
251 | |||
252 | msr = centrino_model->op_points[newstate].index; |
||
253 | rdmsr(MSR_IA32_PERF_CTL, oldmsr, h); |
||
254 | |||
255 | if (msr == (oldmsr & 0xffff)) |
||
256 | return 0; |
||
257 | |||
258 | /* Hm, old frequency can either be the last value we put in |
||
259 | PERF_CTL, or whatever it is now. The trouble is that TM2 |
||
260 | can change it behind our back, which means we never get to |
||
261 | see the speed change. Reading back the current speed would |
||
262 | tell us something happened, but it may leave the things on |
||
263 | the notifier chain confused; we therefore stick to using |
||
264 | the last programmed speed rather than the current speed for |
||
265 | "old". |
||
266 | |||
267 | TODO: work out how the TCC interrupts work, and try to |
||
268 | catch the CPU changing things under us. |
||
269 | */ |
||
270 | freqs.cpu = 0; |
||
271 | freqs.old = extract_clock(oldmsr); |
||
272 | freqs.new = extract_clock(msr); |
||
273 | |||
274 | dprintk(KERN_INFO PFX "target=%dkHz old=%d new=%d msr=%04x\n", |
||
275 | target_freq, freqs.old, freqs.new, msr); |
||
276 | |||
600 | mauro | 277 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
582 | mauro | 278 | |
279 | /* all but 16 LSB are "reserved", so treat them with |
||
280 | care */ |
||
281 | oldmsr &= ~0xffff; |
||
282 | msr &= 0xffff; |
||
283 | oldmsr |= msr; |
||
284 | |||
285 | wrmsr(MSR_IA32_PERF_CTL, oldmsr, h); |
||
286 | |||
600 | mauro | 287 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
582 | mauro | 288 | |
289 | return 0; |
||
290 | } |
||
291 | |||
292 | static struct cpufreq_driver centrino_driver = { |
||
293 | .name = "centrino", /* should be speedstep-centrino, |
||
294 | but there's a 16 char limit */ |
||
295 | .init = centrino_cpu_init, |
||
296 | .verify = centrino_verify, |
||
297 | .target = centrino_target, |
||
298 | .owner = THIS_MODULE, |
||
299 | }; |
||
300 | |||
301 | |||
302 | /** |
||
303 | * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver |
||
304 | * |
||
305 | * Initializes the Enhanced SpeedStep support. Returns -ENODEV on |
||
306 | * unsupported devices, -ENOENT if there's no voltage table for this |
||
307 | * particular CPU model, -EINVAL on problems during initiatization, |
||
308 | * and zero on success. |
||
309 | * |
||
310 | * This is quite picky. Not only does the CPU have to advertise the |
||
311 | * "est" flag in the cpuid capability flags, we look for a specific |
||
312 | * CPU model and stepping, and we need to have the exact model name in |
||
313 | * our voltage tables. That is, be paranoid about not releasing |
||
314 | * someone's valuable magic smoke. |
||
315 | */ |
||
316 | /*static*/ int __init centrino_init(void) |
||
317 | { |
||
318 | struct cpuinfo_x86 *cpu = &new_cpu_data; |
||
319 | const struct cpu_model *model; |
||
320 | unsigned l, h; |
||
321 | |||
322 | if (!cpu_has(cpu, X86_FEATURE_EST)) |
||
323 | return -ENODEV; |
||
324 | |||
325 | /* Only Intel Pentium M stepping 5 for now - add new CPUs as |
||
326 | they appear after making sure they use PERF_CTL in the same |
||
327 | way. */ |
||
328 | if (cpu->x86_vendor != X86_VENDOR_INTEL || |
||
329 | cpu->x86 != 6 || |
||
330 | cpu->x86_model != 9 || |
||
331 | cpu->x86_mask != 5) { |
||
332 | printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: " |
||
333 | "send /proc/cpuinfo to " MAINTAINER "\n"); |
||
334 | return -ENODEV; |
||
335 | } |
||
336 | |||
337 | /* Check to see if Enhanced SpeedStep is enabled, and try to |
||
338 | enable it if not. */ |
||
339 | rdmsr(MSR_IA32_MISC_ENABLE, l, h); |
||
340 | |||
341 | if (!(l & (1<<16))) { |
||
342 | l |= (1<<16); |
||
343 | wrmsr(MSR_IA32_MISC_ENABLE, l, h); |
||
344 | |||
345 | /* check to see if it stuck */ |
||
346 | rdmsr(MSR_IA32_MISC_ENABLE, l, h); |
||
347 | if (!(l & (1<<16))) { |
||
348 | printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n"); |
||
349 | return -ENODEV; |
||
350 | } |
||
351 | } |
||
352 | |||
353 | for(model = models; model->model_name != NULL; model++) |
||
354 | if (strcmp(cpu->x86_model_id, model->model_name) == 0) |
||
355 | break; |
||
356 | if (model->model_name == NULL) { |
||
357 | printk(KERN_INFO PFX "no support for CPU model \"%s\": " |
||
358 | "send /proc/cpuinfo to " MAINTAINER "\n", |
||
359 | cpu->x86_model_id); |
||
360 | return -ENOENT; |
||
361 | } |
||
362 | |||
363 | centrino_model = model; |
||
364 | |||
365 | printk(KERN_INFO PFX "found \"%s\": max frequency: %dkHz\n", |
||
366 | model->model_name, model->max_freq); |
||
367 | |||
368 | return cpufreq_register_driver(¢rino_driver); |
||
369 | } |
||
370 | |||
371 | /*static*/ void __exit centrino_exit(void) |
||
372 | { |
||
373 | cpufreq_unregister_driver(¢rino_driver); |
||
374 | } |
||
375 | |||
376 | MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>"); |
||
377 | MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors."); |
||
378 | MODULE_LICENSE ("GPL"); |
||
379 | |||
380 | module_init(centrino_init); |
||
381 | module_exit(centrino_exit); |