ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[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  * @notify: whether to call cpufreq_notify_transition for CPU speed changes
71  *
72  *   Tries to change the SpeedStep state. 
73  */
74 static void speedstep_set_state (unsigned int state, unsigned int notify)
75 {
76         u32                     pmbase;
77         u8                      pm2_blk;
78         u8                      value;
79         unsigned long           flags;
80         struct cpufreq_freqs    freqs;
81
82         if (!speedstep_chipset_dev || (state > 0x1))
83                 return;
84
85         freqs.old = speedstep_get_processor_frequency(speedstep_processor);
86         freqs.new = speedstep_freqs[state].frequency;
87         freqs.cpu = 0; /* speedstep.c is UP only driver */
88         
89         if (notify)
90                 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
91
92         /* get PMBASE */
93         pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
94         if (!(pmbase & 0x01))
95         {
96                 printk(KERN_ERR "cpufreq: could not find speedstep register\n");
97                 return;
98         }
99
100         pmbase &= 0xFFFFFFFE;
101         if (!pmbase) {
102                 printk(KERN_ERR "cpufreq: could not find speedstep register\n");
103                 return;
104         }
105
106         /* Disable IRQs */
107         local_irq_save(flags);
108
109         /* read state */
110         value = inb(pmbase + 0x50);
111
112         dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
113
114         /* write new state */
115         value &= 0xFE;
116         value |= state;
117
118         dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
119
120         /* Disable bus master arbitration */
121         pm2_blk = inb(pmbase + 0x20);
122         pm2_blk |= 0x01;
123         outb(pm2_blk, (pmbase + 0x20));
124
125         /* Actual transition */
126         outb(value, (pmbase + 0x50));
127
128         /* Restore bus master arbitration */
129         pm2_blk &= 0xfe;
130         outb(pm2_blk, (pmbase + 0x20));
131
132         /* check if transition was successful */
133         value = inb(pmbase + 0x50);
134
135         /* Enable IRQs */
136         local_irq_restore(flags);
137
138         dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
139
140         if (state == (value & 0x1)) {
141                 dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
142         } else {
143                 printk (KERN_ERR "cpufreq: change failed - I/O error\n");
144         }
145
146         if (notify)
147                 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
148
149         return;
150 }
151
152
153 /**
154  * speedstep_activate - activate SpeedStep control in the chipset
155  *
156  *   Tries to activate the SpeedStep status and control registers.
157  * Returns -EINVAL on an unsupported chipset, and zero on success.
158  */
159 static int speedstep_activate (void)
160 {
161         u16             value = 0;
162
163         if (!speedstep_chipset_dev)
164                 return -EINVAL;
165
166         pci_read_config_word(speedstep_chipset_dev, 
167                              0x00A0, &value);
168         if (!(value & 0x08)) {
169                 value |= 0x08;
170                 dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n");
171                 pci_write_config_word(speedstep_chipset_dev, 
172                                       0x00A0, value);
173         }
174
175         return 0;
176 }
177
178
179 /**
180  * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
181  *
182  *   Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to 
183  * the LPC bridge / PM module which contains all power-management 
184  * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
185  * chipset, or zero on failure.
186  */
187 static unsigned int speedstep_detect_chipset (void)
188 {
189         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
190                               PCI_DEVICE_ID_INTEL_82801DB_12, 
191                               PCI_ANY_ID,
192                               PCI_ANY_ID,
193                               NULL);
194         if (speedstep_chipset_dev)
195                 return 4; /* 4-M */
196
197         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
198                               PCI_DEVICE_ID_INTEL_82801CA_12, 
199                               PCI_ANY_ID,
200                               PCI_ANY_ID,
201                               NULL);
202         if (speedstep_chipset_dev)
203                 return 3; /* 3-M */
204
205
206         speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
207                               PCI_DEVICE_ID_INTEL_82801BA_10,
208                               PCI_ANY_ID,
209                               PCI_ANY_ID,
210                               NULL);
211         if (speedstep_chipset_dev) {
212                 /* speedstep.c causes lockups on Dell Inspirons 8000 and
213                  * 8100 which use a pretty old revision of the 82815 
214                  * host brige. Abort on these systems.
215                  */
216                 static struct pci_dev   *hostbridge;
217                 u8                      rev = 0;
218
219                 hostbridge  = pci_find_subsys(PCI_VENDOR_ID_INTEL,
220                               PCI_DEVICE_ID_INTEL_82815_MC,
221                               PCI_ANY_ID,
222                               PCI_ANY_ID,
223                               NULL);
224
225                 if (!hostbridge)
226                         return 2; /* 2-M */
227                         
228                 pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev);
229                 if (rev < 5) {
230                         dprintk(KERN_INFO "cpufreq: hostbridge does not support speedstep\n");
231                         speedstep_chipset_dev = NULL;
232                         return 0;
233                 }
234
235                 return 2; /* 2-M */
236         }
237
238         return 0;
239 }
240
241
242 /**
243  * speedstep_target - set a new CPUFreq policy
244  * @policy: new policy
245  * @target_freq: the target frequency
246  * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
247  *
248  * Sets a new CPUFreq policy.
249  */
250 static int speedstep_target (struct cpufreq_policy *policy,
251                              unsigned int target_freq,
252                              unsigned int relation)
253 {
254         unsigned int    newstate = 0;
255
256         if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
257                 return -EINVAL;
258
259         speedstep_set_state(newstate, 1);
260
261         return 0;
262 }
263
264
265 /**
266  * speedstep_verify - verifies a new CPUFreq policy
267  * @policy: new policy
268  *
269  * Limit must be within speedstep_low_freq and speedstep_high_freq, with
270  * at least one border included.
271  */
272 static int speedstep_verify (struct cpufreq_policy *policy)
273 {
274         return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
275 }
276
277
278 static int speedstep_cpu_init(struct cpufreq_policy *policy)
279 {
280         int             result = 0;
281         unsigned int    speed;
282
283         /* capability check */
284         if (policy->cpu != 0)
285                 return -ENODEV;
286
287         /* detect low and high frequency */
288         result = speedstep_get_freqs(speedstep_processor,
289                                      &speedstep_freqs[SPEEDSTEP_LOW].frequency,
290                                      &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
291                                      &speedstep_set_state);
292         if (result)
293                 return result;
294
295         /* get current speed setting */
296         speed = speedstep_get_processor_frequency(speedstep_processor);
297         if (!speed)
298                 return -EIO;
299
300         dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", 
301                 (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
302                 (speed / 1000));
303
304         /* cpuinfo and default policy values */
305         policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
306         policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
307         policy->cur = speed;
308
309         result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
310         if (result)
311                 return (result);
312
313         cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
314
315         return 0;
316 }
317
318
319 static int speedstep_cpu_exit(struct cpufreq_policy *policy)
320 {
321         cpufreq_frequency_table_put_attr(policy->cpu);
322         return 0;
323 }
324
325
326 static struct freq_attr* speedstep_attr[] = {
327         &cpufreq_freq_attr_scaling_available_freqs,
328         NULL,
329 };
330
331
332 static struct cpufreq_driver speedstep_driver = {
333         .name           = "speedstep-ich",
334         .verify         = speedstep_verify,
335         .target         = speedstep_target,
336         .init           = speedstep_cpu_init,
337         .exit           = speedstep_cpu_exit,
338         .owner          = THIS_MODULE,
339         .attr           = speedstep_attr,
340 };
341
342
343 /**
344  * speedstep_init - initializes the SpeedStep CPUFreq driver
345  *
346  *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
347  * devices, -EINVAL on problems during initiatization, and zero on
348  * success.
349  */
350 static int __init speedstep_init(void)
351 {
352         /* detect processor */
353         speedstep_processor = speedstep_detect_processor();
354         if (!speedstep_processor)
355                 return -ENODEV;
356
357         /* detect chipset */
358         if (!speedstep_detect_chipset()) {
359                 printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
360                 return -ENODEV;
361         }
362
363         /* activate speedstep support */
364         if (speedstep_activate())
365                 return -EINVAL;
366
367         return cpufreq_register_driver(&speedstep_driver);
368 }
369
370
371 /**
372  * speedstep_exit - unregisters SpeedStep support
373  *
374  *   Unregisters SpeedStep support.
375  */
376 static void __exit speedstep_exit(void)
377 {
378         cpufreq_unregister_driver(&speedstep_driver);
379 }
380
381
382 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
383 MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
384 MODULE_LICENSE ("GPL");
385
386 module_init(speedstep_init);
387 module_exit(speedstep_exit);