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
30 /*
31 * This is a driver for Intel's Enhanced SpeedStep, as implemented in
32 * Pentium M processors.
33 *
34 * Reference documentation:
35 *
36 * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
37 * System Programming Guide.
38 * Section 13.14, Enhanced Intel SpeedStep technology.
39 * Table B-2, MSRs in Pentium M Processors.
40 * http://www.intel.com/design/pentium4/manuals/245472.htm
41 *
42 * - Intel Pentium M Processor Datasheet.
43 * Table 5, Voltage and Current Specifications.
44 * http://www.intel.com/design/mobile/datashts/252612.htm
45 *
46 * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
47 * Table 3-4, Voltage and Current Specifications.
48 * http://www.intel.com/design/mobile/datashts/302189.htm
49 *
50 * - Linux cpufreq patches, speedstep-centrino.c.
51 * Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
52 * http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
53 */
54
55
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/sysctl.h>
59
60 #include <machine/cpu.h>
61 #include <machine/cpufunc.h>
62 #include <machine/specialreg.h>
63
64
65 struct fq_info {
66 u_short mhz;
67 u_short mv;
68 };
69
70 /* Ultra Low Voltage Intel Pentium M processor 900 MHz */
71 static const struct fq_info pentium_m_900[] = {
72 { 900, 1004 },
73 { 800, 988 },
74 { 600, 844 },
75 };
76
77 /* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
78 static const struct fq_info pentium_m_1000[] = {
79 { 1000, 1004 },
80 { 900, 988 },
81 { 800, 972 },
82 { 600, 844 },
83 };
84
85 /* Low Voltage Intel Pentium M processor 1.10 GHz */
86 static const struct fq_info pentium_m_1100[] = {
87 { 1100, 1180 },
88 { 1000, 1164 },
89 { 900, 1100 },
90 { 800, 1020 },
91 { 600, 956 },
92 };
93
94 /* Low Voltage Intel Pentium M processor 1.20 GHz */
95 static const struct fq_info pentium_m_1200[] = {
96 { 1200, 1180 },
97 { 1100, 1164 },
98 { 1000, 1100 },
99 { 900, 1020 },
100 { 800, 1004 },
101 { 600, 956 },
102 };
103
104 /* Intel Pentium M processor 1.30 GHz */
105 static const struct fq_info pentium_m_1300[] = {
106 { 1300, 1388 },
107 { 1200, 1356 },
108 { 1000, 1292 },
109 { 800, 1260 },
110 { 600, 956 },
111 };
112
113 /* Intel Pentium M processor 1.40 GHz */
114 static const struct fq_info pentium_m_1400[] = {
115 { 1400, 1484 },
116 { 1200, 1436 },
117 { 1000, 1308 },
118 { 800, 1180 },
119 { 600, 956 }
120 };
121
122 /* Intel Pentium M processor 1.50 GHz */
123 static const struct fq_info pentium_m_1500[] = {
124 { 1500, 1484 },
125 { 1400, 1452 },
126 { 1200, 1356 },
127 { 1000, 1228 },
128 { 800, 1116 },
129 { 600, 956 }
130 };
131
132 /* Intel Pentium M processor 1.60 GHz */
133 static const struct fq_info pentium_m_1600[] = {
134 { 1600, 1484 },
135 { 1400, 1420 },
136 { 1200, 1276 },
137 { 1000, 1164 },
138 { 800, 1036 },
139 { 600, 956 }
140 };
141
142 /* Intel Pentium M processor 1.70 GHz */
143 static const struct fq_info pentium_m_1700[] = {
144 { 1700, 1484 },
145 { 1400, 1308 },
146 { 1200, 1228 },
147 { 1000, 1116 },
148 { 800, 1004 },
149 { 600, 956 }
150 };
151
152
153 /* Intel Pentium M processor 723 1.0 GHz */
154 static const struct fq_info pentium_m_n723[] = {
155 { 1000, 940 },
156 { 900, 908 },
157 { 800, 876 },
158 { 600, 812 }
159 };
160
161 /* Intel Pentium M processor 733 1.1 GHz */
162 static const struct fq_info pentium_m_n733[] = {
163 { 1100, 940 },
164 { 1000, 924 },
165 { 900, 892 },
166 { 800, 876 },
167 { 600, 812 }
168 };
169
170 /* Intel Pentium M processor 753 1.2 GHz */
171 static const struct fq_info pentium_m_n753[] = {
172 { 1200, 940 },
173 { 1100, 924 },
174 { 1000, 908 },
175 { 900, 876 },
176 { 800, 860 },
177 { 600, 812 }
178 };
179
180 /* Intel Pentium M processor 738 1.4 GHz */
181 static const struct fq_info pentium_m_n738[] = {
182 { 1400, 1116 },
183 { 1300, 1116 },
184 { 1200, 1100 },
185 { 1100, 1068 },
186 { 1000, 1052 },
187 { 900, 1036 },
188 { 800, 1020 },
189 { 600, 988 }
190 };
191
192 #if 0
193 /* Intel Pentium M processor 758 1.5 GHz */
194 static const struct fq_info pentium_m_n758[] = {
195 { 1500, 1116 },
196 { 1400, 1116 },
197 { 1300, 1100 },
198 { 1200, 1084 },
199 { 1100, 1068 },
200 { 1000, 1052 },
201 { 900, 1036 },
202 { 800, 1020 },
203 { 600, 988 }
204 };
205 #endif
206
207 /* Intel Pentium M processor 715 1.5 GHz */
208 static const struct fq_info pentium_m_n715[] = {
209 { 1500, 1340 },
210 { 1200, 1228 },
211 { 1000, 1148 },
212 { 800, 1068 },
213 { 600, 988 }
214 };
215
216 /* Intel Pentium M processor 725 1.6 GHz */
217 static const struct fq_info pentium_m_n725[] = {
218 { 1600, 1340 },
219 { 1400, 1276 },
220 { 1200, 1212 },
221 { 1000, 1132 },
222 { 800, 1068 },
223 { 600, 988 }
224 };
225
226 /* Intel Pentium M processor 735 1.7 GHz */
227 static const struct fq_info pentium_m_n735[] = {
228 { 1700, 1340 },
229 { 1400, 1244 },
230 { 1200, 1180 },
231 { 1000, 1116 },
232 { 800, 1052 },
233 { 600, 988 }
234 };
235
236 /* Intel Pentium M processor 745 1.8 GHz */
237 static const struct fq_info pentium_m_n745[] = {
238 { 1800, 1340 },
239 { 1600, 1292 },
240 { 1400, 1228 },
241 { 1200, 1164 },
242 { 1000, 1116 },
243 { 800, 1052 },
244 { 600, 988 }
245 };
246
247 /* Intel Pentium M processor 755 2.0 GHz */
248 static const struct fq_info pentium_m_n755[] = {
249 { 2000, 1340 },
250 { 1800, 1292 },
251 { 1600, 1244 },
252 { 1400, 1196 },
253 { 1200, 1148 },
254 { 1000, 1100 },
255 { 800, 1052 },
256 { 600, 988 }
257 };
258
259 /* Intel Pentium M processor 765 2.1 GHz */
260 static const struct fq_info pentium_m_n765[] = {
261 { 2100, 1340 },
262 { 1800, 1276 },
263 { 1600, 1228 },
264 { 1400, 1180 },
265 { 1200, 1132 },
266 { 1000, 1084 },
267 { 800, 1036 },
268 { 600, 988 }
269 };
270
271 struct fqlist {
272 const char *brand_tag;
273 const struct fq_info *table;
274 u_int n;
275 };
276
277 static const struct fqlist pentium_m[] = {
278 #define ENTRY(s, v) { s, v, sizeof(v) / sizeof((v)[0]) }
279 ENTRY(" 900", pentium_m_900),
280 ENTRY("1000", pentium_m_1000),
281 ENTRY("1100", pentium_m_1100),
282 ENTRY("1200", pentium_m_1200),
283 ENTRY("1300", pentium_m_1300),
284 ENTRY("1400", pentium_m_1400),
285 ENTRY("1500", pentium_m_1500),
286 ENTRY("1600", pentium_m_1600),
287 ENTRY("1700", pentium_m_1700),
288 #undef ENTRY
289 };
290
291 static const struct fqlist pentium_m_dothan[] = {
292 #define ENTRY(s, v) { s, v, sizeof(v) / sizeof((v)[0]) }
293 ENTRY("1.00", pentium_m_n723),
294 ENTRY("1.10", pentium_m_n733),
295 ENTRY("1.20", pentium_m_n753),
296 ENTRY("1.40", pentium_m_n738),
297 #if 0
298 ENTRY("1.50", pentium_m_n758),
299 #endif
300 ENTRY("1.50", pentium_m_n715),
301 ENTRY("1.60", pentium_m_n725),
302 ENTRY("1.70", pentium_m_n735),
303 ENTRY("1.80", pentium_m_n745),
304 ENTRY("2.00", pentium_m_n755),
305 ENTRY("2.10", pentium_m_n765),
306 #undef ENTRY
307 };
308
309 struct est_cpu {
310 const char *brand_prefix;
311 const char *brand_suffix;
312 const struct fqlist *list;
313 int n;
314 };
315
316 static const struct est_cpu est_cpus[] = {
317 {
318 "Intel(R) Pentium(R) M processor ", "MHz",
319 pentium_m,
320 (sizeof(pentium_m) / sizeof(pentium_m[0]))
321 },
322 {
323 "Intel(R) Pentium(R) M processor ", "GHz",
324 pentium_m_dothan,
325 (sizeof(pentium_m_dothan) / sizeof(pentium_m_dothan[0]))
326 },
327 };
328
329 #define NESTCPUS (sizeof(est_cpus) / sizeof(est_cpus[0]))
330
331
332 #define MSRVALUE(mhz, mv) ((((mhz) / 100) << 8) | (((mv) - 700) / 16))
333 #define MSR2MHZ(msr) ((((int) (msr) >> 8) & 0xff) * 100)
334 #define MSR2MV(msr) (((int) (msr) & 0xff) * 16 + 700)
335
336 static const struct fqlist *est_fqlist;
337
338 extern int setperf_prio;
339 extern int perflevel;
340
341 void
est_init(const char * cpu_device)342 est_init(const char *cpu_device)
343 {
344 int i, j, n, mhz, mv;
345 const struct est_cpu *cpu;
346 u_int64_t msr;
347 char *tag;
348 const struct fqlist *fql;
349 extern char cpu_brandstr[];
350 int low, high;
351
352 if (setperf_prio > 3)
353 return;
354
355 if ((cpu_ecxfeature & CPUIDECX_EST) == 0)
356 return;
357
358 msr = rdmsr(MSR_PERF_STATUS);
359 mhz = MSR2MHZ(msr);
360 mv = MSR2MV(msr);
361 printf("%s: Enhanced SpeedStep %d MHz (%d mV)",
362 cpu_device, mhz, mv);
363
364 /*
365 * Look for a CPU matching cpu_brandstr.
366 */
367 for (i = 0; est_fqlist == NULL && i < NESTCPUS; i++) {
368 cpu = &est_cpus[i];
369 n = strlen(cpu->brand_prefix);
370 if (strncmp(cpu->brand_prefix, cpu_brandstr, n) != 0)
371 continue;
372 tag = cpu_brandstr + n;
373 for (j = 0; j < cpu->n; j++) {
374 fql = &cpu->list[j];
375 n = strlen(fql->brand_tag);
376 if (!strncmp(fql->brand_tag, tag, n) &&
377 !strcmp(cpu->brand_suffix, tag + n)) {
378 est_fqlist = fql;
379 break;
380 }
381 }
382 }
383 if (est_fqlist == NULL) {
384 printf(": unknown EST cpu, no changes possible\n");
385 return;
386 }
387
388 /*
389 * Check that the current operating point is in our list.
390 */
391 for (i = est_fqlist->n - 1; i >= 0; i--)
392 if (est_fqlist->table[i].mhz == mhz &&
393 est_fqlist->table[i].mv == mv)
394 break;
395 if (i < 0) {
396 printf(" (not in table)\n");
397 return;
398 }
399 low = est_fqlist->table[est_fqlist->n - 1].mhz;
400 high = est_fqlist->table[0].mhz;
401 perflevel = (mhz - low) * 100 / (high - low);
402
403 /*
404 * OK, tell the user the available frequencies.
405 */
406 printf(": speeds: ");
407 for (i = 0; i < est_fqlist->n; i++)
408 printf("%d%s", est_fqlist->table[i].mhz,
409 i < est_fqlist->n - 1 ? ", " : " MHz\n");
410
411 cpu_setperf = est_setperf;
412 cpu_cpuspeed = est_cpuspeed;
413 setperf_prio = 3;
414 }
415
416 int
est_setperf(int level)417 est_setperf(int level)
418 {
419 int low, high, i, fq;
420 uint64_t msr;
421
422 if (est_fqlist == NULL)
423 return (EOPNOTSUPP);
424
425 low = est_fqlist->table[est_fqlist->n - 1].mhz;
426 high = est_fqlist->table[0].mhz;
427 fq = low + (high - low) * level / 100;
428
429 for (i = est_fqlist->n - 1; i > 0; i--)
430 if (est_fqlist->table[i].mhz >= fq)
431 break;
432 msr = (rdmsr(MSR_PERF_CTL) & ~0xffffULL) |
433 MSRVALUE(est_fqlist->table[i].mhz, est_fqlist->table[i].mv);
434 wrmsr(MSR_PERF_CTL, msr);
435 pentium_mhz = est_fqlist->table[i].mhz;
436
437 return (0);
438 }
439
440
441 int
est_cpuspeed(int * freq)442 est_cpuspeed(int *freq)
443 {
444 *freq = MSR2MHZ(rdmsr(MSR_PERF_STATUS));
445 return (0);
446 }
447