Blame view
drivers/cpufreq/longhaul.c
25.3 KB
1da177e4c
|
1 |
/* |
d5e80b4b1
|
2 |
* (C) 2001-2004 Dave Jones. |
1da177e4c
|
3 4 5 6 7 8 9 10 |
* (C) 2002 Padraig Brady. <padraig@antefacto.com> * * Licensed under the terms of the GNU GPL License version 2. * Based upon datasheets & sample CPUs kindly provided by VIA. * * VIA have currently 3 different versions of Longhaul. * Version 1 (Longhaul) uses the BCR2 MSR at 0x1147. * It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0. |
2b8c0e130
|
11 12 13 |
* Version 2 of longhaul is backward compatible with v1, but adds * LONGHAUL MSR for purpose of both frequency and voltage scaling. * Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C). |
1da177e4c
|
14 |
* Version 3 of longhaul got renamed to Powersaver and redesigned |
2b8c0e130
|
15 |
* to use only the POWERSAVER MSR at 0x110a. |
1da177e4c
|
16 17 18 19 20 21 22 |
* It is present in Ezra-T (C5M), Nehemiah (C5X) and above. * It's pretty much the same feature wise to longhaul v2, though * there is provision for scaling FSB too, but this doesn't work * too well in practice so we don't even try to use this. * * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ |
1c5864e26
|
23 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4c
|
24 25 26 27 28 |
#include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/cpufreq.h> |
179da8e6e
|
29 |
#include <linux/pci.h> |
1da177e4c
|
30 31 |
#include <linux/slab.h> #include <linux/string.h> |
73e107d4a
|
32 |
#include <linux/delay.h> |
ac617bd0f
|
33 34 35 |
#include <linux/timex.h> #include <linux/io.h> #include <linux/acpi.h> |
1da177e4c
|
36 37 |
#include <asm/msr.h> |
fa8031aef
|
38 |
#include <asm/cpu_device_id.h> |
dadb49d87
|
39 |
#include <acpi/processor.h> |
1da177e4c
|
40 41 |
#include "longhaul.h" |
1da177e4c
|
42 43 44 45 46 47 48 49 50 |
#define TYPE_LONGHAUL_V1 1 #define TYPE_LONGHAUL_V2 2 #define TYPE_POWERSAVER 3 #define CPU_SAMUEL 1 #define CPU_SAMUEL2 2 #define CPU_EZRA 3 #define CPU_EZRA_T 4 #define CPU_NEHEMIAH 5 |
980342a7e
|
51 |
#define CPU_NEHEMIAH_C 6 |
1da177e4c
|
52 |
|
264166e60
|
53 54 55 |
/* Flags */ #define USE_ACPI_C3 (1 << 1) #define USE_NORTHBRIDGE (1 << 2) |
1da177e4c
|
56 |
static int cpu_model; |
ac617bd0f
|
57 |
static unsigned int numscales = 16; |
1da177e4c
|
58 |
static unsigned int fsb; |
db44aaf3a
|
59 |
|
bd5ab26a7
|
60 61 |
static const struct mV_pos *vrm_mV_table; static const unsigned char *mV_vrm_table; |
db44aaf3a
|
62 63 |
static unsigned int highest_speed, lowest_speed; /* kHz */ |
1da177e4c
|
64 65 |
static unsigned int minmult, maxmult; static int can_scale_voltage; |
ac617bd0f
|
66 67 |
static struct acpi_processor *pr; static struct acpi_processor_cx *cx; |
275bc6b7f
|
68 |
static u32 acpi_regs_addr; |
264166e60
|
69 |
static u8 longhaul_flags; |
73e107d4a
|
70 |
static unsigned int longhaul_index; |
1da177e4c
|
71 72 |
/* Module parameters */ |
db44aaf3a
|
73 |
static int scale_voltage; |
905497c4b
|
74 |
static int disable_acpi_c3; |
52a2638bf
|
75 |
static int revid_errata; |
b5811bc46
|
76 |
static int enable; |
1da177e4c
|
77 |
|
1da177e4c
|
78 |
/* Clock ratios multiplied by 10 */ |
ac617bd0f
|
79 80 |
static int mults[32]; static int eblcr[32]; |
1da177e4c
|
81 82 |
static int longhaul_version; static struct cpufreq_frequency_table *longhaul_table; |
1da177e4c
|
83 84 85 86 |
static char speedbuffer[8]; static char *print_speed(int speed) { |
e2aa8732a
|
87 |
if (speed < 1000) { |
ac617bd0f
|
88 |
snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed); |
e2aa8732a
|
89 90 91 92 93 94 95 96 97 |
return speedbuffer; } if (speed%1000 == 0) snprintf(speedbuffer, sizeof(speedbuffer), "%dGHz", speed/1000); else snprintf(speedbuffer, sizeof(speedbuffer), "%d.%dGHz", speed/1000, (speed%1000)/100); |
1da177e4c
|
98 99 100 |
return speedbuffer; } |
1da177e4c
|
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
static unsigned int calc_speed(int mult) { int khz; khz = (mult/10)*fsb; if (mult%10) khz += fsb/2; khz *= 1000; return khz; } static int longhaul_get_cpu_mult(void) { |
ac617bd0f
|
116 |
unsigned long invalue = 0, lo, hi; |
1da177e4c
|
117 |
|
ac617bd0f
|
118 119 120 121 |
rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi); invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22; if (longhaul_version == TYPE_LONGHAUL_V2 || longhaul_version == TYPE_POWERSAVER) { |
1da177e4c
|
122 |
if (lo & (1<<27)) |
ac617bd0f
|
123 |
invalue += 16; |
1da177e4c
|
124 |
} |
ac617bd0f
|
125 |
return eblcr[invalue]; |
1da177e4c
|
126 |
} |
dadb49d87
|
127 |
/* For processor with BCR2 MSR */ |
1da177e4c
|
128 |
|
ac617bd0f
|
129 |
static void do_longhaul1(unsigned int mults_index) |
1da177e4c
|
130 |
{ |
dadb49d87
|
131 |
union msr_bcr2 bcr2; |
1da177e4c
|
132 |
|
dadb49d87
|
133 134 135 |
rdmsrl(MSR_VIA_BCR2, bcr2.val); /* Enable software clock multiplier */ bcr2.bits.ESOFTBF = 1; |
ac617bd0f
|
136 |
bcr2.bits.CLOCKMUL = mults_index & 0xff; |
1da177e4c
|
137 |
|
dadb49d87
|
138 139 |
/* Sync to timer tick */ safe_halt(); |
dadb49d87
|
140 141 |
/* Change frequency on next halt or sleep */ wrmsrl(MSR_VIA_BCR2, bcr2.val); |
179da8e6e
|
142 143 144 |
/* Invoke transition */ ACPI_FLUSH_CPU_CACHE(); halt(); |
dadb49d87
|
145 146 147 148 149 150 151 |
/* Disable software clock multiplier */ local_irq_disable(); rdmsrl(MSR_VIA_BCR2, bcr2.val); bcr2.bits.ESOFTBF = 0; wrmsrl(MSR_VIA_BCR2, bcr2.val); } |
3be6a48f3
|
152 |
|
dadb49d87
|
153 |
/* For processor with Longhaul MSR */ |
3be6a48f3
|
154 |
|
ac617bd0f
|
155 |
static void do_powersaver(int cx_address, unsigned int mults_index, |
73e107d4a
|
156 |
unsigned int dir) |
dadb49d87
|
157 158 159 |
{ union msr_longhaul longhaul; u32 t; |
3be6a48f3
|
160 |
|
dadb49d87
|
161 |
rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
348f31ed2
|
162 |
/* Setup new frequency */ |
52a2638bf
|
163 164 165 166 |
if (!revid_errata) longhaul.bits.RevisionKey = longhaul.bits.RevisionID; else longhaul.bits.RevisionKey = 0; |
ac617bd0f
|
167 168 |
longhaul.bits.SoftBusRatio = mults_index & 0xf; longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4; |
348f31ed2
|
169 170 |
/* Setup new voltage */ if (can_scale_voltage) |
ac617bd0f
|
171 |
longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f; |
348f31ed2
|
172 173 174 |
/* Sync to timer tick */ safe_halt(); /* Raise voltage if necessary */ |
73e107d4a
|
175 |
if (can_scale_voltage && dir) { |
db44aaf3a
|
176 |
longhaul.bits.EnableSoftVID = 1; |
348f31ed2
|
177 178 179 180 181 182 183 184 185 186 187 |
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Change voltage */ if (!cx_address) { ACPI_FLUSH_CPU_CACHE(); halt(); } else { ACPI_FLUSH_CPU_CACHE(); /* Invoke C3 */ inb(cx_address); /* Dummy op - must do something useless after P_LVL3 * read */ |
bd0561c9d
|
188 |
t = inl(acpi_gbl_FADT.xpm_timer_block.address); |
348f31ed2
|
189 190 191 |
} longhaul.bits.EnableSoftVID = 0; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
db44aaf3a
|
192 |
} |
dadb49d87
|
193 |
/* Change frequency on next halt or sleep */ |
348f31ed2
|
194 |
longhaul.bits.EnableSoftBusRatio = 1; |
dadb49d87
|
195 |
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
264166e60
|
196 |
if (!cx_address) { |
7f1be8924
|
197 |
ACPI_FLUSH_CPU_CACHE(); |
7f1be8924
|
198 199 200 201 202 203 |
halt(); } else { ACPI_FLUSH_CPU_CACHE(); /* Invoke C3 */ inb(cx_address); /* Dummy op - must do something useless after P_LVL3 read */ |
cee324b14
|
204 |
t = inl(acpi_gbl_FADT.xpm_timer_block.address); |
7f1be8924
|
205 |
} |
dadb49d87
|
206 |
/* Disable bus ratio bit */ |
dadb49d87
|
207 |
longhaul.bits.EnableSoftBusRatio = 0; |
dadb49d87
|
208 |
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
348f31ed2
|
209 210 |
/* Reduce voltage if necessary */ |
73e107d4a
|
211 |
if (can_scale_voltage && !dir) { |
348f31ed2
|
212 213 214 215 216 217 218 219 220 221 222 223 |
longhaul.bits.EnableSoftVID = 1; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Change voltage */ if (!cx_address) { ACPI_FLUSH_CPU_CACHE(); halt(); } else { ACPI_FLUSH_CPU_CACHE(); /* Invoke C3 */ inb(cx_address); /* Dummy op - must do something useless after P_LVL3 * read */ |
bd0561c9d
|
224 |
t = inl(acpi_gbl_FADT.xpm_timer_block.address); |
348f31ed2
|
225 226 227 |
} longhaul.bits.EnableSoftVID = 0; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
348f31ed2
|
228 |
} |
1da177e4c
|
229 230 231 232 |
} /** * longhaul_set_cpu_frequency() |
ac617bd0f
|
233 |
* @mults_index : bitpattern of the new multiplier. |
1da177e4c
|
234 235 236 |
* * Sets a new clock ratio. */ |
7aa0557fa
|
237 |
static int longhaul_setstate(struct cpufreq_policy *policy, |
b43a7ffbf
|
238 |
unsigned int table_index) |
1da177e4c
|
239 |
{ |
ac617bd0f
|
240 |
unsigned int mults_index; |
1da177e4c
|
241 242 |
int speed, mult; struct cpufreq_freqs freqs; |
dadb49d87
|
243 244 |
unsigned long flags; unsigned int pic1_mask, pic2_mask; |
689eba77c
|
245 |
u16 bm_status = 0; |
275bc6b7f
|
246 |
u32 bm_timeout = 1000; |
73e107d4a
|
247 |
unsigned int dir = 0; |
1da177e4c
|
248 |
|
507015880
|
249 |
mults_index = longhaul_table[table_index].driver_data; |
73e107d4a
|
250 |
/* Safety precautions */ |
ac617bd0f
|
251 |
mult = mults[mults_index & 0x1f]; |
1da177e4c
|
252 |
if (mult == -1) |
7aa0557fa
|
253 |
return -EINVAL; |
1da177e4c
|
254 255 |
speed = calc_speed(mult); if ((speed > highest_speed) || (speed < lowest_speed)) |
7aa0557fa
|
256 |
return -EINVAL; |
73e107d4a
|
257 258 259 |
/* Voltage transition before frequency transition? */ if (can_scale_voltage && longhaul_index < table_index) dir = 1; |
1da177e4c
|
260 261 262 |
freqs.old = calc_speed(longhaul_get_cpu_mult()); freqs.new = speed; |
1da177e4c
|
263 |
|
2d06d8c49
|
264 265 |
pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s) ", |
1da177e4c
|
266 |
fsb, mult/10, mult%10, print_speed(speed/1000)); |
52a2638bf
|
267 |
retry_loop: |
dadb49d87
|
268 269 270 271 272 |
preempt_disable(); local_irq_save(flags); pic2_mask = inb(0xA1); pic1_mask = inb(0x21); /* works on C3. save mask. */ |
ac617bd0f
|
273 274 |
outb(0xFF, 0xA1); /* Overkill */ outb(0xFE, 0x21); /* TMR0 only */ |
dadb49d87
|
275 |
|
489dc5cb1
|
276 |
/* Wait while PCI bus is busy. */ |
689eba77c
|
277 278 279 |
if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE || ((pr != NULL) && pr->flags.bm_control))) { bm_status = inw(acpi_regs_addr); |
275bc6b7f
|
280 |
bm_status &= 1 << 4; |
489dc5cb1
|
281 |
while (bm_status && bm_timeout) { |
689eba77c
|
282 |
outw(1 << 4, acpi_regs_addr); |
489dc5cb1
|
283 |
bm_timeout--; |
689eba77c
|
284 |
bm_status = inw(acpi_regs_addr); |
275bc6b7f
|
285 |
bm_status &= 1 << 4; |
489dc5cb1
|
286 287 |
} } |
264166e60
|
288 289 290 291 |
if (longhaul_flags & USE_NORTHBRIDGE) { /* Disable AGP and PCI arbiters */ outb(3, 0x22); } else if ((pr != NULL) && pr->flags.bm_control) { |
73e107d4a
|
292 |
/* Disable bus master arbitration */ |
fb318cbff
|
293 |
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); |
dadb49d87
|
294 |
} |
1da177e4c
|
295 296 297 298 299 |
switch (longhaul_version) { /* * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B]) * Software controlled multipliers only. |
1da177e4c
|
300 301 |
*/ case TYPE_LONGHAUL_V1: |
ac617bd0f
|
302 |
do_longhaul1(mults_index); |
1da177e4c
|
303 304 305 |
break; /* |
2b8c0e130
|
306 307 |
* Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C] * |
1da177e4c
|
308 |
* Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N]) |
1da177e4c
|
309 310 311 |
* Nehemiah can do FSB scaling too, but this has never been proven * to work in practice. */ |
2b8c0e130
|
312 |
case TYPE_LONGHAUL_V2: |
1da177e4c
|
313 |
case TYPE_POWERSAVER: |
264166e60
|
314 315 |
if (longhaul_flags & USE_ACPI_C3) { /* Don't allow wakeup */ |
fb318cbff
|
316 |
acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0); |
ac617bd0f
|
317 |
do_powersaver(cx->address, mults_index, dir); |
264166e60
|
318 |
} else { |
ac617bd0f
|
319 |
do_powersaver(0, mults_index, dir); |
264166e60
|
320 |
} |
1da177e4c
|
321 322 |
break; } |
264166e60
|
323 324 325 326 |
if (longhaul_flags & USE_NORTHBRIDGE) { /* Enable arbiters */ outb(0, 0x22); } else if ((pr != NULL) && pr->flags.bm_control) { |
179da8e6e
|
327 |
/* Enable bus master arbitration */ |
fb318cbff
|
328 |
acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); |
dadb49d87
|
329 |
} |
ac617bd0f
|
330 331 |
outb(pic2_mask, 0xA1); /* restore mask */ outb(pic1_mask, 0x21); |
dadb49d87
|
332 333 334 |
local_irq_restore(flags); preempt_enable(); |
2b8c0e130
|
335 |
freqs.new = calc_speed(longhaul_get_cpu_mult()); |
52a2638bf
|
336 337 |
/* Check if requested frequency is set. */ if (unlikely(freqs.new != speed)) { |
1c5864e26
|
338 339 |
pr_info("Failed to set requested frequency! "); |
52a2638bf
|
340 341 342 343 344 |
/* Revision ID = 1 but processor is expecting revision key * equal to 0. Jumpers at the bottom of processor will change * multiplier and FSB, but will not change bits in Longhaul * MSR nor enable voltage scaling. */ if (!revid_errata) { |
1c5864e26
|
345 346 |
pr_info("Enabling \"Ignore Revision ID\" option "); |
52a2638bf
|
347 348 349 350 351 352 353 354 355 |
revid_errata = 1; msleep(200); goto retry_loop; } /* Why ACPI C3 sometimes doesn't work is a mystery for me. * But it does happen. Processor is entering ACPI C3 state, * but it doesn't change frequency. I tried poking various * bits in northbridge registers, but without success. */ if (longhaul_flags & USE_ACPI_C3) { |
1c5864e26
|
356 357 |
pr_info("Disabling ACPI C3 support "); |
52a2638bf
|
358 359 |
longhaul_flags &= ~USE_ACPI_C3; if (revid_errata) { |
1c5864e26
|
360 361 |
pr_info("Disabling \"Ignore Revision ID\" option "); |
52a2638bf
|
362 363 364 365 366 367 368 369 370 371 |
revid_errata = 0; } msleep(200); goto retry_loop; } /* This shouldn't happen. Longhaul ver. 2 was reported not * working on processors without voltage scaling, but with * RevID = 1. RevID errata will make things right. Just * to be 100% sure. */ if (longhaul_version == TYPE_LONGHAUL_V2) { |
1c5864e26
|
372 373 |
pr_info("Switching to Longhaul ver. 1 "); |
52a2638bf
|
374 375 376 377 378 |
longhaul_version = TYPE_LONGHAUL_V1; msleep(200); goto retry_loop; } } |
489dc5cb1
|
379 |
|
7aa0557fa
|
380 |
if (!bm_timeout) { |
1c5864e26
|
381 382 |
pr_info("Warning: Timeout while waiting for idle PCI bus "); |
7aa0557fa
|
383 384 385 386 |
return -EBUSY; } return 0; |
1da177e4c
|
387 388 389 390 391 392 393 394 395 396 397 398 |
} /* * Centaur decided to make life a little more tricky. * Only longhaul v1 is allowed to read EBLCR BSEL[0:1]. * Samuel2 and above have to try and guess what the FSB is. * We do this by assuming we booted at maximum multiplier, and interpolate * between that value multiplied by possible FSBs and cpu_mhz which * was calculated at boot time. Really ugly, but no other way to do this. */ #define ROUNDING 0xf |
24ebead82
|
399 |
static int guess_fsb(int mult) |
1da177e4c
|
400 |
{ |
46ef955f5
|
401 |
int speed = cpu_khz / 1000; |
1da177e4c
|
402 |
int i; |
46ef955f5
|
403 404 405 406 407 408 409 410 411 |
int speeds[] = { 666, 1000, 1333, 2000 }; int f_max, f_min; for (i = 0; i < 4; i++) { f_max = ((speeds[i] * mult) + 50) / 100; f_max += (ROUNDING / 2); f_min = f_max - ROUNDING; if ((speed <= f_max) && (speed >= f_min)) return speeds[i] / 10; |
1da177e4c
|
412 413 414 |
} return 0; } |
2760984f6
|
415 |
static int longhaul_get_ranges(void) |
1da177e4c
|
416 |
{ |
73e107d4a
|
417 418 |
unsigned int i, j, k = 0; unsigned int ratio; |
980342a7e
|
419 |
int mult; |
1da177e4c
|
420 |
|
980342a7e
|
421 422 423 |
/* Get current frequency */ mult = longhaul_get_cpu_mult(); if (mult == -1) { |
1c5864e26
|
424 425 |
pr_info("Invalid (reserved) multiplier! "); |
980342a7e
|
426 427 428 429 |
return -EINVAL; } fsb = guess_fsb(mult); if (fsb == 0) { |
1c5864e26
|
430 431 |
pr_info("Invalid (reserved) FSB! "); |
980342a7e
|
432 433 434 |
return -EINVAL; } /* Get max multiplier - as we always did. |
0d2eb44f6
|
435 |
* Longhaul MSR is useful only when voltage scaling is enabled. |
980342a7e
|
436 437 438 |
* C3 is booting at max anyway. */ maxmult = mult; /* Get min multiplier */ |
9addf3b63
|
439 440 441 |
switch (cpu_model) { case CPU_NEHEMIAH: minmult = 50; |
1da177e4c
|
442 |
break; |
9addf3b63
|
443 444 445 446 447 |
case CPU_NEHEMIAH_C: minmult = 40; break; default: minmult = 30; |
980342a7e
|
448 |
break; |
1da177e4c
|
449 |
} |
2d06d8c49
|
450 451 |
pr_debug("MinMult:%d.%dx MaxMult:%d.%dx ", |
1da177e4c
|
452 |
minmult/10, minmult%10, maxmult/10, maxmult%10); |
1da177e4c
|
453 454 |
highest_speed = calc_speed(maxmult); lowest_speed = calc_speed(minmult); |
2d06d8c49
|
455 456 |
pr_debug("FSB:%dMHz Lowest speed: %s Highest speed:%s ", fsb, |
cee324b14
|
457 |
print_speed(lowest_speed/1000), |
1da177e4c
|
458 459 460 |
print_speed(highest_speed/1000)); if (lowest_speed == highest_speed) { |
1c5864e26
|
461 462 |
pr_info("highestspeed == lowest, aborting "); |
1da177e4c
|
463 464 465 |
return -EINVAL; } if (lowest_speed > highest_speed) { |
1c5864e26
|
466 467 |
pr_info("nonsense! lowest (%d > %d) ! ", |
1da177e4c
|
468 469 470 |
lowest_speed, highest_speed); return -EINVAL; } |
71508a1f4
|
471 |
longhaul_table = kzalloc((numscales + 1) * sizeof(*longhaul_table), |
ac617bd0f
|
472 473 |
GFP_KERNEL); if (!longhaul_table) |
1da177e4c
|
474 |
return -ENOMEM; |
73e107d4a
|
475 |
for (j = 0; j < numscales; j++) { |
ac617bd0f
|
476 |
ratio = mults[j]; |
1da177e4c
|
477 478 479 480 481 |
if (ratio == -1) continue; if (ratio > maxmult || ratio < minmult) continue; longhaul_table[k].frequency = calc_speed(ratio); |
507015880
|
482 |
longhaul_table[k].driver_data = j; |
1da177e4c
|
483 484 |
k++; } |
73e107d4a
|
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
if (k <= 1) { kfree(longhaul_table); return -ENODEV; } /* Sort */ for (j = 0; j < k - 1; j++) { unsigned int min_f, min_i; min_f = longhaul_table[j].frequency; min_i = j; for (i = j + 1; i < k; i++) { if (longhaul_table[i].frequency < min_f) { min_f = longhaul_table[i].frequency; min_i = i; } } if (min_i != j) { |
91420220d
|
501 502 |
swap(longhaul_table[j].frequency, longhaul_table[min_i].frequency); |
507015880
|
503 504 |
swap(longhaul_table[j].driver_data, longhaul_table[min_i].driver_data); |
73e107d4a
|
505 506 |
} } |
1da177e4c
|
507 508 |
longhaul_table[k].frequency = CPUFREQ_TABLE_END; |
1da177e4c
|
509 |
|
73e107d4a
|
510 511 |
/* Find index we are running on */ for (j = 0; j < k; j++) { |
507015880
|
512 |
if (mults[longhaul_table[j].driver_data & 0x1f] == mult) { |
73e107d4a
|
513 514 515 516 |
longhaul_index = j; break; } } |
1da177e4c
|
517 518 |
return 0; } |
2760984f6
|
519 |
static void longhaul_setup_voltagescaling(void) |
1da177e4c
|
520 |
{ |
041526f91
|
521 |
struct cpufreq_frequency_table *freq_pos; |
1da177e4c
|
522 |
union msr_longhaul longhaul; |
73e107d4a
|
523 |
struct mV_pos minvid, maxvid, vid; |
db44aaf3a
|
524 |
unsigned int j, speed, pos, kHz_step, numvscales; |
348f31ed2
|
525 |
int min_vid_speed; |
1da177e4c
|
526 |
|
db44aaf3a
|
527 528 |
rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); if (!(longhaul.bits.RevisionID & 1)) { |
1c5864e26
|
529 530 |
pr_info("Voltage scaling not supported by CPU "); |
1da177e4c
|
531 |
return; |
db44aaf3a
|
532 533 534 |
} if (!longhaul.bits.VRMRev) { |
1c5864e26
|
535 536 |
pr_info("VRM 8.5 "); |
db44aaf3a
|
537 538 539 |
vrm_mV_table = &vrm85_mV[0]; mV_vrm_table = &mV_vrm85[0]; } else { |
1c5864e26
|
540 541 |
pr_info("Mobile VRM "); |
2b8c0e130
|
542 543 |
if (cpu_model < CPU_NEHEMIAH) return; |
db44aaf3a
|
544 545 546 |
vrm_mV_table = &mobilevrm_mV[0]; mV_vrm_table = &mV_mobilevrm[0]; } |
1da177e4c
|
547 |
|
db44aaf3a
|
548 549 |
minvid = vrm_mV_table[longhaul.bits.MinimumVID]; maxvid = vrm_mV_table[longhaul.bits.MaximumVID]; |
1da177e4c
|
550 |
|
db44aaf3a
|
551 |
if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) { |
1c5864e26
|
552 553 |
pr_info("Bogus values Min:%d.%03d Max:%d.%03d - Voltage scaling disabled ", |
b49c22a6c
|
554 555 |
minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000); |
1da177e4c
|
556 557 |
return; } |
db44aaf3a
|
558 |
if (minvid.mV == maxvid.mV) { |
1c5864e26
|
559 560 |
pr_info("Claims to support voltage scaling but min & max are both %d.%03d - Voltage scaling disabled ", |
b49c22a6c
|
561 |
maxvid.mV/1000, maxvid.mV%1000); |
1da177e4c
|
562 563 |
return; } |
ac617bd0f
|
564 |
/* How many voltage steps*/ |
348f31ed2
|
565 |
numvscales = maxvid.pos - minvid.pos + 1; |
1c5864e26
|
566 567 |
pr_info("Max VID=%d.%03d Min VID=%d.%03d, %d possible voltage scales ", |
db44aaf3a
|
568 569 570 |
maxvid.mV/1000, maxvid.mV%1000, minvid.mV/1000, minvid.mV%1000, numvscales); |
348f31ed2
|
571 572 573 574 575 |
/* Calculate max frequency at min voltage */ j = longhaul.bits.MinMHzBR; if (longhaul.bits.MinMHzBR4) j += 16; |
ac617bd0f
|
576 |
min_vid_speed = eblcr[j]; |
348f31ed2
|
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
if (min_vid_speed == -1) return; switch (longhaul.bits.MinMHzFSB) { case 0: min_vid_speed *= 13333; break; case 1: min_vid_speed *= 10000; break; case 3: min_vid_speed *= 6666; break; default: return; break; } if (min_vid_speed >= highest_speed) return; /* Calculate kHz for one voltage step */ kHz_step = (highest_speed - min_vid_speed) / numvscales; |
041526f91
|
597 598 |
cpufreq_for_each_entry(freq_pos, longhaul_table) { speed = freq_pos->frequency; |
348f31ed2
|
599 600 601 602 |
if (speed > min_vid_speed) pos = (speed - min_vid_speed) / kHz_step + minvid.pos; else pos = minvid.pos; |
041526f91
|
603 |
freq_pos->driver_data |= mV_vrm_table[pos] << 8; |
73e107d4a
|
604 |
vid = vrm_mV_table[mV_vrm_table[pos]]; |
1c5864e26
|
605 606 |
pr_info("f: %d kHz, index: %d, vid: %d mV ", |
041526f91
|
607 |
speed, (int)(freq_pos - longhaul_table), vid.mV); |
1da177e4c
|
608 |
} |
1da177e4c
|
609 |
can_scale_voltage = 1; |
1c5864e26
|
610 611 |
pr_info("Voltage scaling enabled "); |
1da177e4c
|
612 |
} |
1da177e4c
|
613 |
static int longhaul_target(struct cpufreq_policy *policy, |
9c0ebcf78
|
614 |
unsigned int table_index) |
1da177e4c
|
615 |
{ |
73e107d4a
|
616 617 618 |
unsigned int i; unsigned int dir = 0; u8 vid, current_vid; |
7aa0557fa
|
619 |
int retval = 0; |
1da177e4c
|
620 |
|
73e107d4a
|
621 |
if (!can_scale_voltage) |
7aa0557fa
|
622 |
retval = longhaul_setstate(policy, table_index); |
73e107d4a
|
623 624 625 626 627 628 629 |
else { /* On test system voltage transitions exceeding single * step up or down were turning motherboard off. Both * "ondemand" and "userspace" are unsafe. C7 is doing * this in hardware, C3 is old and we need to do this * in software. */ i = longhaul_index; |
507015880
|
630 |
current_vid = (longhaul_table[longhaul_index].driver_data >> 8); |
ac617bd0f
|
631 |
current_vid &= 0x1f; |
73e107d4a
|
632 633 634 |
if (table_index > longhaul_index) dir = 1; while (i != table_index) { |
507015880
|
635 |
vid = (longhaul_table[i].driver_data >> 8) & 0x1f; |
73e107d4a
|
636 |
if (vid != current_vid) { |
7aa0557fa
|
637 |
retval = longhaul_setstate(policy, i); |
73e107d4a
|
638 639 640 641 642 643 644 645 |
current_vid = vid; msleep(200); } if (dir) i++; else i--; } |
7aa0557fa
|
646 |
retval = longhaul_setstate(policy, table_index); |
73e107d4a
|
647 |
} |
7aa0557fa
|
648 |
|
73e107d4a
|
649 |
longhaul_index = table_index; |
7aa0557fa
|
650 |
return retval; |
1da177e4c
|
651 652 653 654 655 656 657 658 659 |
} static unsigned int longhaul_get(unsigned int cpu) { if (cpu) return 0; return calc_speed(longhaul_get_cpu_mult()); } |
c4a96c1eb
|
660 661 662 |
static acpi_status longhaul_walk_callback(acpi_handle obj_handle, u32 nesting_level, void *context, void **return_value) |
dadb49d87
|
663 664 |
{ struct acpi_device *d; |
ac617bd0f
|
665 |
if (acpi_bus_get_device(obj_handle, &d)) |
dadb49d87
|
666 |
return 0; |
ac617bd0f
|
667 |
|
ade1af771
|
668 |
*return_value = acpi_driver_data(d); |
dadb49d87
|
669 670 |
return 1; } |
1da177e4c
|
671 |
|
179da8e6e
|
672 673 674 675 |
/* VIA don't support PM2 reg, but have something similar */ static int enable_arbiter_disable(void) { struct pci_dev *dev; |
73e107d4a
|
676 |
int status = 1; |
7f1be8924
|
677 |
int reg; |
179da8e6e
|
678 679 680 |
u8 pci_cmd; /* Find PLE133 host bridge */ |
7f1be8924
|
681 |
reg = 0x78; |
fb48e1564
|
682 683 |
dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0, NULL); |
a09d60a62
|
684 685 686 687 |
/* Find PM133/VT8605 host bridge */ if (dev == NULL) dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8605_0, NULL); |
7f1be8924
|
688 689 |
/* Find CLE266 host bridge */ if (dev == NULL) { |
7f1be8924
|
690 |
reg = 0x76; |
fb48e1564
|
691 692 |
dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_862X_0, NULL); |
db2fb9db5
|
693 694 |
/* Find CN400 V-Link host bridge */ if (dev == NULL) |
fb48e1564
|
695 |
dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL); |
7f1be8924
|
696 |
} |
179da8e6e
|
697 698 |
if (dev != NULL) { /* Enable access to port 0x22 */ |
7f1be8924
|
699 |
pci_read_config_byte(dev, reg, &pci_cmd); |
786f46b26
|
700 |
if (!(pci_cmd & 1<<7)) { |
179da8e6e
|
701 |
pci_cmd |= 1<<7; |
7f1be8924
|
702 |
pci_write_config_byte(dev, reg, pci_cmd); |
786f46b26
|
703 704 |
pci_read_config_byte(dev, reg, &pci_cmd); if (!(pci_cmd & 1<<7)) { |
1c5864e26
|
705 706 |
pr_err("Can't enable access to port 0x22 "); |
fb48e1564
|
707 |
status = 0; |
786f46b26
|
708 |
} |
179da8e6e
|
709 |
} |
fb48e1564
|
710 711 |
pci_dev_put(dev); return status; |
179da8e6e
|
712 713 714 |
} return 0; } |
7d5edcc02
|
715 |
static int longhaul_setup_southbridge(void) |
786f46b26
|
716 717 718 719 720 |
{ struct pci_dev *dev; u8 pci_cmd; /* Find VT8235 southbridge */ |
fb48e1564
|
721 |
dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL); |
920dd0fbb
|
722 |
if (dev == NULL) |
ac617bd0f
|
723 |
/* Find VT8237 southbridge */ |
920dd0fbb
|
724 725 |
dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, NULL); |
786f46b26
|
726 727 728 729 730 731 732 733 734 735 736 |
if (dev != NULL) { /* Set transition time to max */ pci_read_config_byte(dev, 0xec, &pci_cmd); pci_cmd &= ~(1 << 2); pci_write_config_byte(dev, 0xec, pci_cmd); pci_read_config_byte(dev, 0xe4, &pci_cmd); pci_cmd &= ~(1 << 7); pci_write_config_byte(dev, 0xe4, pci_cmd); pci_read_config_byte(dev, 0xe5, &pci_cmd); pci_cmd |= 1 << 7; pci_write_config_byte(dev, 0xe5, pci_cmd); |
275bc6b7f
|
737 738 739 740 741 |
/* Get address of ACPI registers block*/ pci_read_config_byte(dev, 0x81, &pci_cmd); if (pci_cmd & 1 << 7) { pci_read_config_dword(dev, 0x88, &acpi_regs_addr); acpi_regs_addr &= 0xff00; |
1c5864e26
|
742 743 |
pr_info("ACPI I/O at 0x%x ", acpi_regs_addr); |
275bc6b7f
|
744 |
} |
fb48e1564
|
745 |
pci_dev_put(dev); |
786f46b26
|
746 747 748 749 |
return 1; } return 0; } |
2760984f6
|
750 |
static int longhaul_cpu_init(struct cpufreq_policy *policy) |
1da177e4c
|
751 |
{ |
92cb7612a
|
752 |
struct cpuinfo_x86 *c = &cpu_data(0); |
ac617bd0f
|
753 |
char *cpuname = NULL; |
1da177e4c
|
754 |
int ret; |
2b8c0e130
|
755 |
u32 lo, hi; |
1da177e4c
|
756 |
|
179da8e6e
|
757 |
/* Check what we have on this motherboard */ |
1da177e4c
|
758 759 760 761 762 |
switch (c->x86_model) { case 6: cpu_model = CPU_SAMUEL; cpuname = "C3 'Samuel' [C5A]"; longhaul_version = TYPE_LONGHAUL_V1; |
ac617bd0f
|
763 764 |
memcpy(mults, samuel1_mults, sizeof(samuel1_mults)); memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr)); |
1da177e4c
|
765 766 767 |
break; case 7: |
325cbb04d
|
768 |
switch (c->x86_stepping) { |
1da177e4c
|
769 |
case 0: |
2b8c0e130
|
770 |
longhaul_version = TYPE_LONGHAUL_V1; |
1da177e4c
|
771 772 |
cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; |
2b8c0e130
|
773 774 |
/* Note, this is not a typo, early Samuel2's had * Samuel1 ratios. */ |
ac617bd0f
|
775 776 |
memcpy(mults, samuel1_mults, sizeof(samuel1_mults)); memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr)); |
1da177e4c
|
777 778 |
break; case 1 ... 15: |
f7f3cad06
|
779 |
longhaul_version = TYPE_LONGHAUL_V2; |
325cbb04d
|
780 |
if (c->x86_stepping < 8) { |
1da177e4c
|
781 782 783 784 785 786 |
cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; } else { cpu_model = CPU_EZRA; cpuname = "C3 'Ezra' [C5C]"; } |
ac617bd0f
|
787 788 |
memcpy(mults, ezra_mults, sizeof(ezra_mults)); memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr)); |
1da177e4c
|
789 790 791 792 793 794 795 796 |
break; } break; case 8: cpu_model = CPU_EZRA_T; cpuname = "C3 'Ezra-T' [C5M]"; longhaul_version = TYPE_POWERSAVER; |
ac617bd0f
|
797 798 799 |
numscales = 32; memcpy(mults, ezrat_mults, sizeof(ezrat_mults)); memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr)); |
1da177e4c
|
800 801 802 |
break; case 9: |
1da177e4c
|
803 |
longhaul_version = TYPE_POWERSAVER; |
0d44b2ba2
|
804 |
numscales = 32; |
ac617bd0f
|
805 806 |
memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults)); memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr)); |
325cbb04d
|
807 |
switch (c->x86_stepping) { |
1da177e4c
|
808 |
case 0 ... 1: |
980342a7e
|
809 |
cpu_model = CPU_NEHEMIAH; |
e57501c15
|
810 |
cpuname = "C3 'Nehemiah A' [C5XLOE]"; |
1da177e4c
|
811 812 |
break; case 2 ... 4: |
980342a7e
|
813 |
cpu_model = CPU_NEHEMIAH; |
e57501c15
|
814 |
cpuname = "C3 'Nehemiah B' [C5XLOH]"; |
1da177e4c
|
815 816 |
break; case 5 ... 15: |
980342a7e
|
817 |
cpu_model = CPU_NEHEMIAH_C; |
e57501c15
|
818 |
cpuname = "C3 'Nehemiah C' [C5P]"; |
1da177e4c
|
819 820 821 822 823 824 825 826 |
break; } break; default: cpuname = "Unknown"; break; } |
2b8c0e130
|
827 828 829 830 831 832 833 |
/* Check Longhaul ver. 2 */ if (longhaul_version == TYPE_LONGHAUL_V2) { rdmsr(MSR_VIA_LONGHAUL, lo, hi); if (lo == 0 && hi == 0) /* Looks like MSR isn't present */ longhaul_version = TYPE_LONGHAUL_V1; } |
1da177e4c
|
834 |
|
1c5864e26
|
835 |
pr_info("VIA %s CPU detected. ", cpuname); |
1da177e4c
|
836 837 838 |
switch (longhaul_version) { case TYPE_LONGHAUL_V1: case TYPE_LONGHAUL_V2: |
b49c22a6c
|
839 840 |
pr_cont("Longhaul v%d supported ", longhaul_version); |
1da177e4c
|
841 842 |
break; case TYPE_POWERSAVER: |
b49c22a6c
|
843 844 |
pr_cont("Powersaver supported "); |
1da177e4c
|
845 846 |
break; }; |
786f46b26
|
847 |
/* Doesn't hurt */ |
7d5edcc02
|
848 |
longhaul_setup_southbridge(); |
786f46b26
|
849 |
|
179da8e6e
|
850 |
/* Find ACPI data for processor */ |
786f46b26
|
851 |
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, |
2263576cf
|
852 |
ACPI_UINT32_MAX, &longhaul_walk_callback, NULL, |
786f46b26
|
853 |
NULL, (void *)&pr); |
179da8e6e
|
854 |
|
264166e60
|
855 |
/* Check ACPI support for C3 state */ |
7ab77e03c
|
856 |
if (pr != NULL && longhaul_version == TYPE_POWERSAVER) { |
179da8e6e
|
857 |
cx = &pr->power.states[ACPI_STATE_C3]; |
7d5edcc02
|
858 |
if (cx->address > 0 && cx->latency <= 1000) |
264166e60
|
859 |
longhaul_flags |= USE_ACPI_C3; |
eed7d4125
|
860 |
} |
905497c4b
|
861 862 863 |
/* Disable if it isn't working */ if (disable_acpi_c3) longhaul_flags &= ~USE_ACPI_C3; |
264166e60
|
864 |
/* Check if northbridge is friendly */ |
7d5edcc02
|
865 |
if (enable_arbiter_disable()) |
264166e60
|
866 |
longhaul_flags |= USE_NORTHBRIDGE; |
7d5edcc02
|
867 |
|
eed7d4125
|
868 |
/* Check ACPI support for bus master arbiter disable */ |
7d5edcc02
|
869 870 871 |
if (!(longhaul_flags & USE_ACPI_C3 || longhaul_flags & USE_NORTHBRIDGE) && ((pr == NULL) || !(pr->flags.bm_control))) { |
1c5864e26
|
872 873 |
pr_err("No ACPI support: Unsupported northbridge "); |
264166e60
|
874 |
return -ENODEV; |
179da8e6e
|
875 |
} |
264166e60
|
876 |
|
786f46b26
|
877 |
if (longhaul_flags & USE_NORTHBRIDGE) |
1c5864e26
|
878 879 |
pr_info("Using northbridge support "); |
7d5edcc02
|
880 |
if (longhaul_flags & USE_ACPI_C3) |
1c5864e26
|
881 882 |
pr_info("Using ACPI support "); |
179da8e6e
|
883 |
|
1da177e4c
|
884 885 886 |
ret = longhaul_get_ranges(); if (ret != 0) return ret; |
786f46b26
|
887 |
if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0)) |
1da177e4c
|
888 |
longhaul_setup_voltagescaling(); |
b453f9d8c
|
889 |
policy->transition_delay_us = 200000; /* usec */ |
1da177e4c
|
890 |
|
30aa53412
|
891 |
return cpufreq_table_validate_and_show(policy, longhaul_table); |
1da177e4c
|
892 |
} |
221dee285
|
893 |
static struct cpufreq_driver longhaul_driver = { |
3a4d0342e
|
894 |
.verify = cpufreq_generic_frequency_table_verify, |
9c0ebcf78
|
895 |
.target_index = longhaul_target, |
1da177e4c
|
896 897 |
.get = longhaul_get, .init = longhaul_cpu_init, |
1da177e4c
|
898 |
.name = "longhaul", |
3a4d0342e
|
899 |
.attr = cpufreq_generic_attr, |
1da177e4c
|
900 |
}; |
fa8031aef
|
901 902 903 904 905 |
static const struct x86_cpu_id longhaul_id[] = { { X86_VENDOR_CENTAUR, 6 }, {} }; MODULE_DEVICE_TABLE(x86cpu, longhaul_id); |
1da177e4c
|
906 907 908 |
static int __init longhaul_init(void) { |
92cb7612a
|
909 |
struct cpuinfo_x86 *c = &cpu_data(0); |
1da177e4c
|
910 |
|
fa8031aef
|
911 |
if (!x86_match_cpu(longhaul_id)) |
1da177e4c
|
912 |
return -ENODEV; |
b5811bc46
|
913 |
if (!enable) { |
1c5864e26
|
914 915 |
pr_err("Option \"enable\" not set - Aborting "); |
b5811bc46
|
916 917 |
return -ENODEV; } |
48b7bde0f
|
918 919 |
#ifdef CONFIG_SMP if (num_online_cpus() > 1) { |
1c5864e26
|
920 921 |
pr_err("More than 1 CPU detected, longhaul disabled "); |
1cfe20142
|
922 |
return -ENODEV; |
48b7bde0f
|
923 924 925 |
} #endif #ifdef CONFIG_X86_IO_APIC |
93984fbd4
|
926 |
if (boot_cpu_has(X86_FEATURE_APIC)) { |
1c5864e26
|
927 928 |
pr_err("APIC detected. Longhaul is currently broken in this configuration. "); |
48b7bde0f
|
929 930 931 |
return -ENODEV; } #endif |
1da177e4c
|
932 933 934 |
switch (c->x86_model) { case 6 ... 9: return cpufreq_register_driver(&longhaul_driver); |
8ec9822dd
|
935 |
case 10: |
1c5864e26
|
936 937 |
pr_err("Use acpi-cpufreq driver for VIA C7 "); |
1da177e4c
|
938 |
default: |
c19a28e11
|
939 |
; |
1da177e4c
|
940 941 942 943 944 945 946 947 |
} return -ENODEV; } static void __exit longhaul_exit(void) { |
b43a7ffbf
|
948 |
struct cpufreq_policy *policy = cpufreq_cpu_get(0); |
8eebf1a4c
|
949 |
int i; |
1da177e4c
|
950 |
|
ac617bd0f
|
951 952 |
for (i = 0; i < numscales; i++) { if (mults[i] == maxmult) { |
7aa0557fa
|
953 954 955 956 957 958 959 |
struct cpufreq_freqs freqs; freqs.old = policy->cur; freqs.new = longhaul_table[i].frequency; freqs.flags = 0; cpufreq_freq_transition_begin(policy, &freqs); |
b43a7ffbf
|
960 |
longhaul_setstate(policy, i); |
7aa0557fa
|
961 |
cpufreq_freq_transition_end(policy, &freqs, 0); |
1da177e4c
|
962 963 964 |
break; } } |
b43a7ffbf
|
965 |
cpufreq_cpu_put(policy); |
1da177e4c
|
966 967 968 |
cpufreq_unregister_driver(&longhaul_driver); kfree(longhaul_table); } |
52a2638bf
|
969 970 971 |
/* Even if BIOS is exporting ACPI C3 state, and it is used * with success when CPU is idle, this state doesn't * trigger frequency transition in some cases. */ |
ac617bd0f
|
972 |
module_param(disable_acpi_c3, int, 0644); |
905497c4b
|
973 |
MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support"); |
0d2eb44f6
|
974 |
/* Change CPU voltage with frequency. Very useful to save |
52a2638bf
|
975 |
* power, but most VIA C3 processors aren't supporting it. */ |
ac617bd0f
|
976 |
module_param(scale_voltage, int, 0644); |
db44aaf3a
|
977 |
MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor"); |
52a2638bf
|
978 979 980 981 982 |
/* Force revision key to 0 for processors which doesn't * support voltage scaling, but are introducing itself as * such. */ module_param(revid_errata, int, 0644); MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID"); |
b5811bc46
|
983 984 985 986 |
/* By default driver is disabled to prevent incompatible * system freeze. */ module_param(enable, int, 0644); MODULE_PARM_DESC(enable, "Enable driver"); |
1da177e4c
|
987 |
|
d5e80b4b1
|
988 |
MODULE_AUTHOR("Dave Jones"); |
ac617bd0f
|
989 990 |
MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors."); MODULE_LICENSE("GPL"); |
1da177e4c
|
991 |
|
0d6daba5f
|
992 |
late_initcall(longhaul_init); |
1da177e4c
|
993 |
module_exit(longhaul_exit); |