patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / i386 / kernel / cpu / cpufreq / speedstep-ich.c
1 /*
2  * (C) 2001  Dave Jones, Arjan van de ven.
3  * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
4  *
5  *  Licensed under the terms of the GNU GPL License version 2.
6  *  Based upon reverse engineered information, and on Intel documentation
7  *  for chipsets ICH2-M and ICH3-M.
8  *
9  *  Many thanks to Ducrot Bruno for finding and fixing the last
10  *  "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler 
11  *  for extensive testing.
12  *
13  *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
14  */
15
16
17 /*********************************************************************
18  *                        SPEEDSTEP - DEFINITIONS                    *
19  *********************************************************************/
20
21 #include <linux/kernel.h>
22 #include <linux/module.h> 
23 #include <linux/init.h>
24 #include <linux/cpufreq.h>
25 #include <linux/pci.h>
26 #include <linux/slab.h>
27
28 #include "speedstep-lib.h"
29
30
31 /* speedstep_chipset:
32  *   It is necessary to know which chipset is used. As accesses to 
33  * this device occur at various places in this module, we need a 
34  * static struct pci_dev * pointing to that device.
35  */
36 static struct pci_dev                   *speedstep_chipset_dev;
37
38
39 /* speedstep_processor
40  */
41 static unsigned int                     speedstep_processor = 0;
42
43
44 /* 
45  *   There are only two frequency states for each processor. Values
46  * are in kHz for the time being.
47  */
48 static struct cpufreq_frequency_table speedstep_freqs[] = {
49         {SPEEDSTEP_HIGH,        0},
50         {SPEEDSTEP_LOW,         0},
51         {0,                     CPUFREQ_TABLE_END},
52 };
53
54
55 /* DEBUG
56  *   Define it if you want verbose debug output, e.g. for bug reporting
57  */
58 //#define SPEEDSTEP_DEBUG
59
60 #ifdef SPEEDSTEP_DEBUG
61 #define dprintk(msg...) printk(msg)
62 #else
63 #define dprintk(msg...) do { } while(0)
64 #endif
65
66
67 /**
68  * speedstep_set_state - set the SpeedStep state
69  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
70  *
71  *   Tries to change the SpeedStep state. 
72  */
73 static void speedstep_set_state (unsigned int state)
74 {
75         u32                     pmbase;
76         u8                      pm2_blk;
77         u8                      value;
78         unsigned long           flags;
79
80         if (!speedstep_chipset_dev || (state > 0x1))
81                 return;
82
83         /* get PMBASE */
84         pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
85         if (!(pmbase & 0x01))
86         {
87                 printk(KERN_ERR "cpufreq: could not find speedstep register\n");
88                 return;
89         }
90
91         pmbase &= 0xFFFFFFFE;
92         if (!pmbase) {
93                 printk(KERN_ERR "cpufreq: could not find speedstep register\n");
94                 return;
95         }
96
97         /* Disable IRQs */
98         local_irq_save(flags);
99
100         /* read state */
101         value = inb(pmbase + 0x50);
102
103         dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
104
105         /* write new state */
106         value &= 0xFE;
107         value |= state;
108
109         dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
110
111         /* Disable bus master arbitration */
112         pm2_blk = inb(pmbase + 0x20);
113         pm2_blk |= 0x01;
114         outb(pm2_blk, (pmbase + 0x20));
115
116         /* Actual transition */
117         outb(value, (pmbase + 0x50));
118
119         /* Restore bus master arbitration */
120         pm2_blk &= 0xfe;
121         outb(pm2_blk, (pmbase + 0x20));
122
123         /* check if transition was successful */
124         value = inb(pmbase + 0x50);
125
126         /* Enable IRQs */
127         local_irq_restore(flags);
128
129         dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
130
131         if (state == (value & 0x1)) {
132                 dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
133         } else {
134                 printk (KERN_ERR "cpufreq: change failed - I/O error\n");
135         }
136
137         return;
138 }
139
140
141 /**
142  * speedstep_activate - activate SpeedStep control in the chipset
143  *
144  *   Tries to activate the SpeedStep status and control registers.
145  * Returns -EINVAL on an unsupported chipset, and zero on success.
146  */
147 static int speedstep_activate (void)
148 {
149         u16             value = 0;
150
151         if (!speedstep_chipset_dev)
152                 return -EINVAL;
153
154         pci_read_config_word(speedstep_chipset_dev, 
155                              0x00A0, &value);
156         if (!(value & 0x08)) {
157                 value |= 0x08;
158                 dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n");
159                 pci_write_config_word(speedstep_chipset_dev, 
160                                       0x00A0, value);
161         }
162
163         return 0;
164 }
165
166
167 /**
168  * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
169  *
170  *   Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to 
171  * the LPC bridge / PM module which contains all power-management 
172  * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
173  * chipset, or zero on failure.
174  */
175 static unsigned int speedstep_detect_chipset (void)
176 {
177         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
178                               PCI_DEVICE_ID_INTEL_82801DB_12, 
179                               PCI_ANY_ID,
180                               PCI_ANY_ID,
181                               NULL);
182         if (speedstep_chipset_dev)
183                 return 4; /* 4-M */
184
185         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
186                               PCI_DEVICE_ID_INTEL_82801CA_12, 
187                               PCI_ANY_ID,
188                               PCI_ANY_ID,
189                               NULL);
190         if (speedstep_chipset_dev)
191                 return 3; /* 3-M */
192
193
194         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
195                               PCI_DEVICE_ID_INTEL_82801BA_10,
196                               PCI_ANY_ID,
197                               PCI_ANY_ID,
198                               NULL);
199         if (speedstep_chipset_dev) {
200                 /* speedstep.c causes lockups on Dell Inspirons 8000 and
201                  * 8100 which use a pretty old revision of the 82815 
202                  * host brige. Abort on these systems.
203                  */
204                 static struct pci_dev   *hostbridge;
205                 u8                      rev = 0;
206
207                 hostbridge  = pci_find_subsys(PCI_VENDOR_ID_INTEL,
208                               PCI_DEVICE_ID_INTEL_82815_MC,
209                               PCI_ANY_ID,
210                               PCI_ANY_ID,
211                               NULL);
212
213                 if (!hostbridge)
214                         return 2; /* 2-M */
215                         
216                 pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev);
217                 if (rev < 5) {
218                         dprintk(KERN_INFO "cpufreq: hostbridge does not support speedstep\n");
219                         speedstep_chipset_dev = NULL;
220                         return 0;
221                 }
222
223                 return 2; /* 2-M */
224         }
225
226         return 0;
227 }
228
229
230 /**
231  * speedstep_target - set a new CPUFreq policy
232  * @policy: new policy
233  * @target_freq: the target frequency
234  * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
235  *
236  * Sets a new CPUFreq policy.
237  */
238 static int speedstep_target (struct cpufreq_policy *policy,
239                              unsigned int target_freq,
240                              unsigned int relation)
241 {
242         unsigned int    newstate = 0;
243         struct cpufreq_freqs freqs;
244         cpumask_t cpus_allowed, affected_cpu_map;
245         int i;
246
247         if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
248                 return -EINVAL;
249
250         /* no transition necessary */
251         if (freqs.old == freqs.new)
252                 return 0;
253
254         freqs.old = speedstep_get_processor_frequency(speedstep_processor);
255         freqs.new = speedstep_freqs[newstate].frequency;
256         freqs.cpu = policy->cpu;
257
258         cpus_allowed = current->cpus_allowed;
259
260         /* only run on CPU to be set, or on its sibling */
261 #ifdef CONFIG_SMP
262         affected_cpu_map = cpu_sibling_map[policy->cpu];
263 #else
264         affected_cpu_map = cpumask_of_cpu(policy->cpu);
265 #endif
266
267         for_each_cpu_mask(i, affected_cpu_map) {
268                 freqs.cpu = i;
269                 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
270         }
271
272         /* switch to physical CPU where state is to be changed */
273         set_cpus_allowed(current, affected_cpu_map);
274
275         speedstep_set_state(newstate);
276
277         /* allow to be run on all CPUs */
278         set_cpus_allowed(current, cpus_allowed);
279
280         for_each_cpu_mask(i, affected_cpu_map) {
281                 freqs.cpu = i;
282                 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
283         }
284
285         return 0;
286 }
287
288
289 /**
290  * speedstep_verify - verifies a new CPUFreq policy
291  * @policy: new policy
292  *
293  * Limit must be within speedstep_low_freq and speedstep_high_freq, with
294  * at least one border included.
295  */
296 static int speedstep_verify (struct cpufreq_policy *policy)
297 {
298         return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
299 }
300
301
302 static int speedstep_cpu_init(struct cpufreq_policy *policy)
303 {
304         int             result = 0;
305         unsigned int    speed;
306         cpumask_t       cpus_allowed,affected_cpu_map;
307
308
309         /* capability check */
310         if (policy->cpu != 0) /* FIXME: better support for SMT in cpufreq core. Up until then, it's better to register only one CPU */
311                 return -ENODEV;
312
313         /* only run on CPU to be set, or on its sibling */
314         cpus_allowed = current->cpus_allowed;
315 #ifdef CONFIG_SMP
316         affected_cpu_map = cpu_sibling_map[policy->cpu];
317 #else
318         affected_cpu_map = cpumask_of_cpu(policy->cpu);
319 #endif
320         set_cpus_allowed(current, affected_cpu_map);
321
322         /* detect low and high frequency */
323         result = speedstep_get_freqs(speedstep_processor,
324                                      &speedstep_freqs[SPEEDSTEP_LOW].frequency,
325                                      &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
326                                      &speedstep_set_state);
327         if (result) {
328                 set_cpus_allowed(current, cpus_allowed);
329                 return result;
330         }
331
332         /* get current speed setting */
333         speed = speedstep_get_processor_frequency(speedstep_processor);
334         set_cpus_allowed(current, cpus_allowed);
335         if (!speed)
336                 return -EIO;
337
338         dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", 
339                 (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
340                 (speed / 1000));
341
342         /* cpuinfo and default policy values */
343         policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
344         policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
345         policy->cur = speed;
346
347         result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
348         if (result)
349                 return (result);
350
351         cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
352
353         return 0;
354 }
355
356
357 static int speedstep_cpu_exit(struct cpufreq_policy *policy)
358 {
359         cpufreq_frequency_table_put_attr(policy->cpu);
360         return 0;
361 }
362
363 static unsigned int speedstep_get(unsigned int cpu)
364 {
365         return speedstep_get_processor_frequency(speedstep_processor);
366 }
367
368 static struct freq_attr* speedstep_attr[] = {
369         &cpufreq_freq_attr_scaling_available_freqs,
370         NULL,
371 };
372
373
374 static struct cpufreq_driver speedstep_driver = {
375         .name           = "speedstep-ich",
376         .verify         = speedstep_verify,
377         .target         = speedstep_target,
378         .init           = speedstep_cpu_init,
379         .exit           = speedstep_cpu_exit,
380         .get            = speedstep_get,
381         .owner          = THIS_MODULE,
382         .attr           = speedstep_attr,
383 };
384
385
386 /**
387  * speedstep_init - initializes the SpeedStep CPUFreq driver
388  *
389  *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
390  * devices, -EINVAL on problems during initiatization, and zero on
391  * success.
392  */
393 static int __init speedstep_init(void)
394 {
395         /* detect processor */
396         speedstep_processor = speedstep_detect_processor();
397         if (!speedstep_processor)
398                 return -ENODEV;
399
400         /* detect chipset */
401         if (!speedstep_detect_chipset()) {
402                 printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
403                 return -ENODEV;
404         }
405
406         /* activate speedstep support */
407         if (speedstep_activate())
408                 return -EINVAL;
409
410         return cpufreq_register_driver(&speedstep_driver);
411 }
412
413
414 /**
415  * speedstep_exit - unregisters SpeedStep support
416  *
417  *   Unregisters SpeedStep support.
418  */
419 static void __exit speedstep_exit(void)
420 {
421         cpufreq_unregister_driver(&speedstep_driver);
422 }
423
424
425 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
426 MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
427 MODULE_LICENSE ("GPL");
428
429 module_init(speedstep_init);
430 module_exit(speedstep_exit);