Subversion Repositories shark

Rev

Rev 600 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
582 mauro 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 <linuxcomp.h>
22
 
23
#include <linux/kernel.h>
24
#include <linux/module.h> 
25
#include <linux/init.h>
26
#include <linux/cpufreq.h>
27
#include <linux/pci.h>
28
#include <linux/slab.h>
29
 
30
#include "speedstep-lib.h"
31
 
32
 
33
/* speedstep_chipset:
34
 *   It is necessary to know which chipset is used. As accesses to
35
 * this device occur at various places in this module, we need a
36
 * static struct pci_dev * pointing to that device.
37
 */
38
static struct pci_dev                   *speedstep_chipset_dev;
39
 
40
 
41
/* speedstep_processor
42
 */
43
static unsigned int                     speedstep_processor = 0;
44
 
45
 
46
/*
47
 *   There are only two frequency states for each processor. Values
48
 * are in kHz for the time being.
49
 */
50
static struct cpufreq_frequency_table speedstep_freqs[] = {
51
        {SPEEDSTEP_HIGH,        0},
52
        {SPEEDSTEP_LOW,         0},
53
        {0,                     CPUFREQ_TABLE_END},
54
};
55
 
56
 
57
/* DEBUG
58
 *   Define it if you want verbose debug output, e.g. for bug reporting
59
 */
60
#define SPEEDSTEP_DEBUG
61
 
62
#ifdef SPEEDSTEP_DEBUG
63
#define dprintk(msg...) printk(msg)
64
#else
65
#define dprintk(msg...) do { } while(0)
66
#endif
67
 
68
 
69
/**
70
 * speedstep_set_state - set the SpeedStep state
71
 * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
72
 *
73
 *   Tries to change the SpeedStep state.
74
 */
75
static void speedstep_set_state (unsigned int state, unsigned int notify)
76
{
77
        u32                     pmbase;
78
        u8                      pm2_blk;
79
        u8                      value;
80
        unsigned long           flags;
81
        struct cpufreq_freqs    freqs;
82
 
83
        if (!speedstep_chipset_dev || (state > 0x1))
84
                return;
85
 
86
        freqs.old = speedstep_get_processor_frequency(speedstep_processor);
87
        freqs.new = speedstep_freqs[state].frequency;
88
        freqs.cpu = 0; /* speedstep.c is UP only driver */
89
 
90
        //!!!if (notify)
91
                //!!!cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
92
 
93
        /* get PMBASE */
94
        pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
95
        if (!(pmbase & 0x01))
96
        {
97
                printk(KERN_ERR "cpufreq: could not find speedstep register\n");
98
                return;
99
        }
100
 
101
        pmbase &= 0xFFFFFFFE;
102
        if (!pmbase) {
103
                printk(KERN_ERR "cpufreq: could not find speedstep register\n");
104
                return;
105
        }
106
 
107
        /* Disable IRQs */
108
        local_irq_save(flags);
109
 
110
        /* read state */
111
        value = inb(pmbase + 0x50);
112
 
113
        dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
114
 
115
        /* write new state */
116
        value &= 0xFE;
117
        value |= state;
118
 
119
        dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
120
 
121
        /* Disable bus master arbitration */
122
        pm2_blk = inb(pmbase + 0x20);
123
        pm2_blk |= 0x01;
124
        outb(pm2_blk, (pmbase + 0x20));
125
 
126
        /* Actual transition */
127
        outb(value, (pmbase + 0x50));
128
 
129
        /* Restore bus master arbitration */
130
        pm2_blk &= 0xfe;
131
        outb(pm2_blk, (pmbase + 0x20));
132
 
133
        /* check if transition was successful */
134
        value = inb(pmbase + 0x50);
135
 
136
        /* Enable IRQs */
137
        local_irq_restore(flags);
138
 
139
        dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
140
 
141
        if (state == (value & 0x1)) {
142
                dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
143
        } else {
144
                printk (KERN_ERR "cpufreq: change failed - I/O error\n");
145
        }
146
 
147
        ///!!!if (notify)
148
                //!!!cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
149
 
150
        return;
151
}
152
 
153
 
154
/**
155
 * speedstep_activate - activate SpeedStep control in the chipset
156
 *
157
 *   Tries to activate the SpeedStep status and control registers.
158
 * Returns -EINVAL on an unsupported chipset, and zero on success.
159
 */
160
static int speedstep_activate (void)
161
{
162
        u16             value = 0;
163
 
164
        if (!speedstep_chipset_dev)
165
                return -EINVAL;
166
 
167
        pci_read_config_word(speedstep_chipset_dev,
168
                             0x00A0, &value);
169
        if (!(value & 0x08)) {
170
                value |= 0x08;
171
                dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n");
172
                pci_write_config_word(speedstep_chipset_dev,
173
                                      0x00A0, value);
174
        }
175
 
176
        return 0;
177
}
178
 
179
 
180
/**
181
 * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
182
 *
183
 *   Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to
184
 * the LPC bridge / PM module which contains all power-management
185
 * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
186
 * chipset, or zero on failure.
187
 */
188
static unsigned int speedstep_detect_chipset (void)
189
{
190
        speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
191
                              PCI_DEVICE_ID_INTEL_82801DB_12,
192
                              PCI_ANY_ID,
193
                              PCI_ANY_ID,
194
                              NULL);
195
        if (speedstep_chipset_dev)
196
                return 4; /* 4-M */
197
 
198
        speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
199
                              PCI_DEVICE_ID_INTEL_82801CA_12,
200
                              PCI_ANY_ID,
201
                              PCI_ANY_ID,
202
                              NULL);
203
        if (speedstep_chipset_dev)
204
                return 3; /* 3-M */
205
 
206
 
207
        speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
208
                              PCI_DEVICE_ID_INTEL_82801BA_10,
209
                              PCI_ANY_ID,
210
                              PCI_ANY_ID,
211
                              NULL);
212
        if (speedstep_chipset_dev) {
213
                /* speedstep.c causes lockups on Dell Inspirons 8000 and
214
                 * 8100 which use a pretty old revision of the 82815
215
                 * host brige. Abort on these systems.
216
                 */
217
                static struct pci_dev   *hostbridge;
218
                u8                      rev = 0;
219
 
220
                hostbridge  = pci_find_subsys(PCI_VENDOR_ID_INTEL,
221
                              PCI_DEVICE_ID_INTEL_82815_MC,
222
                              PCI_ANY_ID,
223
                              PCI_ANY_ID,
224
                              NULL);
225
 
226
                if (!hostbridge)
227
                        return 2; /* 2-M */
228
 
229
                pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev);
230
                if (rev < 5) {
231
                        dprintk(KERN_INFO "cpufreq: hostbridge does not support speedstep\n");
232
                        speedstep_chipset_dev = NULL;
233
                        return 0;
234
                }
235
 
236
                return 2; /* 2-M */
237
        }
238
 
239
        return 0;
240
}
241
 
242
 
243
/**
244
 * speedstep_setpolicy - set a new CPUFreq policy
245
 * @policy: new policy
246
 *
247
 * Sets a new CPUFreq policy.
248
 */
249
static int speedstep_target (struct cpufreq_policy *policy,
250
                             unsigned int target_freq,
251
                             unsigned int relation)
252
{
253
        unsigned int    newstate = 0;
254
 
255
        if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
256
                return -EINVAL;
257
 
258
        speedstep_set_state(newstate, 1);
259
 
260
        return 0;
261
}
262
 
263
 
264
/**
265
 * speedstep_verify - verifies a new CPUFreq policy
266
 * @freq: new policy
267
 *
268
 * Limit must be within speedstep_low_freq and speedstep_high_freq, with
269
 * at least one border included.
270
 */
271
static int speedstep_verify (struct cpufreq_policy *policy)
272
{
273
        return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
274
}
275
 
276
 
277
static int speedstep_cpu_init(struct cpufreq_policy *policy)
278
{
279
        int             result = 0;
280
        unsigned int    speed;
281
 
282
        /* capability check */
283
        if (policy->cpu != 0)
284
                return -ENODEV;
285
 
286
        /* detect low and high frequency */
287
        result = speedstep_get_freqs(speedstep_processor,
288
                                     &speedstep_freqs[SPEEDSTEP_LOW].frequency,
289
                                     &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
290
                                     &speedstep_set_state);
291
        if (result)
292
                return result;
293
 
294
        /* get current speed setting */
295
        speed = speedstep_get_processor_frequency(speedstep_processor);
296
        if (!speed)
297
                return -EIO;
298
 
299
        dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n",
300
                (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
301
                (speed / 1000));
302
 
303
        /* cpuinfo and default policy values */
304
        policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
305
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
306
        policy->cur = speed;
307
 
308
        return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
309
}
310
 
311
 
312
static struct cpufreq_driver speedstep_driver = {
313
        .name           = "speedstep-ich",
314
        .verify         = speedstep_verify,
315
        .target         = speedstep_target,
316
        .init           = speedstep_cpu_init,
317
        .owner          = THIS_MODULE,
318
};
319
 
320
 
321
/**
322
 * speedstep_init - initializes the SpeedStep CPUFreq driver
323
 *
324
 *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
325
 * devices, -EINVAL on problems during initiatization, and zero on
326
 * success.
327
 */
328
/*static*/ int __init speedstep_ich_init(void)
329
{
330
        /* detect processor */
331
        speedstep_processor = speedstep_detect_processor();
332
        if (!speedstep_processor)
333
                return -ENODEV;
334
 
335
        /* detect chipset */
336
        if (!speedstep_detect_chipset()) {
337
                printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
338
                return -ENODEV;
339
        }
340
 
341
        /* activate speedstep support */
342
        if (speedstep_activate())
343
                return -EINVAL;
344
 
345
        return cpufreq_register_driver(&speedstep_driver);
346
}
347
 
348
 
349
/**
350
 * speedstep_exit - unregisters SpeedStep support
351
 *
352
 *   Unregisters SpeedStep support.
353
 */
354
/*static*/ void __exit speedstep_ich_exit(void)
355
{
356
        cpufreq_unregister_driver(&speedstep_driver);
357
}
358
 
359
 
360
MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
361
MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
362
MODULE_LICENSE ("GPL");
363
 
364
module_init(speedstep_ich_init);
365
module_exit(speedstep_ich_exit);