diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-08-04 12:41:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-08-04 12:41:22 -0400 |
commit | 12952784e5271374b7cb926501f51b9649bf5a2b (patch) | |
tree | 7e6e16d08df7553f30b345a65d46fdc8b4737a38 | |
parent | efe78cda3596f8a6d1c2d4a6b1a221bafa3e1a48 (diff) | |
parent | 12e704db809cd4101b7d3594fc9a96f30fe88a31 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
* master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq:
[CPUFREQ] Propagate acpi_processor_preregister_performance return value.
[CPUFREQ] [2/2] demand load governor modules.
[CPUFREQ] [1/2] add __find_governor helper and clean up some error handling.
[CPUFREQ] Longhaul - Rename & fix multipliers table
[CPUFREQ] Longhaul - Fix power state test to do something more useful
[CPUFREQ] Longhaul - Readd accidentally dropped line
[CPUFREQ] Make longhaul_walk_callback() static
[CPUFREQ] X86_GX_SUSPMOD must depend on PCI
[CPUFREQ] Longhaul - Initialise later.
[CPUFREQ] Longhaul - Workaround issues with APIC.
[CPUFREQ] Longhaul - Hook into ACPI C states.
[CPUFREQ] return error when failing to set minfreq
-rw-r--r-- | Documentation/cpu-freq/user-guide.txt | 5 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/Kconfig | 3 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 3 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.c | 221 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 75 |
5 files changed, 190 insertions, 117 deletions
diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index 7fedc00c3d30..555c8cf3650a 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt | |||
@@ -153,10 +153,13 @@ scaling_governor, and by "echoing" the name of another | |||
153 | that some governors won't load - they only | 153 | that some governors won't load - they only |
154 | work on some specific architectures or | 154 | work on some specific architectures or |
155 | processors. | 155 | processors. |
156 | scaling_min_freq and | 156 | scaling_min_freq and |
157 | scaling_max_freq show the current "policy limits" (in | 157 | scaling_max_freq show the current "policy limits" (in |
158 | kHz). By echoing new values into these | 158 | kHz). By echoing new values into these |
159 | files, you can change these limits. | 159 | files, you can change these limits. |
160 | NOTE: when setting a policy you need to | ||
161 | first set scaling_max_freq, then | ||
162 | scaling_min_freq. | ||
160 | 163 | ||
161 | 164 | ||
162 | If you have selected the "userspace" governor which allows you to | 165 | If you have selected the "userspace" governor which allows you to |
diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index e44a4c6a4fe5..ccc1edff5c97 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig | |||
@@ -96,6 +96,7 @@ config X86_POWERNOW_K8_ACPI | |||
96 | 96 | ||
97 | config X86_GX_SUSPMOD | 97 | config X86_GX_SUSPMOD |
98 | tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation" | 98 | tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation" |
99 | depends on PCI | ||
99 | help | 100 | help |
100 | This add the CPUFreq driver for NatSemi Geode processors which | 101 | This add the CPUFreq driver for NatSemi Geode processors which |
101 | support suspend modulation. | 102 | support suspend modulation. |
@@ -202,7 +203,7 @@ config X86_LONGRUN | |||
202 | config X86_LONGHAUL | 203 | config X86_LONGHAUL |
203 | tristate "VIA Cyrix III Longhaul" | 204 | tristate "VIA Cyrix III Longhaul" |
204 | select CPU_FREQ_TABLE | 205 | select CPU_FREQ_TABLE |
205 | depends on BROKEN | 206 | depends on ACPI_PROCESSOR |
206 | help | 207 | help |
207 | This adds the CPUFreq driver for VIA Samuel/CyrixIII, | 208 | This adds the CPUFreq driver for VIA Samuel/CyrixIII, |
208 | VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T | 209 | VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T |
diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 567b39bea07e..efb41e81351c 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | |||
@@ -384,8 +384,7 @@ static int acpi_cpufreq_early_init_acpi(void) | |||
384 | } | 384 | } |
385 | 385 | ||
386 | /* Do initialization in ACPI core */ | 386 | /* Do initialization in ACPI core */ |
387 | acpi_processor_preregister_performance(acpi_perf_data); | 387 | return acpi_processor_preregister_performance(acpi_perf_data); |
388 | return 0; | ||
389 | } | 388 | } |
390 | 389 | ||
391 | static int | 390 | static int |
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index 146f607e9c44..4f2c3aeef724 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,65 @@ 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; | ||
165 | longhaul.bits.EnableSoftBusRatio = 1; | ||
172 | 166 | ||
167 | /* Sync to timer tick */ | ||
173 | safe_halt(); | 168 | safe_halt(); |
174 | wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); | 169 | ACPI_FLUSH_CPU_CACHE(); |
175 | halt(); | 170 | /* Change frequency on next halt or sleep */ |
176 | 171 | wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); | |
172 | /* Invoke C3 */ | ||
173 | inb(cx_address); | ||
174 | /* Dummy op - must do something useless after P_LVL3 read */ | ||
175 | t = inl(acpi_fadt.xpm_tmr_blk.address); | ||
176 | |||
177 | /* Disable bus ratio bit */ | ||
177 | local_irq_disable(); | 178 | local_irq_disable(); |
178 | 179 | longhaul.bits.RevisionKey = longhaul.bits.RevisionID; | |
179 | outb(tmp_mask,0x21); /* restore mask */ | 180 | longhaul.bits.EnableSoftBusRatio = 0; |
180 | 181 | longhaul.bits.EnableSoftBSEL = 0; | |
181 | /* restore pci bus master state for all devices */ | 182 | longhaul.bits.EnableSoftVID = 0; |
182 | dev = NULL; | 183 | 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 | } | 184 | } |
200 | 185 | ||
201 | /** | 186 | /** |
@@ -209,9 +194,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
209 | { | 194 | { |
210 | int speed, mult; | 195 | int speed, mult; |
211 | struct cpufreq_freqs freqs; | 196 | struct cpufreq_freqs freqs; |
212 | union msr_longhaul longhaul; | ||
213 | union msr_bcr2 bcr2; | ||
214 | static unsigned int old_ratio=-1; | 197 | static unsigned int old_ratio=-1; |
198 | unsigned long flags; | ||
199 | unsigned int pic1_mask, pic2_mask; | ||
215 | 200 | ||
216 | if (old_ratio == clock_ratio_index) | 201 | if (old_ratio == clock_ratio_index) |
217 | return; | 202 | return; |
@@ -234,6 +219,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
234 | dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", | 219 | dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", |
235 | fsb, mult/10, mult%10, print_speed(speed/1000)); | 220 | fsb, mult/10, mult%10, print_speed(speed/1000)); |
236 | 221 | ||
222 | preempt_disable(); | ||
223 | local_irq_save(flags); | ||
224 | |||
225 | pic2_mask = inb(0xA1); | ||
226 | pic1_mask = inb(0x21); /* works on C3. save mask. */ | ||
227 | outb(0xFF,0xA1); /* Overkill */ | ||
228 | outb(0xFE,0x21); /* TMR0 only */ | ||
229 | |||
230 | /* Disable bus master arbitration */ | ||
231 | if (pr->flags.bm_check) { | ||
232 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1, | ||
233 | ACPI_MTX_DO_NOT_LOCK); | ||
234 | } | ||
235 | |||
237 | switch (longhaul_version) { | 236 | switch (longhaul_version) { |
238 | 237 | ||
239 | /* | 238 | /* |
@@ -245,20 +244,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
245 | */ | 244 | */ |
246 | case TYPE_LONGHAUL_V1: | 245 | case TYPE_LONGHAUL_V1: |
247 | case TYPE_LONGHAUL_V2: | 246 | case TYPE_LONGHAUL_V2: |
248 | rdmsrl (MSR_VIA_BCR2, bcr2.val); | 247 | 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; | 248 | break; |
263 | 249 | ||
264 | /* | 250 | /* |
@@ -273,10 +259,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index) | |||
273 | * to work in practice. | 259 | * to work in practice. |
274 | */ | 260 | */ |
275 | case TYPE_POWERSAVER: | 261 | case TYPE_POWERSAVER: |
276 | do_powersaver(&longhaul, clock_ratio_index); | 262 | do_powersaver(cx->address, clock_ratio_index); |
277 | break; | 263 | break; |
278 | } | 264 | } |
279 | 265 | ||
266 | /* Enable bus master arbitration */ | ||
267 | if (pr->flags.bm_check) { | ||
268 | acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0, | ||
269 | ACPI_MTX_DO_NOT_LOCK); | ||
270 | } | ||
271 | |||
272 | outb(pic2_mask,0xA1); /* restore mask */ | ||
273 | outb(pic1_mask,0x21); | ||
274 | |||
275 | local_irq_restore(flags); | ||
276 | preempt_enable(); | ||
277 | |||
280 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 278 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
281 | } | 279 | } |
282 | 280 | ||
@@ -324,9 +322,11 @@ static int guess_fsb(void) | |||
324 | static int __init longhaul_get_ranges(void) | 322 | static int __init longhaul_get_ranges(void) |
325 | { | 323 | { |
326 | unsigned long invalue; | 324 | unsigned long invalue; |
327 | unsigned int multipliers[32]= { | 325 | unsigned int ezra_t_multipliers[32]= { |
328 | 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, | 326 | 90, 30, 40, 100, 55, 35, 45, 95, |
329 | -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; | 327 | 50, 70, 80, 60, 120, 75, 85, 65, |
328 | -1, 110, 120, -1, 135, 115, 125, 105, | ||
329 | 130, 150, 160, 140, -1, 155, -1, 145 }; | ||
330 | unsigned int j, k = 0; | 330 | unsigned int j, k = 0; |
331 | union msr_longhaul longhaul; | 331 | union msr_longhaul longhaul; |
332 | unsigned long lo, hi; | 332 | unsigned long lo, hi; |
@@ -355,13 +355,13 @@ static int __init longhaul_get_ranges(void) | |||
355 | invalue = longhaul.bits.MaxMHzBR; | 355 | invalue = longhaul.bits.MaxMHzBR; |
356 | if (longhaul.bits.MaxMHzBR4) | 356 | if (longhaul.bits.MaxMHzBR4) |
357 | invalue += 16; | 357 | invalue += 16; |
358 | maxmult=multipliers[invalue]; | 358 | maxmult=ezra_t_multipliers[invalue]; |
359 | 359 | ||
360 | invalue = longhaul.bits.MinMHzBR; | 360 | invalue = longhaul.bits.MinMHzBR; |
361 | if (longhaul.bits.MinMHzBR4 == 1) | 361 | if (longhaul.bits.MinMHzBR4 == 1) |
362 | minmult = 30; | 362 | minmult = 30; |
363 | else | 363 | else |
364 | minmult = multipliers[invalue]; | 364 | minmult = ezra_t_multipliers[invalue]; |
365 | fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; | 365 | fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; |
366 | break; | 366 | break; |
367 | } | 367 | } |
@@ -527,6 +527,18 @@ static unsigned int longhaul_get(unsigned int cpu) | |||
527 | return calc_speed(longhaul_get_cpu_mult()); | 527 | return calc_speed(longhaul_get_cpu_mult()); |
528 | } | 528 | } |
529 | 529 | ||
530 | static acpi_status longhaul_walk_callback(acpi_handle obj_handle, | ||
531 | u32 nesting_level, | ||
532 | void *context, void **return_value) | ||
533 | { | ||
534 | struct acpi_device *d; | ||
535 | |||
536 | if ( acpi_bus_get_device(obj_handle, &d) ) { | ||
537 | return 0; | ||
538 | } | ||
539 | *return_value = (void *)acpi_driver_data(d); | ||
540 | return 1; | ||
541 | } | ||
530 | 542 | ||
531 | static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | 543 | static int __init longhaul_cpu_init(struct cpufreq_policy *policy) |
532 | { | 544 | { |
@@ -534,6 +546,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | |||
534 | char *cpuname=NULL; | 546 | char *cpuname=NULL; |
535 | int ret; | 547 | int ret; |
536 | 548 | ||
549 | /* Check ACPI support for C3 state */ | ||
550 | acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, | ||
551 | &longhaul_walk_callback, NULL, (void *)&pr); | ||
552 | if (pr == NULL) goto err_acpi; | ||
553 | |||
554 | cx = &pr->power.states[ACPI_STATE_C3]; | ||
555 | if (cx->address == 0 || cx->latency > 1000) goto err_acpi; | ||
556 | |||
557 | /* Now check what we have on this motherboard */ | ||
537 | switch (c->x86_model) { | 558 | switch (c->x86_model) { |
538 | case 6: | 559 | case 6: |
539 | cpu_model = CPU_SAMUEL; | 560 | cpu_model = CPU_SAMUEL; |
@@ -634,6 +655,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) | |||
634 | cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); | 655 | cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); |
635 | 656 | ||
636 | return 0; | 657 | return 0; |
658 | |||
659 | err_acpi: | ||
660 | printk(KERN_ERR PFX "No ACPI support for CPU frequency changes.\n"); | ||
661 | return -ENODEV; | ||
637 | } | 662 | } |
638 | 663 | ||
639 | static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) | 664 | static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) |
@@ -666,6 +691,18 @@ static int __init longhaul_init(void) | |||
666 | if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) | 691 | if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) |
667 | return -ENODEV; | 692 | return -ENODEV; |
668 | 693 | ||
694 | #ifdef CONFIG_SMP | ||
695 | if (num_online_cpus() > 1) { | ||
696 | return -ENODEV; | ||
697 | printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n"); | ||
698 | } | ||
699 | #endif | ||
700 | #ifdef CONFIG_X86_IO_APIC | ||
701 | if (cpu_has_apic) { | ||
702 | printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n"); | ||
703 | return -ENODEV; | ||
704 | } | ||
705 | #endif | ||
669 | switch (c->x86_model) { | 706 | switch (c->x86_model) { |
670 | case 6 ... 9: | 707 | case 6 ... 9: |
671 | return cpufreq_register_driver(&longhaul_driver); | 708 | return cpufreq_register_driver(&longhaul_driver); |
@@ -699,6 +736,6 @@ MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>"); | |||
699 | MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); | 736 | MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); |
700 | MODULE_LICENSE ("GPL"); | 737 | MODULE_LICENSE ("GPL"); |
701 | 738 | ||
702 | module_init(longhaul_init); | 739 | late_initcall(longhaul_init); |
703 | module_exit(longhaul_exit); | 740 | module_exit(longhaul_exit); |
704 | 741 | ||
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index bc1088d9b379..b3df613ae4ec 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -284,39 +284,69 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition); | |||
284 | * SYSFS INTERFACE * | 284 | * SYSFS INTERFACE * |
285 | *********************************************************************/ | 285 | *********************************************************************/ |
286 | 286 | ||
287 | static struct cpufreq_governor *__find_governor(const char *str_governor) | ||
288 | { | ||
289 | struct cpufreq_governor *t; | ||
290 | |||
291 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) | ||
292 | if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) | ||
293 | return t; | ||
294 | |||
295 | return NULL; | ||
296 | } | ||
297 | |||
287 | /** | 298 | /** |
288 | * cpufreq_parse_governor - parse a governor string | 299 | * cpufreq_parse_governor - parse a governor string |
289 | */ | 300 | */ |
290 | static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, | 301 | static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, |
291 | struct cpufreq_governor **governor) | 302 | struct cpufreq_governor **governor) |
292 | { | 303 | { |
304 | int err = -EINVAL; | ||
305 | |||
293 | if (!cpufreq_driver) | 306 | if (!cpufreq_driver) |
294 | return -EINVAL; | 307 | goto out; |
308 | |||
295 | if (cpufreq_driver->setpolicy) { | 309 | if (cpufreq_driver->setpolicy) { |
296 | if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { | 310 | if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { |
297 | *policy = CPUFREQ_POLICY_PERFORMANCE; | 311 | *policy = CPUFREQ_POLICY_PERFORMANCE; |
298 | return 0; | 312 | err = 0; |
299 | } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { | 313 | } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { |
300 | *policy = CPUFREQ_POLICY_POWERSAVE; | 314 | *policy = CPUFREQ_POLICY_POWERSAVE; |
301 | return 0; | 315 | err = 0; |
302 | } | 316 | } |
303 | return -EINVAL; | 317 | } else if (cpufreq_driver->target) { |
304 | } else { | ||
305 | struct cpufreq_governor *t; | 318 | struct cpufreq_governor *t; |
319 | |||
306 | mutex_lock(&cpufreq_governor_mutex); | 320 | mutex_lock(&cpufreq_governor_mutex); |
307 | if (!cpufreq_driver || !cpufreq_driver->target) | 321 | |
308 | goto out; | 322 | t = __find_governor(str_governor); |
309 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) { | 323 | |
310 | if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { | 324 | if (t == NULL) { |
311 | *governor = t; | 325 | char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", str_governor); |
326 | |||
327 | if (name) { | ||
328 | int ret; | ||
329 | |||
312 | mutex_unlock(&cpufreq_governor_mutex); | 330 | mutex_unlock(&cpufreq_governor_mutex); |
313 | return 0; | 331 | ret = request_module(name); |
332 | mutex_lock(&cpufreq_governor_mutex); | ||
333 | |||
334 | if (ret == 0) | ||
335 | t = __find_governor(str_governor); | ||
314 | } | 336 | } |
337 | |||
338 | kfree(name); | ||
315 | } | 339 | } |
316 | out: | 340 | |
341 | if (t != NULL) { | ||
342 | *governor = t; | ||
343 | err = 0; | ||
344 | } | ||
345 | |||
317 | mutex_unlock(&cpufreq_governor_mutex); | 346 | mutex_unlock(&cpufreq_governor_mutex); |
318 | } | 347 | } |
319 | return -EINVAL; | 348 | out: |
349 | return err; | ||
320 | } | 350 | } |
321 | 351 | ||
322 | 352 | ||
@@ -1265,23 +1295,21 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) | |||
1265 | 1295 | ||
1266 | int cpufreq_register_governor(struct cpufreq_governor *governor) | 1296 | int cpufreq_register_governor(struct cpufreq_governor *governor) |
1267 | { | 1297 | { |
1268 | struct cpufreq_governor *t; | 1298 | int err; |
1269 | 1299 | ||
1270 | if (!governor) | 1300 | if (!governor) |
1271 | return -EINVAL; | 1301 | return -EINVAL; |
1272 | 1302 | ||
1273 | mutex_lock(&cpufreq_governor_mutex); | 1303 | mutex_lock(&cpufreq_governor_mutex); |
1274 | 1304 | ||
1275 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) { | 1305 | err = -EBUSY; |
1276 | if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { | 1306 | if (__find_governor(governor->name) == NULL) { |
1277 | mutex_unlock(&cpufreq_governor_mutex); | 1307 | err = 0; |
1278 | return -EBUSY; | 1308 | list_add(&governor->governor_list, &cpufreq_governor_list); |
1279 | } | ||
1280 | } | 1309 | } |
1281 | list_add(&governor->governor_list, &cpufreq_governor_list); | ||
1282 | 1310 | ||
1283 | mutex_unlock(&cpufreq_governor_mutex); | 1311 | mutex_unlock(&cpufreq_governor_mutex); |
1284 | return 0; | 1312 | return err; |
1285 | } | 1313 | } |
1286 | EXPORT_SYMBOL_GPL(cpufreq_register_governor); | 1314 | EXPORT_SYMBOL_GPL(cpufreq_register_governor); |
1287 | 1315 | ||
@@ -1343,6 +1371,11 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli | |||
1343 | 1371 | ||
1344 | memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); | 1372 | memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); |
1345 | 1373 | ||
1374 | if (policy->min > data->min && policy->min > policy->max) { | ||
1375 | ret = -EINVAL; | ||
1376 | goto error_out; | ||
1377 | } | ||
1378 | |||
1346 | /* verify the cpu speed can be set within this limit */ | 1379 | /* verify the cpu speed can be set within this limit */ |
1347 | ret = cpufreq_driver->verify(policy); | 1380 | ret = cpufreq_driver->verify(policy); |
1348 | if (ret) | 1381 | if (ret) |