aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorRafaƂ Bilski <rafalbilski@interia.pl>2007-05-28 15:56:19 -0400
committerDave Jones <davej@redhat.com>2007-05-29 16:56:40 -0400
commit73e107d4a156affeed510cf5745177fd893878f1 (patch)
tree76876ef3ede406b909d4f6cdd643aba72c37f8c5 /arch/i386
parent13424f6514f6444554a103362dd9d31eabbbdc54 (diff)
[CPUFREQ] Longhaul - Embedded "conservative"
Longhaul with voltage scaling enabled works great on Ezra CPU (Longhaul ver. 2). As long as "conservative" governor is used. Both "ondemand" and "userspace" can change voltage from min to max at once. Motherboard unfortunatly turns off when vid difference is big. Longhaul was printing warning message, but it is not enough. Now driver will have "conservative" governor built in and will split bigger changes to smaller ones. Signed-off-by: Rafal Bilski <rafalbilski@interia.pl> Signed-off-by: Dave Jones <davej@redhat.com>
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/kernel/cpu/cpufreq/longhaul.c145
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
63static const struct mV_pos *vrm_mV_table; 64static const struct mV_pos *vrm_mV_table;
64static const unsigned char *mV_vrm_table; 65static const unsigned char *mV_vrm_table;
65struct f_msr {
66 u8 vrm;
67 u8 pos;
68};
69static struct f_msr f_msr_table[32];
70 66
71static unsigned int highest_speed, lowest_speed; /* kHz */ 67static unsigned int highest_speed, lowest_speed; /* kHz */
72static unsigned int minmult, maxmult; 68static unsigned int minmult, maxmult;
@@ -74,7 +70,7 @@ static int can_scale_voltage;
74static struct acpi_processor *pr = NULL; 70static struct acpi_processor *pr = NULL;
75static struct acpi_processor_cx *cx = NULL; 71static struct acpi_processor_cx *cx = NULL;
76static u8 longhaul_flags; 72static u8 longhaul_flags;
77static u8 longhaul_pos; 73static unsigned int longhaul_index;
78 74
79/* Module parameters */ 75/* Module parameters */
80static int scale_voltage; 76static int scale_voltage;
@@ -87,7 +83,6 @@ static int clock_ratio[32];
87static int eblcr_table[32]; 83static int eblcr_table[32];
88static int longhaul_version; 84static int longhaul_version;
89static struct cpufreq_frequency_table *longhaul_table; 85static struct cpufreq_frequency_table *longhaul_table;
90static unsigned int old_ratio = -1;
91 86
92#ifdef CONFIG_CPU_FREQ_DEBUG 87#ifdef CONFIG_CPU_FREQ_DEBUG
93static char speedbuffer[8]; 88static 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
166static void do_powersaver(int cx_address, unsigned int clock_ratio_index) 161static 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
251static void longhaul_setstate(unsigned int clock_ratio_index) 242static 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
387static int __init longhaul_get_ranges(void) 380static 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)
465static void __init longhaul_setup_voltagescaling(void) 486static 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,
607static int enable_arbiter_disable(void) 655static 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,