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