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