Annotation of prex/dev/i386/i386/cpu.c, Revision 1.1.1.1
1.1 nbrk 1: /* $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: * cpu.c - Processor driver for Intel CPU
55: */
56:
57: #include <driver.h>
58: #include <sys/ioctl.h>
59: #include <cpufunc.h>
60:
61: /* #define DEBUG_CPU 1 */
62:
63: #ifdef DEBUG
64: #define DPRINTF(a) printf a
65: #else
66: #define DPRINTF(a)
67: #endif
68:
69: /* Status/control registers (from the IA-32 System Programming Guide). */
70: #define MSR_PERF_STATUS 0x198
71: #define MSR_PERF_CTL 0x199
72:
73: /* Register and bit for enabling SpeedStep. */
74: #define MSR_MISC_ENABLE 0x1a0
75: #define MSR_SS_ENABLE (1<<16)
76:
77: static int cpu_ioctl(device_t dev, u_long cmd, void *arg);
78: static int cpu_init(void);
79:
80: /*
81: * Driver structure
82: */
83: struct driver cpu_drv = {
84: /* name */ "Processor",
85: /* order */ 1,
86: /* init */ cpu_init,
87: };
88:
89: static struct devio cpu_io = {
90: /* open */ NULL,
91: /* close */ NULL,
92: /* read */ NULL,
93: /* write */ NULL,
94: /* ioctl */ cpu_ioctl,
95: /* event */ NULL,
96: };
97:
98: static device_t cpu_dev; /* Device object */
99:
100: /*
101: * Frequency tables
102: */
103: struct fq_info {
104: u_short mhz;
105: u_short mv;
106: };
107:
108: /* Ultra Low Voltage Intel Pentium M processor 900 MHz */
109: static const struct fq_info pentium_m_900[] = {
110: { 900, 1004 },
111: { 800, 988 },
112: { 600, 844 },
113: };
114:
115: /* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
116: static const struct fq_info pentium_m_1000[] = {
117: { 1000, 1004 },
118: { 900, 988 },
119: { 800, 972 },
120: { 600, 844 },
121: };
122:
123: /* Low Voltage Intel Pentium M processor 1.10 GHz */
124: static const struct fq_info pentium_m_1100[] = {
125: { 1100, 1180 },
126: { 1000, 1164 },
127: { 900, 1100 },
128: { 800, 1020 },
129: { 600, 956 },
130: };
131:
132: /* Low Voltage Intel Pentium M processor 1.20 GHz */
133: static const struct fq_info pentium_m_1200[] = {
134: { 1200, 1180 },
135: { 1100, 1164 },
136: { 1000, 1100 },
137: { 900, 1020 },
138: { 800, 1004 },
139: { 600, 956 },
140: };
141:
142: /* Intel Pentium M processor 1.30 GHz */
143: static const struct fq_info pentium_m_1300[] = {
144: { 1300, 1388 },
145: { 1200, 1356 },
146: { 1000, 1292 },
147: { 800, 1260 },
148: { 600, 956 },
149: };
150:
151: /* Intel Pentium M processor 1.40 GHz */
152: static const struct fq_info pentium_m_1400[] = {
153: { 1400, 1484 },
154: { 1200, 1436 },
155: { 1000, 1308 },
156: { 800, 1180 },
157: { 600, 956 }
158: };
159:
160: /* Intel Pentium M processor 1.50 GHz */
161: static const struct fq_info pentium_m_1500[] = {
162: { 1500, 1484 },
163: { 1400, 1452 },
164: { 1200, 1356 },
165: { 1000, 1228 },
166: { 800, 1116 },
167: { 600, 956 }
168: };
169:
170: /* Intel Pentium M processor 1.60 GHz */
171: static const struct fq_info pentium_m_1600[] = {
172: { 1600, 1484 },
173: { 1400, 1420 },
174: { 1200, 1276 },
175: { 1000, 1164 },
176: { 800, 1036 },
177: { 600, 956 }
178: };
179:
180: /* Intel Pentium M processor 1.70 GHz */
181: static const struct fq_info pentium_m_1700[] = {
182: { 1700, 1484 },
183: { 1400, 1308 },
184: { 1200, 1228 },
185: { 1000, 1116 },
186: { 800, 1004 },
187: { 600, 956 }
188: };
189:
190:
191: /* Intel Pentium M processor 723 1.0 GHz */
192: static const struct fq_info pentium_m_n723[] = {
193: { 1000, 940 },
194: { 900, 908 },
195: { 800, 876 },
196: { 600, 812 }
197: };
198:
199: /* Intel Pentium M processor 733 1.1 GHz */
200: static const struct fq_info pentium_m_n733[] = {
201: { 1100, 940 },
202: { 1000, 924 },
203: { 900, 892 },
204: { 800, 876 },
205: { 600, 812 }
206: };
207:
208: /* Intel Pentium M processor 753 1.2 GHz */
209: static const struct fq_info pentium_m_n753[] = {
210: { 1200, 940 },
211: { 1100, 924 },
212: { 1000, 908 },
213: { 900, 876 },
214: { 800, 860 },
215: { 600, 812 }
216: };
217:
218: /* Intel Pentium M processor 738 1.4 GHz */
219: static const struct fq_info pentium_m_n738[] = {
220: { 1400, 1116 },
221: { 1300, 1116 },
222: { 1200, 1100 },
223: { 1100, 1068 },
224: { 1000, 1052 },
225: { 900, 1036 },
226: { 800, 1020 },
227: { 600, 988 }
228: };
229:
230: #if 0
231: /* Intel Pentium M processor 758 1.5 GHz */
232: static const struct fq_info pentium_m_n758[] = {
233: { 1500, 1116 },
234: { 1400, 1116 },
235: { 1300, 1100 },
236: { 1200, 1084 },
237: { 1100, 1068 },
238: { 1000, 1052 },
239: { 900, 1036 },
240: { 800, 1020 },
241: { 600, 988 }
242: };
243: #endif
244:
245: /* Intel Pentium M processor 715 1.5 GHz */
246: static const struct fq_info pentium_m_n715[] = {
247: { 1500, 1340 },
248: { 1200, 1228 },
249: { 1000, 1148 },
250: { 800, 1068 },
251: { 600, 988 }
252: };
253:
254: /* Intel Pentium M processor 725 1.6 GHz */
255: static const struct fq_info pentium_m_n725[] = {
256: { 1600, 1340 },
257: { 1400, 1276 },
258: { 1200, 1212 },
259: { 1000, 1132 },
260: { 800, 1068 },
261: { 600, 988 }
262: };
263:
264: /* Intel Pentium M processor 735 1.7 GHz */
265: static const struct fq_info pentium_m_n735[] = {
266: { 1700, 1340 },
267: { 1400, 1244 },
268: { 1200, 1180 },
269: { 1000, 1116 },
270: { 800, 1052 },
271: { 600, 988 }
272: };
273:
274: /* Intel Pentium M processor 745 1.8 GHz */
275: static const struct fq_info pentium_m_n745[] = {
276: { 1800, 1340 },
277: { 1600, 1292 },
278: { 1400, 1228 },
279: { 1200, 1164 },
280: { 1000, 1116 },
281: { 800, 1052 },
282: { 600, 988 }
283: };
284:
285: /* Intel Pentium M processor 755 2.0 GHz */
286: static const struct fq_info pentium_m_n755[] = {
287: { 2000, 1340 },
288: { 1800, 1292 },
289: { 1600, 1244 },
290: { 1400, 1196 },
291: { 1200, 1148 },
292: { 1000, 1100 },
293: { 800, 1052 },
294: { 600, 988 }
295: };
296:
297: /* Intel Pentium M processor 765 2.1 GHz */
298: static const struct fq_info pentium_m_n765[] = {
299: { 2100, 1340 },
300: { 1800, 1276 },
301: { 1600, 1228 },
302: { 1400, 1180 },
303: { 1200, 1132 },
304: { 1000, 1084 },
305: { 800, 1036 },
306: { 600, 988 }
307: };
308:
309: struct fqlist {
310: const char *brand_tag;
311: const struct fq_info *table;
312: int n;
313: };
314:
315: static const struct fqlist pentium_m[] = {
316: #define ENTRY(s, v) { s, v, sizeof(v) / sizeof((v)[0]) }
317: ENTRY(" 900", pentium_m_900),
318: ENTRY("1000", pentium_m_1000),
319: ENTRY("1100", pentium_m_1100),
320: ENTRY("1200", pentium_m_1200),
321: ENTRY("1300", pentium_m_1300),
322: ENTRY("1400", pentium_m_1400),
323: ENTRY("1500", pentium_m_1500),
324: ENTRY("1600", pentium_m_1600),
325: ENTRY("1700", pentium_m_1700),
326: #undef ENTRY
327: };
328:
329: static const struct fqlist pentium_m_dothan[] = {
330: #define ENTRY(s, v) { s, v, sizeof(v) / sizeof((v)[0]) }
331: ENTRY("1.00", pentium_m_n723),
332: ENTRY("1.10", pentium_m_n733),
333: ENTRY("1.20", pentium_m_n753),
334: ENTRY("1.40", pentium_m_n738),
335: #if 0
336: ENTRY("1.50", pentium_m_n758),
337: #endif
338: ENTRY("1.50", pentium_m_n715),
339: ENTRY("1.60", pentium_m_n725),
340: ENTRY("1.70", pentium_m_n735),
341: ENTRY("1.80", pentium_m_n745),
342: ENTRY("2.00", pentium_m_n755),
343: ENTRY("2.10", pentium_m_n765),
344: #undef ENTRY
345: };
346:
347: struct est_cpu {
348: const char *brand_prefix;
349: const char *brand_suffix;
350: const struct fqlist *list;
351: int n;
352: };
353:
354: static const struct est_cpu est_cpus[] = {
355: {
356: "Intel(R) Pentium(R) M processor ", "MHz",
357: pentium_m,
358: (sizeof(pentium_m) / sizeof(pentium_m[0]))
359: },
360: {
361: "Intel(R) Pentium(R) M processor ", "GHz",
362: pentium_m_dothan,
363: (sizeof(pentium_m_dothan) / sizeof(pentium_m_dothan[0]))
364: },
365: };
366:
367: #define NESTCPUS (int)(sizeof(est_cpus) / sizeof(est_cpus[0]))
368:
369:
370: #define MSRVALUE(mhz, mv) ((((mhz) / 100) << 8) | (((mv) - 700) / 16))
371: #define MSR2MHZ(msr) ((((u_int) (msr) >> 8) & 0xff) * 100)
372: #define MSR2MV(msr) (((int) (msr) & 0xff) * 16 + 700)
373:
374: static const struct fqlist *est_fqlist;
375:
376: static struct cpu_info cpu_info;
377: static struct cpu_stat cpu_stat;
378: #ifdef CONFIG_DVS_EMULATION
379: static int bochs;
380: #endif
381:
382: /*
383: * Set CPU performance
384: *
385: * @level: percent of cpu speed
386: */
387: int
388: cpu_setperf(int level)
389: {
390: int i, fq;
391: u_int msr_lo, msr_hi;
392: int max_mhz;
393:
394: ASSERT(cpu_info.clock_ctrl);
395:
396: max_mhz = est_fqlist->table[0].mhz;
397: fq = max_mhz * level / 100;
398:
399: for (i = est_fqlist->n - 1; i > 0; i--)
400: if (est_fqlist->table[i].mhz >= fq)
401: break;
402:
403: if (est_fqlist->table[i].mhz == cpu_stat.speed)
404: return 0;
405:
406: cpu_stat.speed = est_fqlist->table[i].mhz;
407: cpu_stat.power = est_fqlist->table[i].mv;
408: #ifdef DEBUG_CPU
409: DPRINTF(("setperf: %dMHz %dmV\n", cpu_stat.speed, cpu_stat.power));
410: #endif
411: #ifdef CONFIG_DVS_EMULATION
412: if (bochs)
413: return 0;
414: #endif
415: rdmsr(MSR_PERF_CTL, &msr_lo, &msr_hi);
416: msr_lo = (msr_lo & ~0xffff) |
417: MSRVALUE(est_fqlist->table[i].mhz, est_fqlist->table[i].mv);
418: wrmsr(MSR_PERF_CTL, msr_lo, msr_hi);
419: return 0;
420: }
421:
422: /*
423: * Get CPU performance
424: */
425: int
426: cpu_getperf(void)
427: {
428: int max_mhz;
429: int level;
430:
431: ASSERT(cpu_info.clock_ctrl);
432:
433: max_mhz = est_fqlist->table[0].mhz;
434: ASSERT(max_mhz);
435: level = cpu_stat.speed * 100 / max_mhz;
436: return level;
437: }
438:
439: /*
440: * Initialize CPU performance
441: * Return false on error.
442: */
443: int
444: cpu_initperf(void)
445: {
446: int i, j, n, mhz, mv;
447: const struct est_cpu *cpu;
448: u_int msr_lo, msr_hi;
449: char *tag, *brand_str;
450: const struct fqlist *fql;
451:
452: if (!cpu_info.clock_ctrl)
453: return -1;
454:
455: #ifdef CONFIG_DVS_EMULATION
456: if (bochs) {
457: msr_lo = 0x1031;
458: cpu = &est_cpus[0];
459: est_fqlist = &cpu->list[7];
460: } else
461: rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
462: #else
463: rdmsr(MSR_PERF_STATUS, &msr_lo, &msr_hi);
464: #endif
465:
466: mhz = MSR2MHZ(msr_lo);
467: mv = MSR2MV(msr_lo);
468: DPRINTF(("Enhanced SpeedStep %d MHz (%d mV)\n", mhz, mv));
469:
470: #ifdef CONFIG_DVS_EMULATION
471: if (!bochs) {
472: #endif
473: /*
474: * Look for a CPU matching brand_str.
475: */
476: brand_str = cpu_info.name;
477: for (i = 0; est_fqlist == NULL && i < NESTCPUS; i++) {
478: cpu = &est_cpus[i];
479: n = strnlen(cpu->brand_prefix, 48);
480: if (strncmp(cpu->brand_prefix, brand_str, n) != 0)
481: continue;
482: tag = brand_str + n;
483: for (j = 0; j < cpu->n; j++) {
484: fql = &cpu->list[j];
485: n = strnlen(fql->brand_tag, 48);
486: if (!strncmp(fql->brand_tag, tag, n) &&
487: !strncmp(cpu->brand_suffix, tag + n, 48)) {
488: est_fqlist = fql;
489: break;
490: }
491: }
492: }
493: if (est_fqlist == NULL) {
494: DPRINTF(("Unknown EST cpu, no changes possible\n"));
495: cpu_info.clock_ctrl = 0;
496: return -1;
497: }
498:
499: /*
500: * Check that the current operating point is in our list.
501: */
502: for (i = est_fqlist->n - 1; i >= 0; i--)
503: if (est_fqlist->table[i].mhz == mhz)
504: break;
505: if (i < 0) {
506: DPRINTF((" (not in table)\n"));
507: cpu_info.clock_ctrl = 0;
508: return -1;
509: }
510: #ifdef CONFIG_DVS_EMULATION
511: }
512: #endif
513: /*
514: * Store current state
515: */
516: cpu_info.speed = est_fqlist->table[0].mhz;
517: cpu_info.power = est_fqlist->table[0].mv;
518: cpu_stat.speed = mhz;
519: cpu_stat.power = mv;
520:
521: /*
522: * OK, tell the user the available frequencies.
523: */
524: #ifdef DEBUG
525: printf("Speeds: ");
526: for (i = 0; i < est_fqlist->n; i++)
527: printf("%d%s", est_fqlist->table[i].mhz,
528: i < est_fqlist->n - 1 ? ", " : " MHz\n");
529: #endif
530: return 0;
531: }
532:
533: static int
534: cpu_ioctl(device_t dev, u_long cmd, void *arg)
535: {
536:
537: switch (cmd) {
538: case CPUIOC_GET_INFO:
539: if (umem_copyout(&cpu_info, arg, sizeof(cpu_info)))
540: return EFAULT;
541: break;
542: case CPUIOC_GET_STAT:
543: if (umem_copyout(&cpu_stat, arg, sizeof(cpu_stat)))
544: return EFAULT;
545: break;
546: default:
547: return EINVAL;
548: }
549: return 0;
550: }
551:
552: /*
553: * Initialize CPU addon feature
554: *
555: * FIXME: i486 does not support cpuid instruction
556: */
557: static int
558: cpu_init(void)
559: {
560: u_int regs[4];
561: char brand_str[49];
562: char *p, *q;
563:
564: /* Create device object */
565: cpu_dev = device_create(&cpu_io, "cpu", DF_CHR);
566: ASSERT(cpu_dev);
567:
568: #ifdef CONFIG_DVS_EMULATION
569: bochs = 0;
570: if (inb(0xe9) == 0xe9) {
571: /*
572: * Detect Bochs. Fake the cpuid value.
573: */
574: bochs = 1;
575: cpu_info.id = 0x6d6;
576: cpu_info.clock_ctrl = 1;
577: strncpy(cpu_info.name,
578: "Intel(R) Pentium(R) M processor 1600MHz", 50);
579:
580: DPRINTF(("CPU ID: %08x\n", cpu_info.id));
581: DPRINTF(("CPU brand: %s\n", cpu_info.name));
582: return 0;
583: }
584: #endif
585: /*
586: * Check enhanced speed step capability
587: */
588: cpuid(1, regs);
589: cpu_info.id = regs[0];
590: DPRINTF(("CPU ID: %08x\n", regs[0]));
591:
592: if ((regs[2] & 0x80) == 0) {
593: DPRINTF(("cpu: Clock control not supported\n"));
594: cpu_info.clock_ctrl = 0;
595: return 0;
596: }
597: cpu_info.clock_ctrl = 1;
598:
599: /*
600: * Get CPU brand string
601: */
602: cpuid(0x80000002, regs);
603: memcpy(brand_str, regs, sizeof(regs));
604: cpuid(0x80000003, regs);
605: memcpy(brand_str + 16, regs, sizeof(regs));
606: cpuid(0x80000004, regs);
607: memcpy(brand_str + 32, regs, sizeof(regs));
608:
609: /* Store string with lef-align */
610: q = &cpu_info.name[0];
611: p = brand_str;
612: while (*p == ' ')
613: p++;
614: while (*p)
615: *q++ = *p++;
616: *q = '\0';
617:
618: DPRINTF(("CPU brand: %s\n", cpu_info.name));
619: return 0;
620: }
CVSweb