/* $OpenBSD: p4tcc.c,v 1.14 2007/08/03 20:36:02 deraadt Exp $ */ /* * Copyright (c) 2003 Ted Unangst * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Restrict power consumption by using thermal control circuit. * This operates independently of speedstep. * Found on Pentium 4 and later models (feature TM). * * References: * Intel Developer's manual v.3 #245472-012 * * On some models, the cpu can hang if it's running at a slow speed. * Workarounds included below. */ #include #include #include #include #include static struct { u_short level; u_short reg; } tcc[] = { { 88, 0 }, { 75, 7 }, { 63, 6 }, { 50, 5 }, { 38, 4 }, { 25, 3 }, { 13, 2 }, { 0, 1 } }; #define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) extern int setperf_prio; int p4tcc_level; int p4tcc_cpuspeed(int *); void p4tcc_init(int family, int step) { if (setperf_prio > 1) return; switch (family) { case 0xf: /* Pentium 4 */ switch (step) { case 0x22: /* errata O50 P44 and Z21 */ case 0x24: case 0x25: case 0x27: case 0x29: /* hang with 12.5 */ tcc[TCC_LEVELS - 1].reg = 2; break; case 0x07: /* errata N44 and P18 */ case 0x0a: case 0x12: case 0x13: /* hang at 12.5 and 25 */ tcc[TCC_LEVELS - 1].reg = 3; tcc[TCC_LEVELS - 2].reg = 3; break; } break; } p4tcc_level = tcc[0].level; cpu_setperf = p4tcc_setperf; cpu_cpuspeed = p4tcc_cpuspeed; setperf_prio = 1; } int p4tcc_cpuspeed(int *speed) { *speed = cpuspeed * (p4tcc_level + 12) / 100; return 0; } void p4tcc_setperf(int level) { int i; uint64_t msreg, vet; for (i = 0; i < TCC_LEVELS; i++) { if (level >= tcc[i].level) break; } msreg = rdmsr(MSR_THERM_CONTROL); msreg &= ~0x1e; /* bit 0 reserved */ if (tcc[i].reg != 0) /* enable it */ msreg |= tcc[i].reg << 1 | 1 << 4; wrmsr(MSR_THERM_CONTROL, msreg); vet = rdmsr(MSR_THERM_CONTROL); if ((vet & 0x1e) != (msreg & 0x1e)) printf("p4_tcc: cpu did not honor request\n"); else p4tcc_level = tcc[i].level; }