diff options
author | Rafał Bilski <rafalbilski@interia.pl> | 2006-07-03 01:19:05 -0400 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2006-07-31 18:37:05 -0400 |
commit | dadb49d8746bc4a4b5a310dabf0c838e57a9b531 (patch) | |
tree | 48d8cc5382aa37c9eb979e2e4b8a2b629e99ed2a /arch/i386 | |
parent | 9c9a43ed2734081124407c779b36a4761c41139b (diff) |
[CPUFREQ] Longhaul - Hook into ACPI C states.
Minimal change necessary for hardware support.
Changes in longhaul.c:
- most important - now C3 state is causing transition,
- code responsible for clearing "bus master" bit removed,
- protect bcr2 transition in the same way as longhaul.
Signed-off-by: Rafał 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/Kconfig | 2 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.c | 194 |
2 files changed, 109 insertions, 87 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index e44a4c6a4fe5..c593d73908f7 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig | |||
@@ -202,7 +202,7 @@ config X86_LONGRUN | |||
202 | config X86_LONGHAUL | 202 | config X86_LONGHAUL |
203 | tristate "VIA Cyrix III Longhaul" | 203 | tristate "VIA Cyrix III Longhaul" |
204 | select CPU_FREQ_TABLE | 204 | select CPU_FREQ_TABLE |
205 | depends on BROKEN | 205 | depends on ACPI_PROCESSOR |
206 | help | 206 | help |
207 | This adds the CPUFreq driver for VIA Samuel/CyrixIII, | 207 | This adds the CPUFreq driver for VIA Samuel/CyrixIII, |
208 | VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T | 208 | VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T |
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index 146f607e9c44..d735cb460612 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c | |||
@@ -29,11 +29,13 @@ | |||
29 | #include <linux/cpufreq.h> | 29 | #include <linux/cpufreq.h> |
30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
31 | #include <linux/string.h> | 31 | #include <linux/string.h> |
32 | #include <linux/pci.h> | ||
33 | 32 | ||
34 | #include <asm/msr.h> | 33 | #include <asm/msr.h> |
35 | #include <asm/timex.h> | 34 | #include <asm/timex.h> |
36 | #include <asm/io.h> | 35 | #include <asm/io.h> |
36 | #include <asm/acpi.h> | ||
37 | #include <linux/acpi.h> | ||
38 | #include <acpi/processor.h> | ||
37 | 39 | ||
38 | #include "longhaul.h" | 40 | #include "longhaul.h" |
39 | 41 | ||
@@ -56,6 +58,8 @@ static int minvid, maxvid; | |||
56 | static unsigned int minmult, maxmult; | 58 | static unsigned int minmult, maxmult; |
57 | static int can_scale_voltage; | 59 | static int can_scale_voltage; |
58 | static int vrmrev; | 60 | static int vrmrev; |
61 | static struct acpi_processor *pr = NULL; | ||
62 | static struct acpi_processor_cx *cx = NULL; | ||
59 | 63 | ||
60 | /* Module parameters */ | 64 | /* Module parameters */ |
61 | static int dont_scale_voltage; | 65 | static int dont_scale_voltage; |
@@ -118,84 +122,64 @@ static int longhaul_get_cpu_mult(void) | |||
118 | return eblcr_table[invalue]; | 122 | return eblcr_table[invalue]; |
119 | } | 123 | } |
120 | 124 | ||
125 | /* For processor with BCR2 MSR */ | ||
121 | 126 | ||
122 | static void do_powersaver(union msr_longhaul *longhaul, | 127 | static void do_longhaul1(int cx_address, unsigned int clock_ratio_index) |
123 | unsigned int clock_ratio_index) | ||
124 | { | 128 | { |
125 | struct pci_dev *dev; | 129 | union msr_bcr2 bcr2; |
126 | unsigned long flags; | 130 | u32 t; |
127 | unsigned int tmp_mask; | ||
128 | int version; | ||
129 | int i; | ||
130 | u16 pci_cmd; | ||
131 | u16 cmd_state[64]; | ||
132 | 131 | ||
133 | switch (cpu_model) { | 132 | rdmsrl(MSR_VIA_BCR2, bcr2.val); |
134 | case CPU_EZRA_T: | 133 | /* Enable software clock multiplier */ |
135 | version = 3; | 134 | bcr2.bits.ESOFTBF = 1; |
136 | break; | 135 | bcr2.bits.CLOCKMUL = clock_ratio_index; |
137 | case CPU_NEHEMIAH: | ||
138 | version = 0xf; | ||
139 | break; | ||
140 | default: | ||
141 | return; | ||
142 | } | ||
143 | 136 | ||
144 | rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); | 137 | /* Sync to timer tick */ |
145 | longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf; | 138 | safe_halt(); |
146 | longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; | 139 | ACPI_FLUSH_CPU_CACHE(); |
147 | longhaul->bits.EnableSoftBusRatio = 1; | 140 | /* Change frequency on next halt or sleep */ |
148 | longhaul->bits.RevisionKey = 0; | 141 | wrmsrl(MSR_VIA_BCR2, bcr2.val); |
142 | /* Invoke C3 */ | ||
143 | inb(cx_address); | ||
144 | /* Dummy op - must do something useless after P_LVL3 read */ | ||
145 | t = inl(acpi_fadt.xpm_tmr_blk.address); | ||
146 | |||
147 | /* Disable software clock multiplier */ | ||
148 | local_irq_disable(); | ||
149 | rdmsrl(MSR_VIA_BCR2, bcr2.val); | ||
150 | bcr2.bits.ESOFTBF = 0; | ||
151 | wrmsrl(MSR_VIA_BCR2, bcr2.val); | ||
152 | } | ||
149 | 153 | ||
150 | preempt_disable(); | 154 | /* For processor with Longhaul MSR */ |
151 | local_irq_save(flags); | ||
152 | 155 | ||
153 | /* | 156 | static void do_powersaver(int cx_address, unsigned int clock_ratio_index) |
154 | * get current pci bus master state for all devices | 157 | { |
155 | * and clear bus master bit | 158 | union msr_longhaul longhaul; |
156 | */ | 159 | u32 t; |
157 | dev = NULL; | ||
158 | i = 0; | ||
159 | do { | ||
160 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); | ||
161 | if (dev != NULL) { | ||
162 | pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); | ||
163 | cmd_state[i++] = pci_cmd; | ||
164 | pci_cmd &= ~PCI_COMMAND_MASTER; | ||
165 | pci_write_config_word(dev, PCI_COMMAND, pci_cmd); | ||
166 | } | ||
167 | } while (dev != NULL); | ||
168 | 160 | ||
169 | tmp_mask=inb(0x21); /* works on C3. save mask. */ | 161 | rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
170 | outb(0xFE,0x21); /* TMR0 only */ | 162 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; |
171 | outb(0xFF,0x80); /* delay */ | 163 | longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf; |
164 | longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; | ||
172 | 165 | ||
166 | /* Sync to timer tick */ | ||
173 | safe_halt(); | 167 | safe_halt(); |
174 | wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); | 168 | ACPI_FLUSH_CPU_CACHE(); |
175 | halt(); | 169 | /* Change frequency on next halt or sleep */ |
176 | 170 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | |
171 | /* Invoke C3 */ | ||
172 | inb(cx_address); | ||
173 | /* Dummy op - must do something useless after P_LVL3 read */ | ||
174 | t = inl(acpi_fadt.xpm_tmr_blk.address); | ||
175 | |||
176 | /* Disable bus ratio bit */ | ||
177 | local_irq_disable(); | 177 | local_irq_disable(); |
178 | 178 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; | |
179 | outb(tmp_mask,0x21); /* restore mask */ | 179 | longhaul.bits.EnableSoftBusRatio = 0; |
180 | 180 | longhaul.bits.EnableSoftBSEL = 0; | |
181 | /* restore pci bus master state for all devices */ | 181 | longhaul.bits.EnableSoftVID = 0; |
182 | dev = NULL; | 182 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); |
183 | i = 0; | ||
184 | do { | ||
185 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); | ||
186 | if (dev != NULL) { | ||
187 | pci_cmd = cmd_state[i++]; | ||
188 | pci_write_config_byte(dev, PCI_COMMAND, pci_cmd); | ||
189 | } | ||
190 | } while (dev != NULL); | ||
191 | local_irq_restore(flags); | ||
192 | preempt_enable(); | ||
193 | |||
194 | /* disable bus ratio bit */ | ||
195 | rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
196 | longhaul->bits.EnableSoftBusRatio = 0; | ||
197 | longhaul->bits.RevisionKey = version; | ||
198 | wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); | ||
199 | } | 183 | } |
200 | 184 | ||
201 | /** | 185 | /** |
@@ -209,9 +193,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
209 | { | 193 | { |
210 | int speed, mult; | 194 | int speed, mult; |
211 | struct cpufreq_freqs freqs; | 195 | struct cpufreq_freqs freqs; |
212 | union msr_longhaul longhaul; | ||
213 | union msr_bcr2 bcr2; | ||
214 | static unsigned int old_ratio=-1; | 196 | static unsigned int old_ratio=-1; |
197 | unsigned long flags; | ||
198 | unsigned int pic1_mask, pic2_mask; | ||
215 | 199 | ||
216 | if (old_ratio == clock_ratio_index) | 200 | if (old_ratio == clock_ratio_index) |
217 | return; | 201 | return; |
@@ -234,6 +218,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
234 | dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", | 218 | dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", |
235 | fsb, mult/10, mult%10, print_speed(speed/1000)); | 219 | fsb, mult/10, mult%10, print_speed(speed/1000)); |
236 | 220 | ||
221 | preempt_disable(); | ||
222 | local_irq_save(flags); | ||
223 | |||
224 | pic2_mask = inb(0xA1); | ||
225 | pic1_mask = inb(0x21); /* works on C3. save mask. */ | ||
226 | outb(0xFF,0xA1); /* Overkill */ | ||
227 | outb(0xFE,0x21); /* TMR0 only */ | ||
228 | |||
229 | /* Disable bus master arbitration */ | ||
230 | if (pr->flags.bm_check) { | ||
231 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1, | ||
232 | ACPI_MTX_DO_NOT_LOCK); | ||
233 | } | ||
234 | |||
237 | switch (longhaul_version) { | 235 | switch (longhaul_version) { |
238 | 236 | ||
239 | /* | 237 | /* |
@@ -245,20 +243,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
245 | */ | 243 | */ |
246 | case TYPE_LONGHAUL_V1: | 244 | case TYPE_LONGHAUL_V1: |
247 | case TYPE_LONGHAUL_V2: | 245 | case TYPE_LONGHAUL_V2: |
248 | rdmsrl (MSR_VIA_BCR2, bcr2.val); | 246 | do_longhaul1(cx->address, clock_ratio_index); |
249 | /* Enable software clock multiplier */ | ||
250 | bcr2.bits.ESOFTBF = 1; | ||
251 | bcr2.bits.CLOCKMUL = clock_ratio_index; | ||
252 | local_irq_disable(); | ||
253 | wrmsrl (MSR_VIA_BCR2, bcr2.val); | ||
254 | safe_halt(); | ||
255 | |||
256 | /* Disable software clock multiplier */ | ||
257 | rdmsrl (MSR_VIA_BCR2, bcr2.val); | ||
258 | bcr2.bits.ESOFTBF = 0; | ||
259 | local_irq_disable(); | ||
260 | wrmsrl (MSR_VIA_BCR2, bcr2.val); | ||
261 | local_irq_enable(); | ||
262 | break; | 247 | break; |
263 | 248 | ||
264 | /* | 249 | /* |
@@ -273,10 +258,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
273 | * to work in practice. | 258 | * to work in practice. |
274 | */ | 259 | */ |
275 | case TYPE_POWERSAVER: | 260 | case TYPE_POWERSAVER: |
276 | do_powersaver(&longhaul, clock_ratio_index); | 261 | do_powersaver(cx->address, clock_ratio_index); |
277 | break; | 262 | break; |
278 | } | 263 | } |
279 | 264 | ||
265 | /* Enable bus master arbitration */ | ||
266 | if (pr->flags.bm_check) { | ||
267 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0, | ||
268 | ACPI_MTX_DO_NOT_LOCK); | ||
269 | } | ||
270 | |||
271 | outb(pic2_mask,0xA1); /* restore mask */ | ||
272 | outb(pic1_mask,0x21); | ||
273 | |||
274 | local_irq_restore(flags); | ||
275 | preempt_enable(); | ||
276 | |||
280 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 277 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
281 | } | 278 | } |
282 | 279 | ||
@@ -527,6 +524,18 @@ static unsigned int longhaul_get(unsigned int cpu) | |||
527 | return calc_speed(longhaul_get_cpu_mult()); | 524 | return calc_speed(longhaul_get_cpu_mult()); |
528 | } | 525 | } |
529 | 526 | ||
527 | acpi_status longhaul_walk_callback(acpi_handle obj_handle, | ||
528 | u32 nesting_level, | ||
529 | void *context, void **return_value) | ||
530 | { | ||
531 | struct acpi_device *d; | ||
532 | |||
533 | if ( acpi_bus_get_device(obj_handle, &d) ) { | ||
534 | return 0; | ||
535 | } | ||
536 | *return_value = (void *)acpi_driver_data(d); | ||
537 | return 1; | ||
538 | } | ||
530 | 539 | ||
531 | static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | 540 | static int __init longhaul_cpu_init(struct cpufreq_policy *policy) |
532 | { | 541 | { |
@@ -534,6 +543,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | |||
534 | char *cpuname=NULL; | 543 | char *cpuname=NULL; |
535 | int ret; | 544 | int ret; |
536 | 545 | ||
546 | /* Check ACPI support for C3 state */ | ||
547 | acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, | ||
548 | &longhaul_walk_callback, NULL, (void *)&pr); | ||
549 | if (pr == NULL) goto err_acpi; | ||
550 | |||
551 | cx = &pr->power.states[ACPI_STATE_C3]; | ||
552 | if (cx == NULL || cx->latency > 1000) goto err_acpi; | ||
553 | |||
554 | /* Now check what we have on this motherboard */ | ||
537 | switch (c->x86_model) { | 555 | switch (c->x86_model) { |
538 | case 6: | 556 | case 6: |
539 | cpu_model = CPU_SAMUEL; | 557 | cpu_model = CPU_SAMUEL; |
@@ -634,6 +652,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | |||
634 | cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); | 652 | cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); |
635 | 653 | ||
636 | return 0; | 654 | return 0; |
655 | |||
656 | err_acpi: | ||
657 | printk(KERN_ERR PFX "No ACPI support for CPU frequency changes.\n"); | ||
658 | return -ENODEV; | ||
637 | } | 659 | } |
638 | 660 | ||
639 | static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) | 661 | static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) |