diff options
author | Rafał Bilski <rafalbilski@interia.pl> | 2007-02-08 12:56:04 -0500 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2007-02-10 20:05:50 -0500 |
commit | 348f31ed2bd18391fe5903aa0ad7bfcda6d8ca0b (patch) | |
tree | 1752aedc8f7780cef02688e4df386a93188c700b /arch/i386 | |
parent | e57501c15f48d6b7a8fe2b023be8f4779484482d (diff) |
[CPUFREQ] Longhaul - Separate frequency and voltage transition
This change should make Longhaul more compatible with
both ver. 2 and Powersaver processors. Voltage transitions
will be done before or after frequency transition. That depends
on direction of change. I don't know how to force conservative
governor when voltage scaling is enabled, so there is only
a warning for user. Minimal voltage is calculated in different
way now because in this way more power is saved at lower
multipliers.
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.c | 109 |
1 files changed, 93 insertions, 16 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index fc9afbb7c1fc..8f40cb47720e 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c | |||
@@ -65,7 +65,8 @@ static unsigned int fsb; | |||
65 | static struct mV_pos *vrm_mV_table; | 65 | static struct mV_pos *vrm_mV_table; |
66 | static unsigned char *mV_vrm_table; | 66 | static unsigned char *mV_vrm_table; |
67 | struct f_msr { | 67 | struct f_msr { |
68 | unsigned char vrm; | 68 | u8 vrm; |
69 | u8 pos; | ||
69 | }; | 70 | }; |
70 | static struct f_msr f_msr_table[32]; | 71 | static struct f_msr f_msr_table[32]; |
71 | 72 | ||
@@ -75,6 +76,7 @@ static int can_scale_voltage; | |||
75 | static struct acpi_processor *pr = NULL; | 76 | static struct acpi_processor *pr = NULL; |
76 | static struct acpi_processor_cx *cx = NULL; | 77 | static struct acpi_processor_cx *cx = NULL; |
77 | static u8 longhaul_flags; | 78 | static u8 longhaul_flags; |
79 | static u8 longhaul_pos; | ||
78 | 80 | ||
79 | /* Module parameters */ | 81 | /* Module parameters */ |
80 | static int scale_voltage; | 82 | static int scale_voltage; |
@@ -165,26 +167,47 @@ static void do_longhaul1(unsigned int clock_ratio_index) | |||
165 | static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | 167 | static void do_powersaver(int cx_address, unsigned int clock_ratio_index) |
166 | { | 168 | { |
167 | union msr_longhaul longhaul; | 169 | union msr_longhaul longhaul; |
170 | u8 dest_pos; | ||
168 | u32 t; | 171 | u32 t; |
169 | 172 | ||
173 | dest_pos = f_msr_table[clock_ratio_index].pos; | ||
174 | |||
170 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 175 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
176 | /* Setup new frequency */ | ||
171 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; | 177 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; |
172 | longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf; | 178 | longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf; |
173 | longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; | 179 | longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; |
174 | longhaul.bits.EnableSoftBusRatio = 1; | 180 | /* Setup new voltage */ |
175 | 181 | if (can_scale_voltage) | |
176 | if (can_scale_voltage) { | ||
177 | longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm; | 182 | longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm; |
183 | /* Sync to timer tick */ | ||
184 | safe_halt(); | ||
185 | /* Raise voltage if necessary */ | ||
186 | if (can_scale_voltage && longhaul_pos < dest_pos) { | ||
178 | longhaul.bits.EnableSoftVID = 1; | 187 | longhaul.bits.EnableSoftVID = 1; |
188 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | ||
189 | /* Change voltage */ | ||
190 | if (!cx_address) { | ||
191 | ACPI_FLUSH_CPU_CACHE(); | ||
192 | halt(); | ||
193 | } else { | ||
194 | ACPI_FLUSH_CPU_CACHE(); | ||
195 | /* Invoke C3 */ | ||
196 | inb(cx_address); | ||
197 | /* Dummy op - must do something useless after P_LVL3 | ||
198 | * read */ | ||
199 | t = inl(acpi_fadt.xpm_tmr_blk.address); | ||
200 | } | ||
201 | longhaul.bits.EnableSoftVID = 0; | ||
202 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | ||
203 | longhaul_pos = dest_pos; | ||
179 | } | 204 | } |
180 | 205 | ||
181 | /* Sync to timer tick */ | ||
182 | safe_halt(); | ||
183 | /* Change frequency on next halt or sleep */ | 206 | /* Change frequency on next halt or sleep */ |
207 | longhaul.bits.EnableSoftBusRatio = 1; | ||
184 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 208 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
185 | if (!cx_address) { | 209 | if (!cx_address) { |
186 | ACPI_FLUSH_CPU_CACHE(); | 210 | ACPI_FLUSH_CPU_CACHE(); |
187 | /* Invoke C1 */ | ||
188 | halt(); | 211 | halt(); |
189 | } else { | 212 | } else { |
190 | ACPI_FLUSH_CPU_CACHE(); | 213 | ACPI_FLUSH_CPU_CACHE(); |
@@ -194,12 +217,29 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) | |||
194 | t = inl(acpi_fadt.xpm_tmr_blk.address); | 217 | t = inl(acpi_fadt.xpm_tmr_blk.address); |
195 | } | 218 | } |
196 | /* Disable bus ratio bit */ | 219 | /* Disable bus ratio bit */ |
197 | local_irq_disable(); | ||
198 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; | ||
199 | longhaul.bits.EnableSoftBusRatio = 0; | 220 | longhaul.bits.EnableSoftBusRatio = 0; |
200 | longhaul.bits.EnableSoftBSEL = 0; | ||
201 | longhaul.bits.EnableSoftVID = 0; | ||
202 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 221 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
222 | |||
223 | /* Reduce voltage if necessary */ | ||
224 | if (can_scale_voltage && longhaul_pos > dest_pos) { | ||
225 | longhaul.bits.EnableSoftVID = 1; | ||
226 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | ||
227 | /* Change voltage */ | ||
228 | if (!cx_address) { | ||
229 | ACPI_FLUSH_CPU_CACHE(); | ||
230 | halt(); | ||
231 | } else { | ||
232 | ACPI_FLUSH_CPU_CACHE(); | ||
233 | /* Invoke C3 */ | ||
234 | inb(cx_address); | ||
235 | /* Dummy op - must do something useless after P_LVL3 | ||
236 | * read */ | ||
237 | t = inl(acpi_fadt.xpm_tmr_blk.address); | ||
238 | } | ||
239 | longhaul.bits.EnableSoftVID = 0; | ||
240 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | ||
241 | longhaul_pos = dest_pos; | ||
242 | } | ||
203 | } | 243 | } |
204 | 244 | ||
205 | /** | 245 | /** |
@@ -420,6 +460,7 @@ static void __init longhaul_setup_voltagescaling(void) | |||
420 | union msr_longhaul longhaul; | 460 | union msr_longhaul longhaul; |
421 | struct mV_pos minvid, maxvid; | 461 | struct mV_pos minvid, maxvid; |
422 | unsigned int j, speed, pos, kHz_step, numvscales; | 462 | unsigned int j, speed, pos, kHz_step, numvscales; |
463 | int min_vid_speed; | ||
423 | 464 | ||
424 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); | 465 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
425 | if (!(longhaul.bits.RevisionID & 1)) { | 466 | if (!(longhaul.bits.RevisionID & 1)) { |
@@ -439,8 +480,6 @@ static void __init longhaul_setup_voltagescaling(void) | |||
439 | 480 | ||
440 | minvid = vrm_mV_table[longhaul.bits.MinimumVID]; | 481 | minvid = vrm_mV_table[longhaul.bits.MinimumVID]; |
441 | maxvid = vrm_mV_table[longhaul.bits.MaximumVID]; | 482 | maxvid = vrm_mV_table[longhaul.bits.MaximumVID]; |
442 | numvscales = maxvid.pos - minvid.pos + 1; | ||
443 | kHz_step = (highest_speed - lowest_speed) / numvscales; | ||
444 | 483 | ||
445 | if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) { | 484 | if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) { |
446 | printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " | 485 | printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " |
@@ -456,20 +495,58 @@ static void __init longhaul_setup_voltagescaling(void) | |||
456 | return; | 495 | return; |
457 | } | 496 | } |
458 | 497 | ||
459 | printk(KERN_INFO PFX "Max VID=%d.%03d Min VID=%d.%03d, %d possible voltage scales\n", | 498 | /* How many voltage steps */ |
499 | numvscales = maxvid.pos - minvid.pos + 1; | ||
500 | printk(KERN_INFO PFX | ||
501 | "Max VID=%d.%03d " | ||
502 | "Min VID=%d.%03d, " | ||
503 | "%d possible voltage scales\n", | ||
460 | maxvid.mV/1000, maxvid.mV%1000, | 504 | maxvid.mV/1000, maxvid.mV%1000, |
461 | minvid.mV/1000, minvid.mV%1000, | 505 | minvid.mV/1000, minvid.mV%1000, |
462 | numvscales); | 506 | numvscales); |
463 | 507 | ||
508 | /* Calculate max frequency at min voltage */ | ||
509 | j = longhaul.bits.MinMHzBR; | ||
510 | if (longhaul.bits.MinMHzBR4) | ||
511 | j += 16; | ||
512 | min_vid_speed = eblcr_table[j]; | ||
513 | if (min_vid_speed == -1) | ||
514 | return; | ||
515 | switch (longhaul.bits.MinMHzFSB) { | ||
516 | case 0: | ||
517 | min_vid_speed *= 13333; | ||
518 | break; | ||
519 | case 1: | ||
520 | min_vid_speed *= 10000; | ||
521 | break; | ||
522 | case 3: | ||
523 | min_vid_speed *= 6666; | ||
524 | break; | ||
525 | default: | ||
526 | return; | ||
527 | break; | ||
528 | } | ||
529 | if (min_vid_speed >= highest_speed) | ||
530 | return; | ||
531 | /* Calculate kHz for one voltage step */ | ||
532 | kHz_step = (highest_speed - min_vid_speed) / numvscales; | ||
533 | |||
464 | j = 0; | 534 | j = 0; |
465 | while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { | 535 | while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { |
466 | speed = longhaul_table[j].frequency; | 536 | speed = longhaul_table[j].frequency; |
467 | pos = (speed - lowest_speed) / kHz_step + minvid.pos; | 537 | if (speed > min_vid_speed) |
538 | pos = (speed - min_vid_speed) / kHz_step + minvid.pos; | ||
539 | else | ||
540 | pos = minvid.pos; | ||
468 | f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos]; | 541 | f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos]; |
542 | f_msr_table[longhaul_table[j].index].pos = pos; | ||
469 | j++; | 543 | j++; |
470 | } | 544 | } |
471 | 545 | ||
546 | longhaul_pos = maxvid.pos; | ||
472 | can_scale_voltage = 1; | 547 | can_scale_voltage = 1; |
548 | printk(KERN_INFO PFX "Voltage scaling enabled. " | ||
549 | "Use of \"conservative\" governor is highly recommended.\n"); | ||
473 | } | 550 | } |
474 | 551 | ||
475 | 552 | ||