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