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