Subversion Repositories shark

Rev

Rev 582 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
582 mauro 1
/*
2
 * Intel SpeedStep SMI driver.
3
 *
4
 * (C) 2003  Hiroshi Miura <miura@da-cha.org>
5
 *
6
 *  Licensed under the terms of the GNU GPL License version 2.
7
 *
8
 */
9
 
10
 
11
/*********************************************************************
12
 *                        SPEEDSTEP - DEFINITIONS                    *
13
 *********************************************************************/
14
 
15
#include <linuxcomp.h>
16
 
17
#include <linux/kernel.h>
18
#include <linux/module.h> 
19
#include <linux/moduleparam.h> 
20
#include <linux/init.h>
21
#include <linux/cpufreq.h>
22
#include <linux/pci.h>
23
#include <linux/slab.h>
24
#include <asm/ist.h>
25
 
26
#include "speedstep-lib.h"
27
 
28
#define PFX "speedstep-smi: "
29
 
30
/* speedstep system management interface port/command.
31
 *
32
 * These parameters are got from IST-SMI BIOS call.
33
 * If user gives it, these are used.
34
 *
35
 */
36
static int              smi_port        = 0;
37
static int              smi_cmd         = 0;
38
static unsigned int     smi_sig         = 0;
39
 
40
 
41
/*
42
 *   There are only two frequency states for each processor. Values
43
 * are in kHz for the time being.
44
 */
45
static struct cpufreq_frequency_table speedstep_freqs[] = {
46
        {SPEEDSTEP_HIGH,        0},
47
        {SPEEDSTEP_LOW,         0},
48
        {0,                     CPUFREQ_TABLE_END},
49
};
50
 
51
#define GET_SPEEDSTEP_OWNER 0
52
#define GET_SPEEDSTEP_STATE 1
53
#define SET_SPEEDSTEP_STATE 2
54
#define GET_SPEEDSTEP_FREQS 4
55
 
56
/* DEBUG
57
 *   Define it if you want verbose debug output, e.g. for bug reporting
58
 */
59
#define SPEEDSTEP_DEBUG
60
 
61
#ifdef SPEEDSTEP_DEBUG
62
#define dprintk(msg...) printk(msg)
63
#else
64
#define dprintk(msg...) do { } while(0)
65
#endif
66
 
67
/**
68
 * speedstep_smi_ownership
69
 */
70
static int speedstep_smi_ownership (void)
71
{
72
        u32 command, result, magic;
73
        u32 function = GET_SPEEDSTEP_OWNER;
74
        unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
75
 
76
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
77
        magic = virt_to_phys(magic_data);
78
 
79
        __asm__ __volatile__(
80
                "out %%al, (%%dx)\n"
81
                : "=D" (result)
82
                : "a" (command), "b" (function), "c" (0), "d" (smi_port), "D" (0), "S" (magic)
83
        );
84
 
85
        return result;
86
}
87
 
88
/**
89
 * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
90
 *
91
 */
92
static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
93
{
94
        u32 command, result, edi, high_mhz, low_mhz;
95
        u32 state=0;
96
        u32 function = GET_SPEEDSTEP_FREQS;
97
 
98
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
99
 
100
        __asm__ __volatile__("movl $0, %%edi\n"
101
                "out %%al, (%%dx)\n"
102
                : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi)
103
                : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
104
        );
105
        *high = high_mhz * 1000;
106
        *low  = low_mhz  * 1000;
107
 
108
        return result;
109
}
110
 
111
/**
112
 * speedstep_get_state - set the SpeedStep state
113
 * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
114
 *
115
 */
116
static int speedstep_get_state (void)
117
{
118
        u32 function=GET_SPEEDSTEP_STATE;
119
        u32 result, state, edi, command;
120
 
121
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
122
 
123
        __asm__ __volatile__("movl $0, %%edi\n"
124
                "out %%al, (%%dx)\n"
125
                : "=a" (result), "=b" (state), "=D" (edi)
126
                : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0)
127
        );
128
 
129
        return state;
130
}
131
 
132
/**
133
 * speedstep_set_state - set the SpeedStep state
134
 * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
135
 *
136
 */
137
static void speedstep_set_state (unsigned int state, unsigned int notify)
138
{
139
        unsigned int old_state, result, command, new_state;
140
        unsigned long flags;
141
        struct cpufreq_freqs freqs;
142
        unsigned int function=SET_SPEEDSTEP_STATE;
143
 
144
        if (state > 0x1)
145
                return;
146
 
147
        old_state = speedstep_get_state();
148
        freqs.old = speedstep_freqs[old_state].frequency;
149
        freqs.new = speedstep_freqs[state].frequency;
150
        freqs.cpu = 0; /* speedstep.c is UP only driver */
151
 
152
        if (old_state == state)
153
                return;
154
 
600 mauro 155
        if (notify)
156
                cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
582 mauro 157
 
158
        /* Disable IRQs */
159
        local_irq_save(flags);
160
 
161
        command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
162
        __asm__ __volatile__(
163
                "movl $0, %%edi\n"
164
                "out %%al, (%%dx)\n"
165
                : "=b" (new_state), "=D" (result)
166
                : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
167
        );
168
 
169
        /* enable IRQs */
170
        local_irq_restore(flags);
171
 
172
        if (new_state == state) {
173
                dprintk(KERN_INFO "cpufreq: change to %u MHz succeded\n", (freqs.new / 1000));
174
        } else {
175
                printk(KERN_ERR "cpufreq: change failed\n");
176
        }
177
 
600 mauro 178
        if (notify)
179
                cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
582 mauro 180
 
181
        return;
182
}
183
 
184
 
185
/**
186
 * speedstep_target - set a new CPUFreq policy
187
 * @policy: new policy
188
 * @target_freq: new freq
189
 * @relation:
190
 *
191
 * Sets a new CPUFreq policy/freq.
192
 */
193
static int speedstep_target (struct cpufreq_policy *policy,
194
                        unsigned int target_freq, unsigned int relation)
195
{
196
        unsigned int newstate = 0;
197
 
198
        if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
199
                return -EINVAL;
200
 
201
        speedstep_set_state(newstate, 1);
202
 
203
        return 0;
204
}
205
 
206
 
207
/**
208
 * speedstep_verify - verifies a new CPUFreq policy
209
 * @freq: new policy
210
 *
211
 * Limit must be within speedstep_low_freq and speedstep_high_freq, with
212
 * at least one border included.
213
 */
214
static int speedstep_verify (struct cpufreq_policy *policy)
215
{
216
        return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
217
}
218
 
219
 
220
static int speedstep_cpu_init(struct cpufreq_policy *policy)
221
{
222
        int result;
223
        unsigned int speed,state;
224
 
225
        /* capability check */
226
        if (policy->cpu != 0)
227
                return -ENODEV;
228
 
229
        result = speedstep_smi_ownership();
230
 
231
        if (result)
232
                dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a SMI interface.\n");
233
 
234
        /* detect low and high frequency */
235
        result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
236
                                &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
237
        if (result) {
238
                /* fall back to speedstep_lib.c dection mechanism: try both states out */
239
                unsigned int speedstep_processor = speedstep_detect_processor();
240
 
241
                dprintk(KERN_INFO PFX "could not detect low and high frequencies by SMI call.\n");
242
                if (!speedstep_processor)
243
                        return -ENODEV;
244
 
245
                result = speedstep_get_freqs(speedstep_processor,
246
                                &speedstep_freqs[SPEEDSTEP_LOW].frequency,
247
                                &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
248
                                &speedstep_set_state);
249
 
250
                if (result) {
251
                        dprintk(KERN_INFO PFX "could not detect two different speeds -- aborting.\n");
252
                        return result;
253
                } else
254
                        dprintk(KERN_INFO PFX "workaround worked.\n");
255
        }
256
 
257
        /* get current speed setting */
258
        state = speedstep_get_state();
259
        speed = speedstep_freqs[state].frequency;
260
 
261
        dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n",
262
                (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
263
                (speed / 1000));
264
 
265
        /* cpuinfo and default policy values */
266
        policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
267
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
268
        policy->cur = speed;
269
 
270
        return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
271
}
272
 
273
 
274
static int speedstep_resume(struct cpufreq_policy *policy)
275
{
276
        int result = speedstep_smi_ownership();
277
 
278
        if (result)
279
                dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a SMI interface.\n");
280
 
281
        return result;
282
}
283
 
284
 
285
static struct cpufreq_driver speedstep_driver = {
286
        .name           = "speedstep-smi",
287
        .verify         = speedstep_verify,
288
        .target         = speedstep_target,
289
        .init           = speedstep_cpu_init,
290
        .resume         = speedstep_resume,
291
};
292
 
293
/**
294
 * speedstep_init - initializes the SpeedStep CPUFreq driver
295
 *
296
 *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
297
 * BIOS, -EINVAL on problems during initiatization, and zero on
298
 * success.
299
 */
300
/*static*/ int __init speedstep_smi_init(void)
301
{
302
        struct cpuinfo_x86 *c = &new_cpu_data;
303
 
304
        if (c->x86_vendor != X86_VENDOR_INTEL) {
305
                dprintk (KERN_INFO PFX "No Intel CPU detected.\n");
306
                return -ENODEV;
307
        }
308
 
309
        dprintk(KERN_DEBUG PFX "signature:0x%.8lx, command:0x%.8lx, event:0x%.8lx, perf_level:0x%.8lx.\n",
310
                ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level);
311
 
312
 
313
        /* Error if no IST-SMI BIOS or no PARM
314
                 sig= 'ISGE' aka 'Intel Speedstep Gate E' */
315
        if ((ist_info.signature !=  0x47534943) && (
316
            (smi_port == 0) || (smi_cmd == 0)))
317
                return -ENODEV;
318
 
319
        if (smi_sig == 1)
320
                smi_sig = 0x47534943;
321
        else
322
                smi_sig = ist_info.signature;
323
 
324
        /* setup smi_port from MODLULE_PARM or BIOS */
325
        if ((smi_port > 0xff) || (smi_port < 0)) {
326
                return -EINVAL;
327
        } else if (smi_port == 0) {
328
                smi_port = ist_info.command & 0xff;
329
        }
330
 
331
        if ((smi_cmd > 0xff) || (smi_cmd < 0)) {
332
                return -EINVAL;
333
        } else if (smi_cmd == 0) {
334
                smi_cmd = (ist_info.command >> 16) & 0xff;
335
        }
336
 
337
        return cpufreq_register_driver(&speedstep_driver);
338
}
339
 
340
 
341
/**
342
 * speedstep_exit - unregisters SpeedStep support
343
 *
344
 *   Unregisters SpeedStep support.
345
 */
346
/*static*/ void __exit speedstep_smi_exit(void)
347
{
348
        cpufreq_unregister_driver(&speedstep_driver);
349
}
350
 
351
module_param(smi_port,  int, 0444);
352
module_param(smi_cmd,   int, 0444);
353
module_param(smi_sig,  uint, 0444);
354
 
355
MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value -- Intel's default setting is 0xb2");
356
MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value -- Intel's default setting is 0x82");
357
MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the SMI interface.");
358
 
359
MODULE_AUTHOR ("Hiroshi Miura");
360
MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
361
MODULE_LICENSE ("GPL");
362
 
363
module_init(speedstep_smi_init);
364
module_exit(speedstep_smi_exit);