diff options
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.c | 145 |
1 files changed, 96 insertions, 49 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index ff4829666472..dde4e3149179 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/pci.h> | 29 | #include <linux/pci.h> |
30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
31 | #include <linux/string.h> | 31 | #include <linux/string.h> |
32 | #include <linux/delay.h> | ||
32 | 33 | ||
33 | #include <asm/msr.h> | 34 | #include <asm/msr.h> |
34 | #include <asm/timex.h> | 35 | #include <asm/timex.h> |
@@ -62,11 +63,6 @@ static unsigned int fsb; | |||
62 | 63 | ||
63 | static const struct mV_pos *vrm_mV_table; | 64 | static const struct mV_pos *vrm_mV_table; |
64 | static const unsigned char *mV_vrm_table; | 65 | static const unsigned char *mV_vrm_table; |
65 | struct f_msr { | ||
66 | u8 vrm; | ||
67 | u8 pos; | ||
68 | }; | ||
69 | static struct f_msr f_msr_table[32]; | ||
70 | 66 | ||
71 | static unsigned int highest_speed, lowest_speed; /* kHz */ | 67 | static unsigned int highest_speed, lowest_speed; /* kHz */ |
72 | static unsigned int minmult, maxmult; | 68 | static unsigned int minmult, maxmult; |
@@ -74,7 +70,7 @@ static int can_scale_voltage; | |||
74 | static struct acpi_processor *pr = NULL; | 70 | static struct acpi_processor *pr = NULL; |
75 | static struct acpi_processor_cx *cx = NULL; | 71 | static struct acpi_processor_cx *cx = NULL; |
76 | static u8 longhaul_flags; | 72 | static u8 longhaul_flags; |
77 | static u8 longhaul_pos; | 73 | static unsigned int longhaul_index; |
78 | 74 | ||
79 | /* Module parameters */ | 75 | /* Module parameters */ |
80 | static int scale_voltage; | 76 | static int scale_voltage; |
@@ -87,7 +83,6 @@ static int clock_ratio[32]; | |||
87 | static int eblcr_table[32]; | 83 | static int eblcr_table[32]; |
88 | static int longhaul_version; | 84 | static int longhaul_version; |
89 | static struct cpufreq_frequency_table *longhaul_table; | 85 | static struct cpufreq_frequency_table *longhaul_table; |
90 | static unsigned int old_ratio = -1; | ||
91 | 86 | ||
92 | #ifdef CONFIG_CPU_FREQ_DEBUG | 87 | #ifdef CONFIG_CPU_FREQ_DEBUG |
93 | static char speedbuffer[8]; | 88 | static char speedbuffer[8]; |
@@ -144,7 +139,7 @@ static void do_longhaul1(unsigned int clock_ratio_index) | |||
144 | rdmsrl(MSR_VIA_BCR2, bcr2.val); | 139 | rdmsrl(MSR_VIA_BCR2, bcr2.val); |
145 | /* Enable software clock multiplier */ | 140 | /* Enable software clock multiplier */ |
146 | bcr2.bits.ESOFTBF = 1; | 141 | bcr2.bits.ESOFTBF = 1; |
147 | bcr2.bits.CLOCKMUL = clock_ratio_index; | 142 | bcr2.bits.CLOCKMUL = clock_ratio_index & 0xff; |
148 | 143 | ||
149 | /* Sync to timer tick */ | 144 | /* Sync to timer tick */ |
150 | safe_halt(); | 145 | safe_halt(); |
@@ -163,14 +158,12 @@ static void do_longhaul1(unsigned int clock_ratio_index) | |||
163 | 158 | ||
164 | /* For processor with Longhaul MSR */ | 159 | /* For processor with Longhaul MSR */ |
165 | 160 | ||
166 | static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | 161 | static void do_powersaver(int cx_address, unsigned int clock_ratio_index, |
162 | unsigned int dir) | ||
167 | { | 163 | { |
168 | union msr_longhaul longhaul; | 164 | union msr_longhaul longhaul; |
169 | u8 dest_pos; | ||
170 | u32 t; | 165 | u32 t; |
171 | 166 | ||
172 | dest_pos = f_msr_table[clock_ratio_index].pos; | ||
173 | |||
174 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 167 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
175 | /* Setup new frequency */ | 168 | /* Setup new frequency */ |
176 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; | 169 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; |
@@ -178,11 +171,11 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
178 | longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; | 171 | longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; |
179 | /* Setup new voltage */ | 172 | /* Setup new voltage */ |
180 | if (can_scale_voltage) | 173 | if (can_scale_voltage) |
181 | longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm; | 174 | longhaul.bits.SoftVID = (clock_ratio_index >> 8) & 0x1f; |
182 | /* Sync to timer tick */ | 175 | /* Sync to timer tick */ |
183 | safe_halt(); | 176 | safe_halt(); |
184 | /* Raise voltage if necessary */ | 177 | /* Raise voltage if necessary */ |
185 | if (can_scale_voltage && longhaul_pos < dest_pos) { | 178 | if (can_scale_voltage && dir) { |
186 | longhaul.bits.EnableSoftVID = 1; | 179 | longhaul.bits.EnableSoftVID = 1; |
187 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 180 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
188 | /* Change voltage */ | 181 | /* Change voltage */ |
@@ -199,7 +192,6 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
199 | } | 192 | } |
200 | longhaul.bits.EnableSoftVID = 0; | 193 | longhaul.bits.EnableSoftVID = 0; |
201 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 194 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
202 | longhaul_pos = dest_pos; | ||
203 | } | 195 | } |
204 | 196 | ||
205 | /* Change frequency on next halt or sleep */ | 197 | /* Change frequency on next halt or sleep */ |
@@ -220,7 +212,7 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
220 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 212 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
221 | 213 | ||
222 | /* Reduce voltage if necessary */ | 214 | /* Reduce voltage if necessary */ |
223 | if (can_scale_voltage && longhaul_pos > dest_pos) { | 215 | if (can_scale_voltage && !dir) { |
224 | longhaul.bits.EnableSoftVID = 1; | 216 | longhaul.bits.EnableSoftVID = 1; |
225 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 217 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
226 | /* Change voltage */ | 218 | /* Change voltage */ |
@@ -237,7 +229,6 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
237 | } | 229 | } |
238 | longhaul.bits.EnableSoftVID = 0; | 230 | longhaul.bits.EnableSoftVID = 0; |
239 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 231 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
240 | longhaul_pos = dest_pos; | ||
241 | } | 232 | } |
242 | } | 233 | } |
243 | 234 | ||
@@ -248,26 +239,28 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
248 | * Sets a new clock ratio. | 239 | * Sets a new clock ratio. |
249 | */ | 240 | */ |
250 | 241 | ||
251 | static void longhaul_setstate(unsigned int clock_ratio_index) | 242 | static void longhaul_setstate(unsigned int table_index) |
252 | { | 243 | { |
244 | unsigned int clock_ratio_index; | ||
253 | int speed, mult; | 245 | int speed, mult; |
254 | struct cpufreq_freqs freqs; | 246 | struct cpufreq_freqs freqs; |
255 | unsigned long flags; | 247 | unsigned long flags; |
256 | unsigned int pic1_mask, pic2_mask; | 248 | unsigned int pic1_mask, pic2_mask; |
257 | u32 bm_status = 0; | 249 | u32 bm_status = 0; |
258 | u32 bm_timeout = 100000; | 250 | u32 bm_timeout = 100000; |
251 | unsigned int dir = 0; | ||
259 | 252 | ||
260 | if (old_ratio == clock_ratio_index) | 253 | clock_ratio_index = longhaul_table[table_index].index; |
261 | return; | 254 | /* Safety precautions */ |
262 | old_ratio = clock_ratio_index; | 255 | mult = clock_ratio[clock_ratio_index & 0x1f]; |
263 | |||
264 | mult = clock_ratio[clock_ratio_index]; | ||
265 | if (mult == -1) | 256 | if (mult == -1) |
266 | return; | 257 | return; |
267 | |||
268 | speed = calc_speed(mult); | 258 | speed = calc_speed(mult); |
269 | if ((speed > highest_speed) || (speed < lowest_speed)) | 259 | if ((speed > highest_speed) || (speed < lowest_speed)) |
270 | return; | 260 | return; |
261 | /* Voltage transition before frequency transition? */ | ||
262 | if (can_scale_voltage && longhaul_index < table_index) | ||
263 | dir = 1; | ||
271 | 264 | ||
272 | freqs.old = calc_speed(longhaul_get_cpu_mult()); | 265 | freqs.old = calc_speed(longhaul_get_cpu_mult()); |
273 | freqs.new = speed; | 266 | freqs.new = speed; |
@@ -302,7 +295,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
302 | /* Disable AGP and PCI arbiters */ | 295 | /* Disable AGP and PCI arbiters */ |
303 | outb(3, 0x22); | 296 | outb(3, 0x22); |
304 | } else if ((pr != NULL) && pr->flags.bm_control) { | 297 | } else if ((pr != NULL) && pr->flags.bm_control) { |
305 | /* Disable bus master arbitration */ | 298 | /* Disable bus master arbitration */ |
306 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); | 299 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); |
307 | } | 300 | } |
308 | switch (longhaul_version) { | 301 | switch (longhaul_version) { |
@@ -327,9 +320,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
327 | if (longhaul_flags & USE_ACPI_C3) { | 320 | if (longhaul_flags & USE_ACPI_C3) { |
328 | /* Don't allow wakeup */ | 321 | /* Don't allow wakeup */ |
329 | acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); | 322 | acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); |
330 | do_powersaver(cx->address, clock_ratio_index); | 323 | do_powersaver(cx->address, clock_ratio_index, dir); |
331 | } else { | 324 | } else { |
332 | do_powersaver(0, clock_ratio_index); | 325 | do_powersaver(0, clock_ratio_index, dir); |
333 | } | 326 | } |
334 | break; | 327 | break; |
335 | } | 328 | } |
@@ -386,7 +379,8 @@ static int guess_fsb(int mult) | |||
386 | 379 | ||
387 | static int __init longhaul_get_ranges(void) | 380 | static int __init longhaul_get_ranges(void) |
388 | { | 381 | { |
389 | unsigned int j, k = 0; | 382 | unsigned int i, j, k = 0; |
383 | unsigned int ratio; | ||
390 | int mult; | 384 | int mult; |
391 | 385 | ||
392 | /* Get current frequency */ | 386 | /* Get current frequency */ |
@@ -440,8 +434,7 @@ static int __init longhaul_get_ranges(void) | |||
440 | if(!longhaul_table) | 434 | if(!longhaul_table) |
441 | return -ENOMEM; | 435 | return -ENOMEM; |
442 | 436 | ||
443 | for (j=0; j < numscales; j++) { | 437 | for (j = 0; j < numscales; j++) { |
444 | unsigned int ratio; | ||
445 | ratio = clock_ratio[j]; | 438 | ratio = clock_ratio[j]; |
446 | if (ratio == -1) | 439 | if (ratio == -1) |
447 | continue; | 440 | continue; |
@@ -451,13 +444,41 @@ static int __init longhaul_get_ranges(void) | |||
451 | longhaul_table[k].index = j; | 444 | longhaul_table[k].index = j; |
452 | k++; | 445 | k++; |
453 | } | 446 | } |
447 | if (k <= 1) { | ||
448 | kfree(longhaul_table); | ||
449 | return -ENODEV; | ||
450 | } | ||
451 | /* Sort */ | ||
452 | for (j = 0; j < k - 1; j++) { | ||
453 | unsigned int min_f, min_i; | ||
454 | min_f = longhaul_table[j].frequency; | ||
455 | min_i = j; | ||
456 | for (i = j + 1; i < k; i++) { | ||
457 | if (longhaul_table[i].frequency < min_f) { | ||
458 | min_f = longhaul_table[i].frequency; | ||
459 | min_i = i; | ||
460 | } | ||
461 | } | ||
462 | if (min_i != j) { | ||
463 | unsigned int temp; | ||
464 | temp = longhaul_table[j].frequency; | ||
465 | longhaul_table[j].frequency = longhaul_table[min_i].frequency; | ||
466 | longhaul_table[min_i].frequency = temp; | ||
467 | temp = longhaul_table[j].index; | ||
468 | longhaul_table[j].index = longhaul_table[min_i].index; | ||
469 | longhaul_table[min_i].index = temp; | ||
470 | } | ||
471 | } | ||
454 | 472 | ||
455 | longhaul_table[k].frequency = CPUFREQ_TABLE_END; | 473 | longhaul_table[k].frequency = CPUFREQ_TABLE_END; |
456 | if (!k) { | ||
457 | kfree (longhaul_table); | ||
458 | return -EINVAL; | ||
459 | } | ||
460 | 474 | ||
475 | /* Find index we are running on */ | ||
476 | for (j = 0; j < k; j++) { | ||
477 | if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) { | ||
478 | longhaul_index = j; | ||
479 | break; | ||
480 | } | ||
481 | } | ||
461 | return 0; | 482 | return 0; |
462 | } | 483 | } |
463 | 484 | ||
@@ -465,7 +486,7 @@ static int __init longhaul_get_ranges(void) | |||
465 | static void __init longhaul_setup_voltagescaling(void) | 486 | static void __init longhaul_setup_voltagescaling(void) |
466 | { | 487 | { |
467 | union msr_longhaul longhaul; | 488 | union msr_longhaul longhaul; |
468 | struct mV_pos minvid, maxvid; | 489 | struct mV_pos minvid, maxvid, vid; |
469 | unsigned int j, speed, pos, kHz_step, numvscales; | 490 | unsigned int j, speed, pos, kHz_step, numvscales; |
470 | int min_vid_speed; | 491 | int min_vid_speed; |
471 | 492 | ||
@@ -476,11 +497,11 @@ static void __init longhaul_setup_voltagescaling(void) | |||
476 | } | 497 | } |
477 | 498 | ||
478 | if (!longhaul.bits.VRMRev) { | 499 | if (!longhaul.bits.VRMRev) { |
479 | printk (KERN_INFO PFX "VRM 8.5\n"); | 500 | printk(KERN_INFO PFX "VRM 8.5\n"); |
480 | vrm_mV_table = &vrm85_mV[0]; | 501 | vrm_mV_table = &vrm85_mV[0]; |
481 | mV_vrm_table = &mV_vrm85[0]; | 502 | mV_vrm_table = &mV_vrm85[0]; |
482 | } else { | 503 | } else { |
483 | printk (KERN_INFO PFX "Mobile VRM\n"); | 504 | printk(KERN_INFO PFX "Mobile VRM\n"); |
484 | if (cpu_model < CPU_NEHEMIAH) | 505 | if (cpu_model < CPU_NEHEMIAH) |
485 | return; | 506 | return; |
486 | vrm_mV_table = &mobilevrm_mV[0]; | 507 | vrm_mV_table = &mobilevrm_mV[0]; |
@@ -540,7 +561,6 @@ static void __init longhaul_setup_voltagescaling(void) | |||
540 | /* Calculate kHz for one voltage step */ | 561 | /* Calculate kHz for one voltage step */ |
541 | kHz_step = (highest_speed - min_vid_speed) / numvscales; | 562 | kHz_step = (highest_speed - min_vid_speed) / numvscales; |
542 | 563 | ||
543 | |||
544 | j = 0; | 564 | j = 0; |
545 | while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { | 565 | while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { |
546 | speed = longhaul_table[j].frequency; | 566 | speed = longhaul_table[j].frequency; |
@@ -548,15 +568,14 @@ static void __init longhaul_setup_voltagescaling(void) | |||
548 | pos = (speed - min_vid_speed) / kHz_step + minvid.pos; | 568 | pos = (speed - min_vid_speed) / kHz_step + minvid.pos; |
549 | else | 569 | else |
550 | pos = minvid.pos; | 570 | pos = minvid.pos; |
551 | f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos]; | 571 | longhaul_table[j].index |= mV_vrm_table[pos] << 8; |
552 | f_msr_table[longhaul_table[j].index].pos = pos; | 572 | vid = vrm_mV_table[mV_vrm_table[pos]]; |
573 | printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV); | ||
553 | j++; | 574 | j++; |
554 | } | 575 | } |
555 | 576 | ||
556 | longhaul_pos = maxvid.pos; | ||
557 | can_scale_voltage = 1; | 577 | can_scale_voltage = 1; |
558 | printk(KERN_INFO PFX "Voltage scaling enabled. " | 578 | printk(KERN_INFO PFX "Voltage scaling enabled.\n"); |
559 | "Use of \"conservative\" governor is highly recommended.\n"); | ||
560 | } | 579 | } |
561 | 580 | ||
562 | 581 | ||
@@ -570,15 +589,44 @@ static int longhaul_target(struct cpufreq_policy *policy, | |||
570 | unsigned int target_freq, unsigned int relation) | 589 | unsigned int target_freq, unsigned int relation) |
571 | { | 590 | { |
572 | unsigned int table_index = 0; | 591 | unsigned int table_index = 0; |
573 | unsigned int new_clock_ratio = 0; | 592 | unsigned int i; |
593 | unsigned int dir = 0; | ||
594 | u8 vid, current_vid; | ||
574 | 595 | ||
575 | if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) | 596 | if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) |
576 | return -EINVAL; | 597 | return -EINVAL; |
577 | 598 | ||
578 | new_clock_ratio = longhaul_table[table_index].index & 0xFF; | 599 | /* Don't set same frequency again */ |
579 | 600 | if (longhaul_index == table_index) | |
580 | longhaul_setstate(new_clock_ratio); | 601 | return 0; |
581 | 602 | ||
603 | if (!can_scale_voltage) | ||
604 | longhaul_setstate(table_index); | ||
605 | else { | ||
606 | /* On test system voltage transitions exceeding single | ||
607 | * step up or down were turning motherboard off. Both | ||
608 | * "ondemand" and "userspace" are unsafe. C7 is doing | ||
609 | * this in hardware, C3 is old and we need to do this | ||
610 | * in software. */ | ||
611 | i = longhaul_index; | ||
612 | current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f; | ||
613 | if (table_index > longhaul_index) | ||
614 | dir = 1; | ||
615 | while (i != table_index) { | ||
616 | vid = (longhaul_table[i].index >> 8) & 0x1f; | ||
617 | if (vid != current_vid) { | ||
618 | longhaul_setstate(i); | ||
619 | current_vid = vid; | ||
620 | msleep(200); | ||
621 | } | ||
622 | if (dir) | ||
623 | i++; | ||
624 | else | ||
625 | i--; | ||
626 | } | ||
627 | longhaul_setstate(table_index); | ||
628 | } | ||
629 | longhaul_index = table_index; | ||
582 | return 0; | 630 | return 0; |
583 | } | 631 | } |
584 | 632 | ||
@@ -607,11 +655,10 @@ static acpi_status longhaul_walk_callback(acpi_handle obj_handle, | |||
607 | static int enable_arbiter_disable(void) | 655 | static int enable_arbiter_disable(void) |
608 | { | 656 | { |
609 | struct pci_dev *dev; | 657 | struct pci_dev *dev; |
610 | int status; | 658 | int status = 1; |
611 | int reg; | 659 | int reg; |
612 | u8 pci_cmd; | 660 | u8 pci_cmd; |
613 | 661 | ||
614 | status = 1; | ||
615 | /* Find PLE133 host bridge */ | 662 | /* Find PLE133 host bridge */ |
616 | reg = 0x78; | 663 | reg = 0x78; |
617 | dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0, | 664 | dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0, |