Project

General

Profile

Statistics
| Branch: | Revision:

root / prex-0.9.0 / bsp / drv / dev / cpufreq / est.c @ 03e9c04a

History | View | Annotate | Download (13.2 KB)

1 03e9c04a Brad Neuman
/*        $OpenBSD: est.c,v 1.11 2005/03/07 06:59:14 mbalmer Exp $ */
2
/*
3
 * Copyright (c) 2003 Michael Eriksson.
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 * 3. The name of the author may not be used to endorse or promote products
15
 *    derived from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
/*
29
 * This is a driver for Intel's Enhanced SpeedStep, as implemented in
30
 * Pentium M processors.
31
 *
32
 * Reference documentation:
33
 *
34
 * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
35
 *   System Programming Guide.
36
 *   Section 13.14, Enhanced Intel SpeedStep technology.
37
 *   Table B-2, MSRs in Pentium M Processors.
38
 *   http://www.intel.com/design/pentium4/manuals/245472.htm
39
 *
40
 * - Intel Pentium M Processor Datasheet.
41
 *   Table 5, Voltage and Current Specifications.
42
 *   http://www.intel.com/design/mobile/datashts/252612.htm
43
 *
44
 * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
45
 *   Table 3-4, Voltage and Current Specifications.
46
 *   http://www.intel.com/design/mobile/datashts/302189.htm
47
 *
48
 * - Linux cpufreq patches, speedstep-centrino.c.
49
 *   Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
50
 *   http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
51
 */
52
53
/*
54
 * est.c - Intel enhanced speedstep driver from OpenBSD.
55
 */
56
57
#include <driver.h>
58
#include <cpufreq.h>
59
#include <cpufunc.h>
60
#include <sys/ioctl.h>
61
62
/* #define DEBUG_EST 1 */
63
64
#ifdef DEBUG_EST
65
#define DPRINTF(a) printf a
66
#else
67
#define DPRINTF(a)
68
#endif
69
70
/* Status/control registers (from the IA-32 System Programming Guide). */
71
#define MSR_PERF_STATUS                0x198
72
#define MSR_PERF_CTL                0x199
73
74
/* Register and bit for enabling SpeedStep. */
75
#define MSR_MISC_ENABLE                0x1a0
76
#define MSR_SS_ENABLE                (1<<16)
77
78
static int        est_probe(struct driver *);
79
static int        est_init(struct driver *);
80
static int        est_setperf(int);
81
static int        est_getperf(void);
82
static void        est_getinfo(struct cpufreqinfo *);
83
static int        est_identify(char *);
84
85
static struct cpufreq_ops est_ops = {
86
        /* setperf */        est_setperf,
87
        /* getpref */        est_getperf,
88
        /* getinfo */        est_getinfo,
89
};
90
91
struct driver est_driver = {
92
        /* name */        "est",
93
        /* devops */        NULL,
94
        /* flags */        0,
95
        /* flags */        0,
96
        /* probe */        est_probe,
97
        /* init */        est_init,
98
        /* shutdown */        NULL,
99
};
100
101
/*
102
 * Frequency tables
103
 */
104
struct fq_info {
105
        int        mhz;
106
        int        mv;
107
};
108
109
/* Ultra Low Voltage Intel Pentium M processor 900 MHz */
110
static const struct fq_info pentium_m_900[] = {
111
        {  900, 1004 },
112
        {  800,  988 },
113
        {  600,  844 },
114
};
115
116
/* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
117
static const struct fq_info pentium_m_1000[] = {
118
        { 1000, 1004 },
119
        {  900,  988 },
120
        {  800,  972 },
121
        {  600,  844 },
122
};
123
124
/* Low Voltage Intel Pentium M processor 1.10 GHz */
125
static const struct fq_info pentium_m_1100[] = {
126
        { 1100, 1180 },
127
        { 1000, 1164 },
128
        {  900, 1100 },
129
        {  800, 1020 },
130
        {  600,  956 },
131
};
132
133
/* Low Voltage Intel Pentium M processor 1.20 GHz */
134
static const struct fq_info pentium_m_1200[] = {
135
        { 1200, 1180 },
136
        { 1100, 1164 },
137
        { 1000, 1100 },
138
        {  900, 1020 },
139
        {  800, 1004 },
140
        {  600,  956 },
141
};
142
143
/* Intel Pentium M processor 1.30 GHz */
144
static const struct fq_info pentium_m_1300[] = {
145
        { 1300, 1388 },
146
        { 1200, 1356 },
147
        { 1000, 1292 },
148
        {  800, 1260 },
149
        {  600,  956 },
150
};
151
152
/* Intel Pentium M processor 1.40 GHz */
153
static const struct fq_info pentium_m_1400[] = {
154
        { 1400, 1484 },
155
        { 1200, 1436 },
156
        { 1000, 1308 },
157
        {  800, 1180 },
158
        {  600,  956 }
159
};
160
161
/* Intel Pentium M processor 1.50 GHz */
162
static const struct fq_info pentium_m_1500[] = {
163
        { 1500, 1484 },
164
        { 1400, 1452 },
165
        { 1200, 1356 },
166
        { 1000, 1228 },
167
        {  800, 1116 },
168
        {  600,  956 }
169
};
170
171
/* Intel Pentium M processor 1.60 GHz */
172
static const struct fq_info pentium_m_1600[] = {
173
        { 1600, 1484 },
174
        { 1400, 1420 },
175
        { 1200, 1276 },
176
        { 1000, 1164 },
177
        {  800, 1036 },
178
        {  600,  956 }
179
};
180
181
/* Intel Pentium M processor 1.70 GHz */
182
static const struct fq_info pentium_m_1700[] = {
183
        { 1700, 1484 },
184
        { 1400, 1308 },
185
        { 1200, 1228 },
186
        { 1000, 1116 },
187
        {  800, 1004 },
188
        {  600,  956 }
189
};
190
191
192
/* Intel Pentium M processor 723 1.0 GHz */
193
static const struct fq_info pentium_m_n723[] = {
194
        { 1000,  940 },
195
        {  900,  908 },
196
        {  800,  876 },
197
        {  600,  812 }
198
};
199
200
/* Intel Pentium M processor 733 1.1 GHz */
201
static const struct fq_info pentium_m_n733[] = {
202
        { 1100,  940 },
203
        { 1000,  924 },
204
        {  900,  892 },
205
        {  800,  876 },
206
        {  600,  812 }
207
};
208
209
/* Intel Pentium M processor 753 1.2 GHz */
210
static const struct fq_info pentium_m_n753[] = {
211
        { 1200,  940 },
212
        { 1100,  924 },
213
        { 1000,  908 },
214
        {  900,  876 },
215
        {  800,  860 },
216
        {  600,  812 }
217
};
218
219
/* Intel Pentium M processor 738 1.4 GHz */
220
static const struct fq_info pentium_m_n738[] = {
221
        { 1400, 1116 },
222
        { 1300, 1116 },
223
        { 1200, 1100 },
224
        { 1100, 1068 },
225
        { 1000, 1052 },
226
        {  900, 1036 },
227
        {  800, 1020 },
228
        {  600,  988 }
229
};
230
231
#if 0
232
/* Intel Pentium M processor 758 1.5 GHz */
233
static const struct fq_info pentium_m_n758[] = {
234
        { 1500, 1116 },
235
        { 1400, 1116 },
236
        { 1300, 1100 },
237
        { 1200, 1084 },
238
        { 1100, 1068 },
239
        { 1000, 1052 },
240
        {  900, 1036 },
241
        {  800, 1020 },
242
        {  600,  988 }
243
};
244
#endif
245
246
/* Intel Pentium M processor 715 1.5 GHz */
247
static const struct fq_info pentium_m_n715[] = {
248
        { 1500, 1340 },
249
        { 1200, 1228 },
250
        { 1000, 1148 },
251
        {  800, 1068 },
252
        {  600,  988 }
253
};
254
255
/* Intel Pentium M processor 725 1.6 GHz */
256
static const struct fq_info pentium_m_n725[] = {
257
        { 1600, 1340 },
258
        { 1400, 1276 },
259
        { 1200, 1212 },
260
        { 1000, 1132 },
261
        {  800, 1068 },
262
        {  600,  988 }
263
};
264
265
/* Intel Pentium M processor 735 1.7 GHz */
266
static const struct fq_info pentium_m_n735[] = {
267
        { 1700, 1340 },
268
        { 1400, 1244 },
269
        { 1200, 1180 },
270
        { 1000, 1116 },
271
        {  800, 1052 },
272
        {  600,  988 }
273
};
274
275
/* Intel Pentium M processor 745 1.8 GHz */
276
static const struct fq_info pentium_m_n745[] = {
277
        { 1800, 1340 },
278
        { 1600, 1292 },
279
        { 1400, 1228 },
280
        { 1200, 1164 },
281
        { 1000, 1116 },
282
        {  800, 1052 },
283
        {  600,  988 }
284
};
285
286
/* Intel Pentium M processor 755 2.0 GHz */
287
static const struct fq_info pentium_m_n755[] = {
288
        { 2000, 1340 },
289
        { 1800, 1292 },
290
        { 1600, 1244 },
291
        { 1400, 1196 },
292
        { 1200, 1148 },
293
        { 1000, 1100 },
294
        {  800, 1052 },
295
        {  600,  988 }
296
};
297
298
/* Intel Pentium M processor 765 2.1 GHz */
299
static const struct fq_info pentium_m_n765[] = {
300
        { 2100, 1340 },
301
        { 1800, 1276 },
302
        { 1600, 1228 },
303
        { 1400, 1180 },
304
        { 1200, 1132 },
305
        { 1000, 1084 },
306
        {  800, 1036 },
307
        {  600,  988 }
308
};
309
310
struct fqlist {
311
        const char *brand_tag;
312
        const struct fq_info *table;
313
        int n;
314
};
315
316
static const struct fqlist pentium_m[] = {
317
#define ENTRY(s, v)        { s, v, (int)(sizeof(v) / sizeof((v)[0])) }
318
        ENTRY(" 900", pentium_m_900),
319
        ENTRY("1000", pentium_m_1000),
320
        ENTRY("1100", pentium_m_1100),
321
        ENTRY("1200", pentium_m_1200),
322
        ENTRY("1300", pentium_m_1300),
323
        ENTRY("1400", pentium_m_1400),
324
        ENTRY("1500", pentium_m_1500),
325
        ENTRY("1600", pentium_m_1600),
326
        ENTRY("1700", pentium_m_1700),
327
#undef ENTRY
328
};
329
330
static const struct fqlist pentium_m_dothan[] = {
331
#define ENTRY(s, v)        { s, v, (int)(sizeof(v) / sizeof((v)[0])) }
332
        ENTRY("1.00", pentium_m_n723),
333
        ENTRY("1.10", pentium_m_n733),
334
        ENTRY("1.20", pentium_m_n753),
335
        ENTRY("1.40", pentium_m_n738),
336
#if 0
337
        ENTRY("1.50", pentium_m_n758),
338
#endif
339
        ENTRY("1.50", pentium_m_n715),
340
        ENTRY("1.60", pentium_m_n725),
341
        ENTRY("1.70", pentium_m_n735),
342
        ENTRY("1.80", pentium_m_n745),
343
        ENTRY("2.00", pentium_m_n755),
344
        ENTRY("2.10", pentium_m_n765),
345
#undef ENTRY
346
};
347
348
struct est_cpu {
349
        const char *brand_prefix;
350
        const char *brand_suffix;
351
        const struct fqlist *list;
352
        int n;
353
};
354
355
static const struct est_cpu est_cpus[] = {
356
        {
357
                "Intel(R) Pentium(R) M processor ", "MHz",
358
                pentium_m,
359
                (int)(sizeof(pentium_m) / sizeof(pentium_m[0]))
360
        },
361
        {
362
                "Intel(R) Pentium(R) M processor ", "GHz",
363
                pentium_m_dothan,
364
                (int)(sizeof(pentium_m_dothan) / sizeof(pentium_m_dothan[0]))
365
        },
366
};
367
368
#define NESTCPUS          (int)(sizeof(est_cpus) / sizeof(est_cpus[0]))
369
370
371
#define MSRVALUE(mhz, mv)        ((((mhz) / 100) << 8) | (((mv) - 700) / 16))
372
#define MSR2MHZ(msr)                (int)((((u_int) (msr) >> 8) & 0xff) * 100)
373
#define MSR2MV(msr)                (((int) (msr) & 0xff) * 16 + 700)
374
375
static const struct fqlist *est_fqlist;
376
377
static int        maxfreq;        /* max speed in Mhz */
378
static int        maxvolts;        /* max voltage in mV */
379
static int        curfreq;        /* current speed in Mhz */
380
static int        curvolts;        /* current voltage in mV */
381
#ifdef CONFIG_DVS_EMULATION
382
static int        bochs;                /* true if bochs is active */
383
#endif
384
385
/*
386
 * Set CPU performance
387
 *
388
 * @level: percent of cpu speed
389
 */
390
static int
391
est_setperf(int level)
392
{
393
        int i;
394
        int fq, max_mhz;
395
        u_int msr_lo, msr_hi;
396
397
        max_mhz = est_fqlist->table[0].mhz;
398
        fq = max_mhz * level / 100;
399
400
        for (i = est_fqlist->n - 1; i > 0; i--)
401
                if (est_fqlist->table[i].mhz >= fq)
402
                        break;
403
404
        if (est_fqlist->table[i].mhz == curfreq)
405
                return 0;
406
407
        curfreq = est_fqlist->table[i].mhz;
408
        curvolts = est_fqlist->table[i].mv;
409
#ifdef DEBUG_EST
410
        DPRINTF(("setperf: %dMHz %dmV\n", curfreq, curvolts));
411
#endif
412
#ifdef CONFIG_DVS_EMULATION
413
        if (bochs)
414
                return 0;
415
#endif
416
        rdmsr(MSR_PERF_CTL, &msr_lo, &msr_hi);
417
        msr_lo = (msr_lo & ~0xffff) |
418
                MSRVALUE(est_fqlist->table[i].mhz, est_fqlist->table[i].mv);
419
        wrmsr(MSR_PERF_CTL, msr_lo, msr_hi);
420
        return 0;
421
}
422
423
/*
424
 * Get CPU performance
425
 */
426
static int
427
est_getperf(void)
428
{
429
        int max_mhz;
430
        int level;
431
432
        max_mhz = est_fqlist->table[0].mhz;
433
        ASSERT(max_mhz);
434
        level = curfreq * 100 / max_mhz;
435
        return level;
436
}
437
438
static void
439
est_getinfo(struct cpufreqinfo *info)
440
{
441
442
        info->maxfreq = maxfreq;
443
        info->maxvolts = maxvolts;
444
        info->freq = curfreq;
445
        info->volts = curvolts;
446
}
447
448
static int
449
est_identify(char *brand_str)
450
{
451
        int i, j, mhz, mv;
452
        size_t len;
453
        const struct est_cpu *cpu;
454
        u_int msr_lo, msr_hi;
455
        char *tag;
456
        const struct fqlist *fql;
457
458
        DPRINTF(("CPU brand: %s\n", brand_str));
459
460
#ifdef CONFIG_DVS_EMULATION
461
        if (bochs) {
462
                msr_lo = 0x1031;
463
                cpu = &est_cpus[0];
464
                est_fqlist = &cpu->list[7];
465
        } else
466
                rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
467
#else
468
        rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
469
#endif
470
        mhz = MSR2MHZ(msr_lo);
471
        mv = MSR2MV(msr_lo);
472
473
#ifdef CONFIG_DVS_EMULATION
474
        if (!bochs) {
475
#endif
476
        /*
477
         * Look for a CPU matching brand_str.
478
         */
479
        for (i = 0; est_fqlist == NULL && i < NESTCPUS; i++) {
480
                cpu = &est_cpus[i];
481
                len = strnlen(cpu->brand_prefix, 48);
482
                if (strncmp(cpu->brand_prefix, brand_str, len) != 0)
483
                        continue;
484
                tag = brand_str + len;
485
                for (j = 0; j < cpu->n; j++) {
486
                        fql = &cpu->list[j];
487
                        len = strnlen(fql->brand_tag, 48);
488
                        if (!strncmp(fql->brand_tag, tag, len) &&
489
                            !strncmp(cpu->brand_suffix, tag + len, 48)) {
490
                                est_fqlist = fql;
491
                                break;
492
                        }
493
                }
494
        }
495
        if (est_fqlist == NULL) {
496
                DPRINTF(("Unknown EST cpu, no changes possible\n"));
497
                return ENXIO;
498
        }
499
500
        /*
501
         * Check that the current operating point is in our list.
502
         */
503
        for (i = est_fqlist->n - 1; i >= 0; i--)
504
                if (est_fqlist->table[i].mhz == mhz)
505
                        break;
506
        if (i < 0) {
507
                DPRINTF((" (not in table)\n"));
508
                return ENXIO;
509
        }
510
#ifdef CONFIG_DVS_EMULATION
511
        }
512
#endif
513
        /*
514
         * Store current state
515
         */
516
        maxfreq = est_fqlist->table[0].mhz;
517
        maxvolts = est_fqlist->table[0].mv;
518
        curfreq = mhz;
519
        curvolts = mv;
520
        return 0;
521
}
522
523
static int
524
est_probe(struct driver *self)
525
{
526
        u_int regs[4];
527
        char brand_str[49];
528
        char *p, *q;
529
        u_int cpu_id;
530
531
#ifdef CONFIG_DVS_EMULATION
532
        bochs = 0;
533
        if (bus_read_8(0xe9) == 0xe9) {
534
                /*
535
                 * Detect Bochs. Fake the cpuid value.
536
                 */
537
                bochs = 1;
538
                cpu_id = 0x6d6;
539
                strlcpy(brand_str,
540
                        "Intel(R) Pentium(R) M processor 1600MHz",
541
                        sizeof(brand_str));
542
                DPRINTF(("CPU ID: %08x\n", cpu_id));
543
                return est_identify(brand_str);
544
        }
545
#endif
546
        /*
547
         * Check enhanced speed step capability
548
         */
549
        cpuid(1, regs);
550
        cpu_id = regs[0];
551
        DPRINTF(("CPU ID: %08x\n", cpu_id));
552
        if ((regs[2] & 0x80) == 0) {
553
                DPRINTF(("cpu: clock control not supported\n"));
554
                return ENXIO;
555
        }
556
557
        /*
558
         * Get CPU brand string
559
         */
560
        cpuid(0x80000002, regs);
561
        memcpy(brand_str, regs, sizeof(regs));
562
        cpuid(0x80000003, regs);
563
        memcpy(brand_str + 16, regs, sizeof(regs));
564
        cpuid(0x80000004, regs);
565
        memcpy(brand_str + 32, regs, sizeof(regs));
566
567
        /* Store string with lef-align */
568
        p = q = brand_str;
569
        while (*p == ' ')
570
                p++;
571
        while (*p)
572
                *q++ = *p++;
573
        *q = '\0';
574
575
        return est_identify(brand_str);
576
}
577
578
static int
579
est_init(struct driver *self)
580
{
581
#ifdef DEBUG
582
        int i;
583
#endif
584
585
        cpufreq_attach(&est_ops);
586
587
#ifdef DEBUG
588
        printf("Enhanced SpeedStep %d MHz (%d mV)\n", curfreq, curvolts);
589
        printf("Speeds: ");
590
        for (i = 0; i < est_fqlist->n; i++)
591
                printf("%d%s", est_fqlist->table[i].mhz,
592
                       i < est_fqlist->n - 1 ? ", " : " MHz\n");
593
#endif
594
        return 0;
595
}