2 * (C) 2001-2004 Dave Jones. <davej@codemonkey.org.uk>
3 * (C) 2002 Padraig Brady. <padraig@antefacto.com>
5 * Licensed under the terms of the GNU GPL License version 2.
6 * Based upon datasheets & sample CPUs kindly provided by VIA.
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.
22 * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
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 #include <linux/pci.h>
35 #include <asm/timex.h>
40 #define PFX "longhaul: "
42 #define TYPE_LONGHAUL_V1 1
43 #define TYPE_LONGHAUL_V2 2
44 #define TYPE_POWERSAVER 3
50 #define CPU_NEHEMIAH 5
53 static unsigned int numscales
=16, numvscales
;
54 static unsigned int fsb
;
55 static int minvid
, maxvid
;
56 static unsigned int minmult
, maxmult
;
57 static int can_scale_voltage
;
60 /* Module parameters */
61 static int dont_scale_voltage
;
64 #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
67 #define __hlt() __asm__ __volatile__("hlt": : :"memory")
69 /* Clock ratios multiplied by 10 */
70 static int clock_ratio
[32];
71 static int eblcr_table
[32];
72 static int voltage_table
[32];
73 static unsigned int highest_speed
, lowest_speed
; /* kHz */
74 static int longhaul_version
;
75 static struct cpufreq_frequency_table
*longhaul_table
;
77 #ifdef CONFIG_CPU_FREQ_DEBUG
78 static char speedbuffer
[8];
80 static char *print_speed(int speed
)
84 sprintf (speedbuffer
, "%dGHz", speed
/1000);
86 sprintf (speedbuffer
, "%d.%dGHz", speed
/1000, (speed
%1000)/100);
88 sprintf (speedbuffer
, "%dMHz", speed
);
95 static unsigned int calc_speed(int mult
)
106 static int longhaul_get_cpu_mult(void)
108 unsigned long invalue
=0,lo
, hi
;
110 rdmsr (MSR_IA32_EBL_CR_POWERON
, lo
, hi
);
111 invalue
= (lo
& (1<<22|1<<23|1<<24|1<<25)) >>22;
112 if (longhaul_version
==TYPE_LONGHAUL_V2
|| longhaul_version
==TYPE_POWERSAVER
) {
116 return eblcr_table
[invalue
];
120 static void do_powersaver(union msr_longhaul
*longhaul
,
121 unsigned int clock_ratio_index
)
125 unsigned int tmp_mask
;
142 rdmsrl(MSR_VIA_LONGHAUL
, longhaul
->val
);
143 longhaul
->bits
.SoftBusRatio
= clock_ratio_index
& 0xf;
144 longhaul
->bits
.SoftBusRatio4
= (clock_ratio_index
& 0x10) >> 4;
145 longhaul
->bits
.EnableSoftBusRatio
= 1;
146 longhaul
->bits
.RevisionKey
= 0;
149 local_irq_save(flags
);
152 * get current pci bus master state for all devices
153 * and clear bus master bit
158 dev
= pci_get_device(PCI_ANY_ID
, PCI_ANY_ID
, dev
);
160 pci_read_config_word(dev
, PCI_COMMAND
, &pci_cmd
);
161 cmd_state
[i
++] = pci_cmd
;
162 pci_cmd
&= ~PCI_COMMAND_MASTER
;
163 pci_write_config_word(dev
, PCI_COMMAND
, pci_cmd
);
165 } while (dev
!= NULL
);
167 tmp_mask
=inb(0x21); /* works on C3. save mask. */
168 outb(0xFE,0x21); /* TMR0 only */
169 outb(0xFF,0x80); /* delay */
174 wrmsrl(MSR_VIA_LONGHAUL
, longhaul
->val
);
179 outb(tmp_mask
,0x21); /* restore mask */
181 /* restore pci bus master state for all devices */
185 dev
= pci_get_device(PCI_ANY_ID
, PCI_ANY_ID
, dev
);
187 pci_cmd
= cmd_state
[i
++];
188 pci_write_config_byte(dev
, PCI_COMMAND
, pci_cmd
);
190 } while (dev
!= NULL
);
191 local_irq_restore(flags
);
194 /* disable bus ratio bit */
195 rdmsrl(MSR_VIA_LONGHAUL
, longhaul
->val
);
196 longhaul
->bits
.EnableSoftBusRatio
= 0;
197 longhaul
->bits
.RevisionKey
= version
;
198 wrmsrl(MSR_VIA_LONGHAUL
, longhaul
->val
);
202 * longhaul_set_cpu_frequency()
203 * @clock_ratio_index : bitpattern of the new multiplier.
205 * Sets a new clock ratio.
208 static void longhaul_setstate(unsigned int clock_ratio_index
)
211 struct cpufreq_freqs freqs
;
212 union msr_longhaul longhaul
;
214 static unsigned int old_ratio
=-1;
216 if (old_ratio
== clock_ratio_index
)
218 old_ratio
= clock_ratio_index
;
220 mult
= clock_ratio
[clock_ratio_index
];
224 speed
= calc_speed(mult
);
225 if ((speed
> highest_speed
) || (speed
< lowest_speed
))
228 freqs
.old
= calc_speed(longhaul_get_cpu_mult());
230 freqs
.cpu
= 0; /* longhaul.c is UP only driver */
232 cpufreq_notify_transition(&freqs
, CPUFREQ_PRECHANGE
);
234 dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
235 fsb
, mult
/10, mult
%10, print_speed(speed
/1000));
237 switch (longhaul_version
) {
240 * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
241 * Software controlled multipliers only.
243 * *NB* Until we get voltage scaling working v1 & v2 are the same code.
244 * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5b] and Ezra [C5C]
246 case TYPE_LONGHAUL_V1
:
247 case TYPE_LONGHAUL_V2
:
248 rdmsrl (MSR_VIA_BCR2
, bcr2
.val
);
249 /* Enable software clock multiplier */
250 bcr2
.bits
.ESOFTBF
= 1;
251 bcr2
.bits
.CLOCKMUL
= clock_ratio_index
;
253 wrmsrl (MSR_VIA_BCR2
, bcr2
.val
);
258 /* Disable software clock multiplier */
259 rdmsrl (MSR_VIA_BCR2
, bcr2
.val
);
260 bcr2
.bits
.ESOFTBF
= 0;
262 wrmsrl (MSR_VIA_BCR2
, bcr2
.val
);
267 * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
268 * We can scale voltage with this too, but that's currently
269 * disabled until we come up with a decent 'match freq to voltage'
271 * When we add voltage scaling, we will also need to do the
272 * voltage/freq setting in order depending on the direction
273 * of scaling (like we do in powernow-k7.c)
274 * Nehemiah can do FSB scaling too, but this has never been proven
275 * to work in practice.
277 case TYPE_POWERSAVER
:
278 do_powersaver(&longhaul
, clock_ratio_index
);
282 cpufreq_notify_transition(&freqs
, CPUFREQ_POSTCHANGE
);
286 * Centaur decided to make life a little more tricky.
287 * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
288 * Samuel2 and above have to try and guess what the FSB is.
289 * We do this by assuming we booted at maximum multiplier, and interpolate
290 * between that value multiplied by possible FSBs and cpu_mhz which
291 * was calculated at boot time. Really ugly, but no other way to do this.
296 static int _guess(int guess
)
300 target
= ((maxmult
/10)*guess
);
303 target
+= ROUNDING
/2;
309 static int guess_fsb(void)
311 int speed
= (cpu_khz
/1000);
313 int speeds
[3] = { 66, 100, 133 };
318 for (i
=0; i
<3; i
++) {
319 if (_guess(speeds
[i
]) == speed
)
326 static int __init
longhaul_get_ranges(void)
328 unsigned long invalue
;
329 unsigned int multipliers
[32]= {
330 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65,
331 -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 };
332 unsigned int j
, k
= 0;
333 union msr_longhaul longhaul
;
334 unsigned long lo
, hi
;
335 unsigned int eblcr_fsb_table_v1
[] = { 66, 133, 100, -1 };
336 unsigned int eblcr_fsb_table_v2
[] = { 133, 100, -1, 66 };
338 switch (longhaul_version
) {
339 case TYPE_LONGHAUL_V1
:
340 case TYPE_LONGHAUL_V2
:
341 /* Ugh, Longhaul v1 didn't have the min/max MSRs.
342 Assume min=3.0x & max = whatever we booted at. */
344 maxmult
= longhaul_get_cpu_mult();
345 rdmsr (MSR_IA32_EBL_CR_POWERON
, lo
, hi
);
346 invalue
= (lo
& (1<<18|1<<19)) >>18;
347 if (cpu_model
==CPU_SAMUEL
|| cpu_model
==CPU_SAMUEL2
)
348 fsb
= eblcr_fsb_table_v1
[invalue
];
353 case TYPE_POWERSAVER
:
355 if (cpu_model
==CPU_EZRA_T
) {
356 rdmsrl (MSR_VIA_LONGHAUL
, longhaul
.val
);
357 invalue
= longhaul
.bits
.MaxMHzBR
;
358 if (longhaul
.bits
.MaxMHzBR4
)
360 maxmult
=multipliers
[invalue
];
362 invalue
= longhaul
.bits
.MinMHzBR
;
363 if (longhaul
.bits
.MinMHzBR4
== 1)
366 minmult
= multipliers
[invalue
];
367 fsb
= eblcr_fsb_table_v2
[longhaul
.bits
.MaxMHzFSB
];
372 if (cpu_model
==CPU_NEHEMIAH
) {
373 rdmsrl (MSR_VIA_LONGHAUL
, longhaul
.val
);
376 * TODO: This code works, but raises a lot of questions.
377 * - Some Nehemiah's seem to have broken Min/MaxMHzBR's.
378 * We get around this by using a hardcoded multiplier of 4.0x
379 * for the minimimum speed, and the speed we booted up at for the max.
380 * This is done in longhaul_get_cpu_mult() by reading the EBLCR register.
381 * - According to some VIA documentation EBLCR is only
382 * in pre-Nehemiah C3s. How this still works is a mystery.
383 * We're possibly using something undocumented and unsupported,
384 * But it works, so we don't grumble.
387 maxmult
=longhaul_get_cpu_mult();
389 /* Starting with the 1.2GHz parts, theres a 200MHz bus. */
390 if ((cpu_khz
/1000) > 1200)
393 fsb
= eblcr_fsb_table_v2
[longhaul
.bits
.MaxMHzFSB
];
398 dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
399 minmult
/10, minmult
%10, maxmult
/10, maxmult
%10);
402 printk (KERN_INFO PFX
"Invalid (reserved) FSB!\n");
406 highest_speed
= calc_speed(maxmult
);
407 lowest_speed
= calc_speed(minmult
);
408 dprintk ("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb
,
409 print_speed(lowest_speed
/1000),
410 print_speed(highest_speed
/1000));
412 if (lowest_speed
== highest_speed
) {
413 printk (KERN_INFO PFX
"highestspeed == lowest, aborting.\n");
416 if (lowest_speed
> highest_speed
) {
417 printk (KERN_INFO PFX
"nonsense! lowest (%d > %d) !\n",
418 lowest_speed
, highest_speed
);
422 longhaul_table
= kmalloc((numscales
+ 1) * sizeof(struct cpufreq_frequency_table
), GFP_KERNEL
);
426 for (j
=0; j
< numscales
; j
++) {
428 ratio
= clock_ratio
[j
];
431 if (ratio
> maxmult
|| ratio
< minmult
)
433 longhaul_table
[k
].frequency
= calc_speed(ratio
);
434 longhaul_table
[k
].index
= j
;
438 longhaul_table
[k
].frequency
= CPUFREQ_TABLE_END
;
440 kfree (longhaul_table
);
448 static void __init
longhaul_setup_voltagescaling(void)
450 union msr_longhaul longhaul
;
452 rdmsrl (MSR_VIA_LONGHAUL
, longhaul
.val
);
454 if (!(longhaul
.bits
.RevisionID
& 1))
457 minvid
= longhaul
.bits
.MinimumVID
;
458 maxvid
= longhaul
.bits
.MaximumVID
;
459 vrmrev
= longhaul
.bits
.VRMRev
;
461 if (minvid
== 0 || maxvid
== 0) {
462 printk (KERN_INFO PFX
"Bogus values Min:%d.%03d Max:%d.%03d. "
463 "Voltage scaling disabled.\n",
464 minvid
/1000, minvid
%1000, maxvid
/1000, maxvid
%1000);
468 if (minvid
== maxvid
) {
469 printk (KERN_INFO PFX
"Claims to support voltage scaling but min & max are "
470 "both %d.%03d. Voltage scaling disabled\n",
471 maxvid
/1000, maxvid
%1000);
476 dprintk ("VRM 8.5\n");
477 memcpy (voltage_table
, vrm85scales
, sizeof(voltage_table
));
478 numvscales
= (voltage_table
[maxvid
]-voltage_table
[minvid
])/25;
480 dprintk ("Mobile VRM\n");
481 memcpy (voltage_table
, mobilevrmscales
, sizeof(voltage_table
));
482 numvscales
= (voltage_table
[maxvid
]-voltage_table
[minvid
])/5;
485 /* Current voltage isn't readable at first, so we need to
486 set it to a known value. The spec says to use maxvid */
487 longhaul
.bits
.RevisionKey
= longhaul
.bits
.RevisionID
; /* FIXME: This is bad. */
488 longhaul
.bits
.EnableSoftVID
= 1;
489 longhaul
.bits
.SoftVID
= maxvid
;
490 wrmsrl (MSR_VIA_LONGHAUL
, longhaul
.val
);
492 minvid
= voltage_table
[minvid
];
493 maxvid
= voltage_table
[maxvid
];
495 dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n",
496 maxvid
/1000, maxvid
%1000, minvid
/1000, minvid
%1000, numvscales
);
498 can_scale_voltage
= 1;
502 static int longhaul_verify(struct cpufreq_policy
*policy
)
504 return cpufreq_frequency_table_verify(policy
, longhaul_table
);
508 static int longhaul_target(struct cpufreq_policy
*policy
,
509 unsigned int target_freq
, unsigned int relation
)
511 unsigned int table_index
= 0;
512 unsigned int new_clock_ratio
= 0;
514 if (cpufreq_frequency_table_target(policy
, longhaul_table
, target_freq
, relation
, &table_index
))
517 new_clock_ratio
= longhaul_table
[table_index
].index
& 0xFF;
519 longhaul_setstate(new_clock_ratio
);
525 static unsigned int longhaul_get(unsigned int cpu
)
529 return calc_speed(longhaul_get_cpu_mult());
533 static int __init
longhaul_cpu_init(struct cpufreq_policy
*policy
)
535 struct cpuinfo_x86
*c
= cpu_data
;
539 switch (c
->x86_model
) {
541 cpu_model
= CPU_SAMUEL
;
542 cpuname
= "C3 'Samuel' [C5A]";
543 longhaul_version
= TYPE_LONGHAUL_V1
;
544 memcpy (clock_ratio
, samuel1_clock_ratio
, sizeof(samuel1_clock_ratio
));
545 memcpy (eblcr_table
, samuel1_eblcr
, sizeof(samuel1_eblcr
));
549 longhaul_version
= TYPE_LONGHAUL_V1
;
550 switch (c
->x86_mask
) {
552 cpu_model
= CPU_SAMUEL2
;
553 cpuname
= "C3 'Samuel 2' [C5B]";
554 /* Note, this is not a typo, early Samuel2's had Samuel1 ratios. */
555 memcpy (clock_ratio
, samuel1_clock_ratio
, sizeof(samuel1_clock_ratio
));
556 memcpy (eblcr_table
, samuel2_eblcr
, sizeof(samuel2_eblcr
));
559 if (c
->x86_mask
< 8) {
560 cpu_model
= CPU_SAMUEL2
;
561 cpuname
= "C3 'Samuel 2' [C5B]";
563 cpu_model
= CPU_EZRA
;
564 cpuname
= "C3 'Ezra' [C5C]";
566 memcpy (clock_ratio
, ezra_clock_ratio
, sizeof(ezra_clock_ratio
));
567 memcpy (eblcr_table
, ezra_eblcr
, sizeof(ezra_eblcr
));
573 cpu_model
= CPU_EZRA_T
;
574 cpuname
= "C3 'Ezra-T' [C5M]";
575 longhaul_version
= TYPE_POWERSAVER
;
577 memcpy (clock_ratio
, ezrat_clock_ratio
, sizeof(ezrat_clock_ratio
));
578 memcpy (eblcr_table
, ezrat_eblcr
, sizeof(ezrat_eblcr
));
582 cpu_model
= CPU_NEHEMIAH
;
583 longhaul_version
= TYPE_POWERSAVER
;
585 switch (c
->x86_mask
) {
587 cpuname
= "C3 'Nehemiah A' [C5N]";
588 memcpy (clock_ratio
, nehemiah_a_clock_ratio
, sizeof(nehemiah_a_clock_ratio
));
589 memcpy (eblcr_table
, nehemiah_a_eblcr
, sizeof(nehemiah_a_eblcr
));
592 cpuname
= "C3 'Nehemiah B' [C5N]";
593 memcpy (clock_ratio
, nehemiah_b_clock_ratio
, sizeof(nehemiah_b_clock_ratio
));
594 memcpy (eblcr_table
, nehemiah_b_eblcr
, sizeof(nehemiah_b_eblcr
));
597 cpuname
= "C3 'Nehemiah C' [C5N]";
598 memcpy (clock_ratio
, nehemiah_c_clock_ratio
, sizeof(nehemiah_c_clock_ratio
));
599 memcpy (eblcr_table
, nehemiah_c_eblcr
, sizeof(nehemiah_c_eblcr
));
609 printk (KERN_INFO PFX
"VIA %s CPU detected. ", cpuname
);
610 switch (longhaul_version
) {
611 case TYPE_LONGHAUL_V1
:
612 case TYPE_LONGHAUL_V2
:
613 printk ("Longhaul v%d supported.\n", longhaul_version
);
615 case TYPE_POWERSAVER
:
616 printk ("Powersaver supported.\n");
620 ret
= longhaul_get_ranges();
624 if ((longhaul_version
==TYPE_LONGHAUL_V2
|| longhaul_version
==TYPE_POWERSAVER
) &&
625 (dont_scale_voltage
==0))
626 longhaul_setup_voltagescaling();
628 policy
->governor
= CPUFREQ_DEFAULT_GOVERNOR
;
629 policy
->cpuinfo
.transition_latency
= 200000; /* nsec */
630 policy
->cur
= calc_speed(longhaul_get_cpu_mult());
632 ret
= cpufreq_frequency_table_cpuinfo(policy
, longhaul_table
);
636 cpufreq_frequency_table_get_attr(longhaul_table
, policy
->cpu
);
641 static int __devexit
longhaul_cpu_exit(struct cpufreq_policy
*policy
)
643 cpufreq_frequency_table_put_attr(policy
->cpu
);
647 static struct freq_attr
* longhaul_attr
[] = {
648 &cpufreq_freq_attr_scaling_available_freqs
,
652 static struct cpufreq_driver longhaul_driver
= {
653 .verify
= longhaul_verify
,
654 .target
= longhaul_target
,
656 .init
= longhaul_cpu_init
,
657 .exit
= __devexit_p(longhaul_cpu_exit
),
659 .owner
= THIS_MODULE
,
660 .attr
= longhaul_attr
,
664 static int __init
longhaul_init(void)
666 struct cpuinfo_x86
*c
= cpu_data
;
668 if (c
->x86_vendor
!= X86_VENDOR_CENTAUR
|| c
->x86
!= 6)
671 switch (c
->x86_model
) {
673 return cpufreq_register_driver(&longhaul_driver
);
675 printk (KERN_INFO PFX
"Unknown VIA CPU. Contact davej@codemonkey.org.uk\n");
682 static void __exit
longhaul_exit(void)
686 for (i
=0; i
< numscales
; i
++) {
687 if (clock_ratio
[i
] == maxmult
) {
688 longhaul_setstate(i
);
693 cpufreq_unregister_driver(&longhaul_driver
);
694 kfree(longhaul_table
);
697 module_param (dont_scale_voltage
, int, 0644);
698 MODULE_PARM_DESC(dont_scale_voltage
, "Don't scale voltage of processor");
700 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
701 MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
702 MODULE_LICENSE ("GPL");
704 module_init(longhaul_init
);
705 module_exit(longhaul_exit
);