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