diff options
Diffstat (limited to 'arch/i386/kernel/cpu/cpufreq/longhaul.c')
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.c | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c new file mode 100644 index 000000000000..ab0f9f5aac11 --- /dev/null +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c | |||
@@ -0,0 +1,658 @@ | |||
1 | /* | ||
2 | * (C) 2001-2004 Dave Jones. <davej@codemonkey.org.uk> | ||
3 | * (C) 2002 Padraig Brady. <padraig@antefacto.com> | ||
4 | * | ||
5 | * Licensed under the terms of the GNU GPL License version 2. | ||
6 | * Based upon datasheets & sample CPUs kindly provided by VIA. | ||
7 | * | ||
8 | * VIA have currently 3 different versions of Longhaul. | ||
9 | * Version 1 (Longhaul) uses the BCR2 MSR at 0x1147. | ||
10 | * It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0. | ||
11 | * Version 2 of longhaul is the same as v1, but adds voltage scaling. | ||
12 | * Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C) | ||
13 | * voltage scaling support has currently been disabled in this driver | ||
14 | * until we have code that gets it right. | ||
15 | * Version 3 of longhaul got renamed to Powersaver and redesigned | ||
16 | * to use the POWERSAVER MSR at 0x110a. | ||
17 | * It is present in Ezra-T (C5M), Nehemiah (C5X) and above. | ||
18 | * It's pretty much the same feature wise to longhaul v2, though | ||
19 | * there is provision for scaling FSB too, but this doesn't work | ||
20 | * too well in practice so we don't even try to use this. | ||
21 | * | ||
22 | * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* | ||
23 | */ | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/cpufreq.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/string.h> | ||
32 | |||
33 | #include <asm/msr.h> | ||
34 | #include <asm/timex.h> | ||
35 | #include <asm/io.h> | ||
36 | |||
37 | #include "longhaul.h" | ||
38 | |||
39 | #define PFX "longhaul: " | ||
40 | |||
41 | #define TYPE_LONGHAUL_V1 1 | ||
42 | #define TYPE_LONGHAUL_V2 2 | ||
43 | #define TYPE_POWERSAVER 3 | ||
44 | |||
45 | #define CPU_SAMUEL 1 | ||
46 | #define CPU_SAMUEL2 2 | ||
47 | #define CPU_EZRA 3 | ||
48 | #define CPU_EZRA_T 4 | ||
49 | #define CPU_NEHEMIAH 5 | ||
50 | |||
51 | static int cpu_model; | ||
52 | static unsigned int numscales=16, numvscales; | ||
53 | static unsigned int fsb; | ||
54 | static int minvid, maxvid; | ||
55 | static unsigned int minmult, maxmult; | ||
56 | static int can_scale_voltage; | ||
57 | static int vrmrev; | ||
58 | |||
59 | /* Module parameters */ | ||
60 | static int dont_scale_voltage; | ||
61 | |||
62 | |||
63 | #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg) | ||
64 | |||
65 | |||
66 | #define __hlt() __asm__ __volatile__("hlt": : :"memory") | ||
67 | |||
68 | /* Clock ratios multiplied by 10 */ | ||
69 | static int clock_ratio[32]; | ||
70 | static int eblcr_table[32]; | ||
71 | static int voltage_table[32]; | ||
72 | static unsigned int highest_speed, lowest_speed; /* kHz */ | ||
73 | static int longhaul_version; | ||
74 | static struct cpufreq_frequency_table *longhaul_table; | ||
75 | |||
76 | #ifdef CONFIG_CPU_FREQ_DEBUG | ||
77 | static char speedbuffer[8]; | ||
78 | |||
79 | static char *print_speed(int speed) | ||
80 | { | ||
81 | if (speed > 1000) { | ||
82 | if (speed%1000 == 0) | ||
83 | sprintf (speedbuffer, "%dGHz", speed/1000); | ||
84 | else | ||
85 | sprintf (speedbuffer, "%d.%dGHz", speed/1000, (speed%1000)/100); | ||
86 | } else | ||
87 | sprintf (speedbuffer, "%dMHz", speed); | ||
88 | |||
89 | return speedbuffer; | ||
90 | } | ||
91 | #endif | ||
92 | |||
93 | |||
94 | static unsigned int calc_speed(int mult) | ||
95 | { | ||
96 | int khz; | ||
97 | khz = (mult/10)*fsb; | ||
98 | if (mult%10) | ||
99 | khz += fsb/2; | ||
100 | khz *= 1000; | ||
101 | return khz; | ||
102 | } | ||
103 | |||
104 | |||
105 | static int longhaul_get_cpu_mult(void) | ||
106 | { | ||
107 | unsigned long invalue=0,lo, hi; | ||
108 | |||
109 | rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); | ||
110 | invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; | ||
111 | if (longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) { | ||
112 | if (lo & (1<<27)) | ||
113 | invalue+=16; | ||
114 | } | ||
115 | return eblcr_table[invalue]; | ||
116 | } | ||
117 | |||
118 | |||
119 | static void do_powersaver(union msr_longhaul *longhaul, | ||
120 | unsigned int clock_ratio_index) | ||
121 | { | ||
122 | int version; | ||
123 | |||
124 | switch (cpu_model) { | ||
125 | case CPU_EZRA_T: | ||
126 | version = 3; | ||
127 | break; | ||
128 | case CPU_NEHEMIAH: | ||
129 | version = 0xf; | ||
130 | break; | ||
131 | default: | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
136 | longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf; | ||
137 | longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; | ||
138 | longhaul->bits.EnableSoftBusRatio = 1; | ||
139 | longhaul->bits.RevisionKey = 0; | ||
140 | local_irq_disable(); | ||
141 | wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
142 | local_irq_enable(); | ||
143 | __hlt(); | ||
144 | |||
145 | rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
146 | longhaul->bits.EnableSoftBusRatio = 0; | ||
147 | longhaul->bits.RevisionKey = version; | ||
148 | local_irq_disable(); | ||
149 | wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
150 | local_irq_enable(); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * longhaul_set_cpu_frequency() | ||
155 | * @clock_ratio_index : bitpattern of the new multiplier. | ||
156 | * | ||
157 | * Sets a new clock ratio. | ||
158 | */ | ||
159 | |||
160 | static void longhaul_setstate(unsigned int clock_ratio_index) | ||
161 | { | ||
162 | int speed, mult; | ||
163 | struct cpufreq_freqs freqs; | ||
164 | union msr_longhaul longhaul; | ||
165 | union msr_bcr2 bcr2; | ||
166 | static unsigned int old_ratio=-1; | ||
167 | |||
168 | if (old_ratio == clock_ratio_index) | ||
169 | return; | ||
170 | old_ratio = clock_ratio_index; | ||
171 | |||
172 | mult = clock_ratio[clock_ratio_index]; | ||
173 | if (mult == -1) | ||
174 | return; | ||
175 | |||
176 | speed = calc_speed(mult); | ||
177 | if ((speed > highest_speed) || (speed < lowest_speed)) | ||
178 | return; | ||
179 | |||
180 | freqs.old = calc_speed(longhaul_get_cpu_mult()); | ||
181 | freqs.new = speed; | ||
182 | freqs.cpu = 0; /* longhaul.c is UP only driver */ | ||
183 | |||
184 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
185 | |||
186 | dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", | ||
187 | fsb, mult/10, mult%10, print_speed(speed/1000)); | ||
188 | |||
189 | switch (longhaul_version) { | ||
190 | |||
191 | /* | ||
192 | * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B]) | ||
193 | * Software controlled multipliers only. | ||
194 | * | ||
195 | * *NB* Until we get voltage scaling working v1 & v2 are the same code. | ||
196 | * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5b] and Ezra [C5C] | ||
197 | */ | ||
198 | case TYPE_LONGHAUL_V1: | ||
199 | case TYPE_LONGHAUL_V2: | ||
200 | rdmsrl (MSR_VIA_BCR2, bcr2.val); | ||
201 | /* Enable software clock multiplier */ | ||
202 | bcr2.bits.ESOFTBF = 1; | ||
203 | bcr2.bits.CLOCKMUL = clock_ratio_index; | ||
204 | local_irq_disable(); | ||
205 | wrmsrl (MSR_VIA_BCR2, bcr2.val); | ||
206 | local_irq_enable(); | ||
207 | |||
208 | __hlt(); | ||
209 | |||
210 | /* Disable software clock multiplier */ | ||
211 | rdmsrl (MSR_VIA_BCR2, bcr2.val); | ||
212 | bcr2.bits.ESOFTBF = 0; | ||
213 | local_irq_disable(); | ||
214 | wrmsrl (MSR_VIA_BCR2, bcr2.val); | ||
215 | local_irq_enable(); | ||
216 | break; | ||
217 | |||
218 | /* | ||
219 | * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N]) | ||
220 | * We can scale voltage with this too, but that's currently | ||
221 | * disabled until we come up with a decent 'match freq to voltage' | ||
222 | * algorithm. | ||
223 | * When we add voltage scaling, we will also need to do the | ||
224 | * voltage/freq setting in order depending on the direction | ||
225 | * of scaling (like we do in powernow-k7.c) | ||
226 | * Nehemiah can do FSB scaling too, but this has never been proven | ||
227 | * to work in practice. | ||
228 | */ | ||
229 | case TYPE_POWERSAVER: | ||
230 | do_powersaver(&longhaul, clock_ratio_index); | ||
231 | break; | ||
232 | } | ||
233 | |||
234 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Centaur decided to make life a little more tricky. | ||
239 | * Only longhaul v1 is allowed to read EBLCR BSEL[0:1]. | ||
240 | * Samuel2 and above have to try and guess what the FSB is. | ||
241 | * We do this by assuming we booted at maximum multiplier, and interpolate | ||
242 | * between that value multiplied by possible FSBs and cpu_mhz which | ||
243 | * was calculated at boot time. Really ugly, but no other way to do this. | ||
244 | */ | ||
245 | |||
246 | #define ROUNDING 0xf | ||
247 | |||
248 | static int _guess(int guess) | ||
249 | { | ||
250 | int target; | ||
251 | |||
252 | target = ((maxmult/10)*guess); | ||
253 | if (maxmult%10 != 0) | ||
254 | target += (guess/2); | ||
255 | target += ROUNDING/2; | ||
256 | target &= ~ROUNDING; | ||
257 | return target; | ||
258 | } | ||
259 | |||
260 | |||
261 | static int guess_fsb(void) | ||
262 | { | ||
263 | int speed = (cpu_khz/1000); | ||
264 | int i; | ||
265 | int speeds[3] = { 66, 100, 133 }; | ||
266 | |||
267 | speed += ROUNDING/2; | ||
268 | speed &= ~ROUNDING; | ||
269 | |||
270 | for (i=0; i<3; i++) { | ||
271 | if (_guess(speeds[i]) == speed) | ||
272 | return speeds[i]; | ||
273 | } | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | |||
278 | static int __init longhaul_get_ranges(void) | ||
279 | { | ||
280 | unsigned long invalue; | ||
281 | unsigned int multipliers[32]= { | ||
282 | 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, | ||
283 | -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; | ||
284 | unsigned int j, k = 0; | ||
285 | union msr_longhaul longhaul; | ||
286 | unsigned long lo, hi; | ||
287 | unsigned int eblcr_fsb_table_v1[] = { 66, 133, 100, -1 }; | ||
288 | unsigned int eblcr_fsb_table_v2[] = { 133, 100, -1, 66 }; | ||
289 | |||
290 | switch (longhaul_version) { | ||
291 | case TYPE_LONGHAUL_V1: | ||
292 | case TYPE_LONGHAUL_V2: | ||
293 | /* Ugh, Longhaul v1 didn't have the min/max MSRs. | ||
294 | Assume min=3.0x & max = whatever we booted at. */ | ||
295 | minmult = 30; | ||
296 | maxmult = longhaul_get_cpu_mult(); | ||
297 | rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); | ||
298 | invalue = (lo & (1<<18|1<<19)) >>18; | ||
299 | if (cpu_model==CPU_SAMUEL || cpu_model==CPU_SAMUEL2) | ||
300 | fsb = eblcr_fsb_table_v1[invalue]; | ||
301 | else | ||
302 | fsb = guess_fsb(); | ||
303 | break; | ||
304 | |||
305 | case TYPE_POWERSAVER: | ||
306 | /* Ezra-T */ | ||
307 | if (cpu_model==CPU_EZRA_T) { | ||
308 | rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); | ||
309 | invalue = longhaul.bits.MaxMHzBR; | ||
310 | if (longhaul.bits.MaxMHzBR4) | ||
311 | invalue += 16; | ||
312 | maxmult=multipliers[invalue]; | ||
313 | |||
314 | invalue = longhaul.bits.MinMHzBR; | ||
315 | if (longhaul.bits.MinMHzBR4 == 1) | ||
316 | minmult = 30; | ||
317 | else | ||
318 | minmult = multipliers[invalue]; | ||
319 | fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; | ||
320 | break; | ||
321 | } | ||
322 | |||
323 | /* Nehemiah */ | ||
324 | if (cpu_model==CPU_NEHEMIAH) { | ||
325 | rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); | ||
326 | |||
327 | /* | ||
328 | * TODO: This code works, but raises a lot of questions. | ||
329 | * - Some Nehemiah's seem to have broken Min/MaxMHzBR's. | ||
330 | * We get around this by using a hardcoded multiplier of 4.0x | ||
331 | * for the minimimum speed, and the speed we booted up at for the max. | ||
332 | * This is done in longhaul_get_cpu_mult() by reading the EBLCR register. | ||
333 | * - According to some VIA documentation EBLCR is only | ||
334 | * in pre-Nehemiah C3s. How this still works is a mystery. | ||
335 | * We're possibly using something undocumented and unsupported, | ||
336 | * But it works, so we don't grumble. | ||
337 | */ | ||
338 | minmult=40; | ||
339 | maxmult=longhaul_get_cpu_mult(); | ||
340 | |||
341 | /* Starting with the 1.2GHz parts, theres a 200MHz bus. */ | ||
342 | if ((cpu_khz/1000) > 1200) | ||
343 | fsb = 200; | ||
344 | else | ||
345 | fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; | ||
346 | break; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n", | ||
351 | minmult/10, minmult%10, maxmult/10, maxmult%10); | ||
352 | |||
353 | if (fsb == -1) { | ||
354 | printk (KERN_INFO PFX "Invalid (reserved) FSB!\n"); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | |||
358 | highest_speed = calc_speed(maxmult); | ||
359 | lowest_speed = calc_speed(minmult); | ||
360 | dprintk ("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb, | ||
361 | print_speed(lowest_speed/1000), | ||
362 | print_speed(highest_speed/1000)); | ||
363 | |||
364 | if (lowest_speed == highest_speed) { | ||
365 | printk (KERN_INFO PFX "highestspeed == lowest, aborting.\n"); | ||
366 | return -EINVAL; | ||
367 | } | ||
368 | if (lowest_speed > highest_speed) { | ||
369 | printk (KERN_INFO PFX "nonsense! lowest (%d > %d) !\n", | ||
370 | lowest_speed, highest_speed); | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | |||
374 | longhaul_table = kmalloc((numscales + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL); | ||
375 | if(!longhaul_table) | ||
376 | return -ENOMEM; | ||
377 | |||
378 | for (j=0; j < numscales; j++) { | ||
379 | unsigned int ratio; | ||
380 | ratio = clock_ratio[j]; | ||
381 | if (ratio == -1) | ||
382 | continue; | ||
383 | if (ratio > maxmult || ratio < minmult) | ||
384 | continue; | ||
385 | longhaul_table[k].frequency = calc_speed(ratio); | ||
386 | longhaul_table[k].index = j; | ||
387 | k++; | ||
388 | } | ||
389 | |||
390 | longhaul_table[k].frequency = CPUFREQ_TABLE_END; | ||
391 | if (!k) { | ||
392 | kfree (longhaul_table); | ||
393 | return -EINVAL; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | |||
400 | static void __init longhaul_setup_voltagescaling(void) | ||
401 | { | ||
402 | union msr_longhaul longhaul; | ||
403 | |||
404 | rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); | ||
405 | |||
406 | if (!(longhaul.bits.RevisionID & 1)) | ||
407 | return; | ||
408 | |||
409 | minvid = longhaul.bits.MinimumVID; | ||
410 | maxvid = longhaul.bits.MaximumVID; | ||
411 | vrmrev = longhaul.bits.VRMRev; | ||
412 | |||
413 | if (minvid == 0 || maxvid == 0) { | ||
414 | printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " | ||
415 | "Voltage scaling disabled.\n", | ||
416 | minvid/1000, minvid%1000, maxvid/1000, maxvid%1000); | ||
417 | return; | ||
418 | } | ||
419 | |||
420 | if (minvid == maxvid) { | ||
421 | printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are " | ||
422 | "both %d.%03d. Voltage scaling disabled\n", | ||
423 | maxvid/1000, maxvid%1000); | ||
424 | return; | ||
425 | } | ||
426 | |||
427 | if (vrmrev==0) { | ||
428 | dprintk ("VRM 8.5 \n"); | ||
429 | memcpy (voltage_table, vrm85scales, sizeof(voltage_table)); | ||
430 | numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25; | ||
431 | } else { | ||
432 | dprintk ("Mobile VRM \n"); | ||
433 | memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table)); | ||
434 | numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5; | ||
435 | } | ||
436 | |||
437 | /* Current voltage isn't readable at first, so we need to | ||
438 | set it to a known value. The spec says to use maxvid */ | ||
439 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; /* FIXME: This is bad. */ | ||
440 | longhaul.bits.EnableSoftVID = 1; | ||
441 | longhaul.bits.SoftVID = maxvid; | ||
442 | wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); | ||
443 | |||
444 | minvid = voltage_table[minvid]; | ||
445 | maxvid = voltage_table[maxvid]; | ||
446 | |||
447 | dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n", | ||
448 | maxvid/1000, maxvid%1000, minvid/1000, minvid%1000, numvscales); | ||
449 | |||
450 | can_scale_voltage = 1; | ||
451 | } | ||
452 | |||
453 | |||
454 | static int longhaul_verify(struct cpufreq_policy *policy) | ||
455 | { | ||
456 | return cpufreq_frequency_table_verify(policy, longhaul_table); | ||
457 | } | ||
458 | |||
459 | |||
460 | static int longhaul_target(struct cpufreq_policy *policy, | ||
461 | unsigned int target_freq, unsigned int relation) | ||
462 | { | ||
463 | unsigned int table_index = 0; | ||
464 | unsigned int new_clock_ratio = 0; | ||
465 | |||
466 | if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) | ||
467 | return -EINVAL; | ||
468 | |||
469 | new_clock_ratio = longhaul_table[table_index].index & 0xFF; | ||
470 | |||
471 | longhaul_setstate(new_clock_ratio); | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | |||
477 | static unsigned int longhaul_get(unsigned int cpu) | ||
478 | { | ||
479 | if (cpu) | ||
480 | return 0; | ||
481 | return calc_speed(longhaul_get_cpu_mult()); | ||
482 | } | ||
483 | |||
484 | |||
485 | static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | ||
486 | { | ||
487 | struct cpuinfo_x86 *c = cpu_data; | ||
488 | char *cpuname=NULL; | ||
489 | int ret; | ||
490 | |||
491 | switch (c->x86_model) { | ||
492 | case 6: | ||
493 | cpu_model = CPU_SAMUEL; | ||
494 | cpuname = "C3 'Samuel' [C5A]"; | ||
495 | longhaul_version = TYPE_LONGHAUL_V1; | ||
496 | memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); | ||
497 | memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); | ||
498 | break; | ||
499 | |||
500 | case 7: | ||
501 | longhaul_version = TYPE_LONGHAUL_V1; | ||
502 | switch (c->x86_mask) { | ||
503 | case 0: | ||
504 | cpu_model = CPU_SAMUEL2; | ||
505 | cpuname = "C3 'Samuel 2' [C5B]"; | ||
506 | /* Note, this is not a typo, early Samuel2's had Samuel1 ratios. */ | ||
507 | memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); | ||
508 | memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); | ||
509 | break; | ||
510 | case 1 ... 15: | ||
511 | if (c->x86_mask < 8) { | ||
512 | cpu_model = CPU_SAMUEL2; | ||
513 | cpuname = "C3 'Samuel 2' [C5B]"; | ||
514 | } else { | ||
515 | cpu_model = CPU_EZRA; | ||
516 | cpuname = "C3 'Ezra' [C5C]"; | ||
517 | } | ||
518 | memcpy (clock_ratio, ezra_clock_ratio, sizeof(ezra_clock_ratio)); | ||
519 | memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); | ||
520 | break; | ||
521 | } | ||
522 | break; | ||
523 | |||
524 | case 8: | ||
525 | cpu_model = CPU_EZRA_T; | ||
526 | cpuname = "C3 'Ezra-T' [C5M]"; | ||
527 | longhaul_version = TYPE_POWERSAVER; | ||
528 | numscales=32; | ||
529 | memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio)); | ||
530 | memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr)); | ||
531 | break; | ||
532 | |||
533 | case 9: | ||
534 | cpu_model = CPU_NEHEMIAH; | ||
535 | longhaul_version = TYPE_POWERSAVER; | ||
536 | numscales=32; | ||
537 | switch (c->x86_mask) { | ||
538 | case 0 ... 1: | ||
539 | cpuname = "C3 'Nehemiah A' [C5N]"; | ||
540 | memcpy (clock_ratio, nehemiah_a_clock_ratio, sizeof(nehemiah_a_clock_ratio)); | ||
541 | memcpy (eblcr_table, nehemiah_a_eblcr, sizeof(nehemiah_a_eblcr)); | ||
542 | break; | ||
543 | case 2 ... 4: | ||
544 | cpuname = "C3 'Nehemiah B' [C5N]"; | ||
545 | memcpy (clock_ratio, nehemiah_b_clock_ratio, sizeof(nehemiah_b_clock_ratio)); | ||
546 | memcpy (eblcr_table, nehemiah_b_eblcr, sizeof(nehemiah_b_eblcr)); | ||
547 | break; | ||
548 | case 5 ... 15: | ||
549 | cpuname = "C3 'Nehemiah C' [C5N]"; | ||
550 | memcpy (clock_ratio, nehemiah_c_clock_ratio, sizeof(nehemiah_c_clock_ratio)); | ||
551 | memcpy (eblcr_table, nehemiah_c_eblcr, sizeof(nehemiah_c_eblcr)); | ||
552 | break; | ||
553 | } | ||
554 | break; | ||
555 | |||
556 | default: | ||
557 | cpuname = "Unknown"; | ||
558 | break; | ||
559 | } | ||
560 | |||
561 | printk (KERN_INFO PFX "VIA %s CPU detected. ", cpuname); | ||
562 | switch (longhaul_version) { | ||
563 | case TYPE_LONGHAUL_V1: | ||
564 | case TYPE_LONGHAUL_V2: | ||
565 | printk ("Longhaul v%d supported.\n", longhaul_version); | ||
566 | break; | ||
567 | case TYPE_POWERSAVER: | ||
568 | printk ("Powersaver supported.\n"); | ||
569 | break; | ||
570 | }; | ||
571 | |||
572 | ret = longhaul_get_ranges(); | ||
573 | if (ret != 0) | ||
574 | return ret; | ||
575 | |||
576 | if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) && | ||
577 | (dont_scale_voltage==0)) | ||
578 | longhaul_setup_voltagescaling(); | ||
579 | |||
580 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
581 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | ||
582 | policy->cur = calc_speed(longhaul_get_cpu_mult()); | ||
583 | |||
584 | ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table); | ||
585 | if (ret) | ||
586 | return ret; | ||
587 | |||
588 | cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) | ||
594 | { | ||
595 | cpufreq_frequency_table_put_attr(policy->cpu); | ||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static struct freq_attr* longhaul_attr[] = { | ||
600 | &cpufreq_freq_attr_scaling_available_freqs, | ||
601 | NULL, | ||
602 | }; | ||
603 | |||
604 | static struct cpufreq_driver longhaul_driver = { | ||
605 | .verify = longhaul_verify, | ||
606 | .target = longhaul_target, | ||
607 | .get = longhaul_get, | ||
608 | .init = longhaul_cpu_init, | ||
609 | .exit = __devexit_p(longhaul_cpu_exit), | ||
610 | .name = "longhaul", | ||
611 | .owner = THIS_MODULE, | ||
612 | .attr = longhaul_attr, | ||
613 | }; | ||
614 | |||
615 | |||
616 | static int __init longhaul_init(void) | ||
617 | { | ||
618 | struct cpuinfo_x86 *c = cpu_data; | ||
619 | |||
620 | if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) | ||
621 | return -ENODEV; | ||
622 | |||
623 | switch (c->x86_model) { | ||
624 | case 6 ... 9: | ||
625 | return cpufreq_register_driver(&longhaul_driver); | ||
626 | default: | ||
627 | printk (KERN_INFO PFX "Unknown VIA CPU. Contact davej@codemonkey.org.uk\n"); | ||
628 | } | ||
629 | |||
630 | return -ENODEV; | ||
631 | } | ||
632 | |||
633 | |||
634 | static void __exit longhaul_exit(void) | ||
635 | { | ||
636 | int i=0; | ||
637 | |||
638 | for (i=0; i < numscales; i++) { | ||
639 | if (clock_ratio[i] == maxmult) { | ||
640 | longhaul_setstate(i); | ||
641 | break; | ||
642 | } | ||
643 | } | ||
644 | |||
645 | cpufreq_unregister_driver(&longhaul_driver); | ||
646 | kfree(longhaul_table); | ||
647 | } | ||
648 | |||
649 | module_param (dont_scale_voltage, int, 0644); | ||
650 | MODULE_PARM_DESC(dont_scale_voltage, "Don't scale voltage of processor"); | ||
651 | |||
652 | MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>"); | ||
653 | MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); | ||
654 | MODULE_LICENSE ("GPL"); | ||
655 | |||
656 | module_init(longhaul_init); | ||
657 | module_exit(longhaul_exit); | ||
658 | |||