Annotation of sys/arch/amd64/amd64/est.c, Revision 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