Annotation of sys/arch/amd64/amd64/est.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: est.c,v 1.5 2007/06/07 11:20:58 dim 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: #define CPUVENDOR_INTEL 0
65: #define CPUVENDOR_VIA 8
66:
67:
68: /* Convert MHz and mV into IDs for passing to the MSR. */
69: #define ID16(MHz, mV, bus_clk) \
70: ((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
71:
72: /* Possible bus speeds (multiplied by 100 for rounding) */
73: #define BUS100 10000
74: #define BUS133 13333
75: #define BUS166 16667
76: #define BUS200 20000
77: #define BUS266 26667
78: #define BUS333 33333
79:
80: #define MSR2MHZ(msr, bus) \
81: (((((int) (msr) >> 8) & 0xff) * (bus) + 50) / 100)
82: #define MSR2MV(msr) \
83: (((int) (msr) & 0xff) * 16 + 700)
84:
85: struct fqlist {
86: int vendor: 5;
87: unsigned bus_clk : 1;
88: unsigned n : 5;
89: const u_int16_t *table;
90: };
91:
92: static const struct fqlist *est_fqlist;
93:
94: static u_int16_t fake_table[3];
95: static struct fqlist fake_fqlist;
96:
97: extern int setperf_prio;
98: extern int perflevel;
99:
100: int bus_clock;
101:
102: void p4_get_bus_clock(struct cpu_info *);
103: void p3_get_bus_clock(struct cpu_info *);
104:
105: void
106: p4_get_bus_clock(struct cpu_info *ci)
107: {
108: u_int64_t msr;
109: int model, bus;
110:
111: model = (ci->ci_signature >> 4) & 15;
112: msr = rdmsr(MSR_EBC_FREQUENCY_ID);
113: if (model < 2) {
114: bus = (msr >> 21) & 0x7;
115: switch (bus) {
116: case 0:
117: bus_clock = BUS100;
118: break;
119: case 1:
120: bus_clock = BUS133;
121: break;
122: default:
123: printf("%s: unknown Pentium 4 (model %d) "
124: "EBC_FREQUENCY_ID value %d\n",
125: ci->ci_dev->dv_xname, model, bus);
126: break;
127: }
128: } else {
129: bus = (msr >> 16) & 0x7;
130: switch (bus) {
131: case 0:
132: bus_clock = (model == 2) ? BUS100 : BUS266;
133: break;
134: case 1:
135: bus_clock = BUS133;
136: break;
137: case 2:
138: bus_clock = BUS200;
139: break;
140: case 3:
141: bus_clock = BUS166;
142: break;
143: default:
144: printf("%s: unknown Pentium 4 (model %d) "
145: "EBC_FREQUENCY_ID value %d\n",
146: ci->ci_dev->dv_xname, model, bus);
147: break;
148: }
149: }
150: }
151:
152: void
153: p3_get_bus_clock(struct cpu_info *ci)
154: {
155: u_int64_t msr;
156: int model, bus;
157:
158: model = (ci->ci_signature >> 4) & 15;
159: switch (model) {
160: case 0xe: /* Core Duo/Solo */
161: case 0xf: /* Core Xeon */
162: msr = rdmsr(MSR_FSB_FREQ);
163: bus = (msr >> 0) & 0x7;
164: switch (bus) {
165: case 5:
166: bus_clock = BUS100;
167: break;
168: case 1:
169: bus_clock = BUS133;
170: break;
171: case 3:
172: bus_clock = BUS166;
173: break;
174: case 2:
175: bus_clock = BUS200;
176: break;
177: case 0:
178: bus_clock = BUS266;
179: break;
180: case 4:
181: bus_clock = BUS333;
182: break;
183: default:
184: printf("%s: unknown Core FSB_FREQ value %d",
185: ci->ci_dev->dv_xname, bus);
186: break;
187: }
188: break;
189: default:
190: printf("%s: unknown i686 model %d, can't get bus clock",
191: ci->ci_dev->dv_xname, model);
192: }
193: }
194:
195: void
196: est_init(struct cpu_info *ci)
197: {
198: const char *cpu_device = ci->ci_dev->dv_xname;
199: int vendor = -1;
200: int i, mhz, mv, low, high, family;
201: u_int64_t msr;
202: u_int16_t idhi, idlo, cur;
203: u_int8_t crhi, crlo, crcur;
204:
205: if (setperf_prio > 3)
206: return;
207:
208: family = (ci->ci_signature >> 8) & 15;
209: if (family == 0xf) {
210: p4_get_bus_clock(ci);
211: } else if (family == 6) {
212: p3_get_bus_clock(ci);
213: }
214: if (bus_clock == 0) {
215: printf("%s: EST: unknown system bus clock\n", cpu_device);
216: return;
217: }
218:
219: msr = rdmsr(MSR_PERF_STATUS);
220: idhi = (msr >> 32) & 0xffff;
221: idlo = (msr >> 48) & 0xffff;
222: cur = msr & 0xffff;
223: crhi = (idhi >> 8) & 0xff;
224: crlo = (idlo >> 8) & 0xff;
225: crcur = (cur >> 8) & 0xff;
226: if (crlo == 0 || crhi == crlo) {
227: /*
228: * Don't complain about these cases, and silently disable EST:
229: * - A lowest clock ratio of 0, which seems to happen on all
230: * Pentium 4's that report EST.
231: * - An equal highest and lowest clock ratio, which happens on
232: * at least the Core 2 Duo X6800, maybe on newer models too.
233: */
234: return;
235: }
236: if (crhi == 0 || crcur == 0 || crlo > crhi ||
237: crcur < crlo || crcur > crhi) {
238: /*
239: * Do complain about other weirdness, because we first want to
240: * know about it, before we decide what to do with it.
241: */
242: printf("%s: EST: strange msr value 0x%016llx\n",
243: cpu_device, msr);
244: return;
245: }
246: if (est_fqlist == NULL) {
247: printf("%s: unknown Enhanced SpeedStep CPU, msr 0x%016llx\n",
248: cpu_device, msr);
249:
250: /*
251: * Generate a fake table with the power states we know.
252: */
253: fake_table[0] = idhi;
254: if (cur == idhi || cur == idlo) {
255: printf("%s: using only highest and lowest power "
256: "states\n", cpu_device);
257:
258: fake_table[1] = idlo;
259: fake_fqlist.n = 2;
260: } else {
261: printf("%s: using only highest, current and lowest "
262: "power states\n", cpu_device);
263:
264: fake_table[1] = cur;
265: fake_table[2] = idlo;
266: fake_fqlist.n = 3;
267: }
268: fake_fqlist.vendor = vendor;
269: fake_fqlist.table = fake_table;
270: est_fqlist = &fake_fqlist;
271: }
272:
273: mhz = MSR2MHZ(cur, bus_clock);
274: mv = MSR2MV(cur);
275: printf("%s: Enhanced SpeedStep %d MHz (%d mV)", cpu_device, mhz, mv);
276:
277: low = MSR2MHZ(est_fqlist->table[est_fqlist->n - 1], bus_clock);
278: high = MSR2MHZ(est_fqlist->table[0], bus_clock);
279: perflevel = (mhz - low) * 100 / (high - low);
280:
281: /*
282: * OK, tell the user the available frequencies.
283: */
284: printf(": speeds: ");
285: for (i = 0; i < est_fqlist->n; i++)
286: printf("%d%s", MSR2MHZ(est_fqlist->table[i], bus_clock),
287: i < est_fqlist->n - 1 ? ", " : " MHz\n");
288:
289: cpu_setperf = est_setperf;
290: setperf_prio = 3;
291: }
292:
293: void
294: est_setperf(int level)
295: {
296: int i;
297: uint64_t msr;
298:
299: if (est_fqlist == NULL)
300: return;
301:
302: i = ((level * est_fqlist->n) + 1) / 101;
303: if (i >= est_fqlist->n)
304: i = est_fqlist->n - 1;
305: i = est_fqlist->n - 1 - i;
306:
307: msr = rdmsr(MSR_PERF_CTL);
308: msr &= ~0xffffULL;
309: msr |= est_fqlist->table[i];
310: wrmsr(MSR_PERF_CTL, msr);
311: cpuspeed = MSR2MHZ(est_fqlist->table[i], bus_clock);
312: }
CVSweb