1 /* $NetBSD: odcm.c,v 1.5 2017/06/01 02:45:08 chs Exp $ */ 2 /* $OpenBSD: p4tcc.c,v 1.13 2006/12/20 17:50:40 gwk Exp $ */ 3 4 /* 5 * Copyright (c) 2007 Juan Romero Pardines 6 * Copyright (c) 2003 Ted Unangst 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * On-Demand Clock Modulation driver, to modulate the clock duty cycle 33 * by software. Available on Pentium M and later models (feature TM). 34 * 35 * References: 36 * Intel Developer's manual v.3 #245472-012 37 * 38 * On some models, the cpu can hang if it's running at a slow speed. 39 * Workarounds included below. 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: odcm.c,v 1.5 2017/06/01 02:45:08 chs Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/device.h> 47 #include <sys/module.h> 48 #include <sys/kmem.h> 49 #include <sys/sysctl.h> 50 #include <sys/xcall.h> 51 52 #include <x86/cpuvar.h> 53 #include <x86/cpu_msr.h> 54 #include <x86/specialreg.h> 55 56 #define ODCM_ENABLE (1 << 4) /* Enable bit 4 */ 57 #define ODCM_REGOFFSET 1 58 #define ODCM_MAXSTATES 8 59 60 static struct { 61 int level; 62 int reg; 63 int errata; 64 } state[] = { 65 { .level = 7, .reg = 0, .errata = 0 }, 66 { .level = 6, .reg = 7, .errata = 0 }, 67 { .level = 5, .reg = 6, .errata = 0 }, 68 { .level = 4, .reg = 5, .errata = 0 }, 69 { .level = 3, .reg = 4, .errata = 0 }, 70 { .level = 2, .reg = 3, .errata = 0 }, 71 { .level = 1, .reg = 2, .errata = 0 }, 72 { .level = 0, .reg = 1, .errata = 0 } 73 }; 74 75 struct odcm_softc { 76 device_t sc_dev; 77 struct sysctllog *sc_log; 78 char *sc_names; 79 size_t sc_names_len; 80 int sc_level; 81 int sc_target; 82 int sc_current; 83 }; 84 85 static int odcm_match(device_t, cfdata_t, void *); 86 static void odcm_attach(device_t, device_t, void *); 87 static int odcm_detach(device_t, int); 88 static void odcm_quirks(void); 89 static bool odcm_init(device_t); 90 static bool odcm_sysctl(device_t); 91 static int odcm_state_get(void); 92 static void odcm_state_set(int); 93 static int odcm_sysctl_helper(SYSCTLFN_ARGS); 94 95 CFATTACH_DECL_NEW(odcm, sizeof(struct odcm_softc), 96 odcm_match, odcm_attach, odcm_detach, NULL); 97 98 static int 99 odcm_match(device_t parent, cfdata_t cf, void *aux) 100 { 101 const uint32_t odcm = CPUID_ACPI | CPUID_TM; 102 struct cpufeature_attach_args *cfaa = aux; 103 uint32_t regs[4]; 104 105 if (strcmp(cfaa->name, "frequency") != 0) 106 return 0; 107 108 if (cpuid_level < 1) 109 return 0; 110 else { 111 x86_cpuid(1, regs); 112 113 return ((regs[3] & odcm) != odcm) ? 0 : 1; 114 } 115 } 116 117 static void 118 odcm_attach(device_t parent, device_t self, void *aux) 119 { 120 struct odcm_softc *sc = device_private(self); 121 122 sc->sc_dev = self; 123 sc->sc_log = NULL; 124 sc->sc_names = NULL; 125 126 odcm_quirks(); 127 128 if (odcm_init(self) != true) { 129 aprint_normal(": failed to initialize\n"); 130 return; 131 } 132 133 aprint_naive("\n"); 134 aprint_normal(": on-demand clock modulation (state %s)\n", 135 sc->sc_level == (ODCM_MAXSTATES - 1) ? "disabled" : "enabled"); 136 137 (void)pmf_device_register(self, NULL, NULL); 138 } 139 140 static int 141 odcm_detach(device_t self, int flags) 142 { 143 struct odcm_softc *sc = device_private(self); 144 145 /* 146 * Make sure the CPU operates with 147 * 100 % duty cycle after we are done. 148 */ 149 odcm_state_set(7); 150 151 if (sc->sc_log != NULL) 152 sysctl_teardown(&sc->sc_log); 153 154 if (sc->sc_names != NULL) 155 kmem_free(sc->sc_names, sc->sc_names_len); 156 157 pmf_device_deregister(self); 158 159 return 0; 160 } 161 162 static void 163 odcm_quirks(void) 164 { 165 uint32_t regs[4]; 166 167 x86_cpuid(1, regs); 168 169 switch (CPUID_TO_STEPPING(regs[0])) { 170 171 case 0x22: /* errata O50 P44 and Z21 */ 172 case 0x24: 173 case 0x25: 174 case 0x27: 175 case 0x29: 176 /* hang with 12.5 */ 177 state[__arraycount(state) - 1].errata = 1; 178 break; 179 180 case 0x07: /* errata N44 and P18 */ 181 case 0x0a: 182 case 0x12: 183 case 0x13: 184 /* hang at 12.5 and 25 */ 185 state[__arraycount(state) - 1].errata = 1; 186 state[__arraycount(state) - 2].errata = 1; 187 break; 188 } 189 } 190 191 static bool 192 odcm_init(device_t self) 193 { 194 struct odcm_softc *sc = device_private(self); 195 size_t i, len; 196 197 sc->sc_names_len = state[0].level * (sizeof("9999 ") - 1) + 1; 198 sc->sc_names = kmem_zalloc(sc->sc_names_len, KM_SLEEP); 199 200 for (i = len = 0; i < __arraycount(state); i++) { 201 202 if (state[i].errata) 203 continue; 204 205 len += snprintf(sc->sc_names + len, 206 sc->sc_names_len - len, "%d%s", state[i].level, 207 i < __arraycount(state) ? " " : ""); 208 if (len > sc->sc_names_len) 209 break; 210 } 211 212 /* 213 * Get the current value and create 214 * sysctl machdep.clockmod subtree. 215 */ 216 sc->sc_level = odcm_state_get(); 217 218 return odcm_sysctl(self); 219 } 220 221 static bool 222 odcm_sysctl(device_t self) 223 { 224 struct odcm_softc *sc = device_private(self); 225 const struct sysctlnode *node, *odcmnode; 226 int rv; 227 228 rv = sysctl_createv(&sc->sc_log, 0, NULL, &node, 229 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 230 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 231 232 if (rv != 0) 233 goto fail; 234 235 rv = sysctl_createv(&sc->sc_log, 0, &node, &odcmnode, 236 0, CTLTYPE_NODE, "clockmod", NULL, 237 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 238 239 if (rv != 0) 240 goto fail; 241 242 rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node, 243 CTLFLAG_READWRITE, CTLTYPE_INT, "target", 244 SYSCTL_DESCR("target duty cycle (0 = lowest, 7 highest)"), 245 odcm_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); 246 247 if (rv != 0) 248 goto fail; 249 250 sc->sc_target = node->sysctl_num; 251 252 rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node, 253 0, CTLTYPE_INT, "current", 254 SYSCTL_DESCR("current duty cycle"), 255 odcm_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); 256 257 if (rv != 0) 258 goto fail; 259 260 sc->sc_current = node->sysctl_num; 261 262 rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node, 263 0, CTLTYPE_STRING, "available", 264 SYSCTL_DESCR("list of duty cycles available"), 265 NULL, 0, sc->sc_names, sc->sc_names_len, 266 CTL_CREATE, CTL_EOL); 267 268 if (rv != 0) 269 goto fail; 270 271 return true; 272 273 fail: 274 sysctl_teardown(&sc->sc_log); 275 sc->sc_log = NULL; 276 277 return false; 278 } 279 280 static int 281 odcm_sysctl_helper(SYSCTLFN_ARGS) 282 { 283 struct sysctlnode node; 284 struct odcm_softc *sc; 285 int level, old, err; 286 size_t i; 287 288 node = *rnode; 289 sc = node.sysctl_data; 290 291 level = old = 0; 292 node.sysctl_data = &level; 293 294 if (rnode->sysctl_num == sc->sc_target) 295 level = old = sc->sc_level; 296 else if (rnode->sysctl_num == sc->sc_current) 297 level = odcm_state_get(); 298 else 299 return EOPNOTSUPP; 300 301 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 302 303 if (err || newp == NULL) 304 return err; 305 306 /* 307 * Check for an invalid level. 308 */ 309 for (i = 0; i < __arraycount(state); i++) { 310 311 if (level == state[i].level && !state[i].errata) 312 break; 313 } 314 315 if (i == __arraycount(state)) 316 return EINVAL; 317 318 if (rnode->sysctl_num == sc->sc_target && level != old) { 319 odcm_state_set(level); 320 sc->sc_level = level; 321 } 322 323 return 0; 324 } 325 326 static int 327 odcm_state_get(void) 328 { 329 uint64_t msr; 330 size_t i; 331 int val; 332 333 msr = rdmsr(MSR_THERM_CONTROL); 334 335 if ((msr & ODCM_ENABLE) == 0) 336 return (ODCM_MAXSTATES - 1); 337 338 msr = (msr >> ODCM_REGOFFSET) & (ODCM_MAXSTATES - 1); 339 340 for (val = -1, i = 0; i < __arraycount(state); i++) { 341 342 KASSERT(msr < INT_MAX); 343 344 if ((int)msr == state[i].reg) { 345 val = state[i].level; 346 break; 347 } 348 } 349 350 KASSERT(val != -1); 351 352 return val; 353 } 354 355 static void 356 odcm_state_set(int level) 357 { 358 struct msr_rw_info msr; 359 uint64_t xc; 360 size_t i; 361 362 for (i = 0; i < __arraycount(state); i++) { 363 364 if (level == state[i].level && !state[i].errata) 365 break; 366 } 367 368 KASSERT(i != __arraycount(state)); 369 370 msr.msr_read = true; 371 msr.msr_type = MSR_THERM_CONTROL; 372 msr.msr_mask = 0x1e; 373 374 if (state[i].reg != 0) /* bit 0 reserved */ 375 msr.msr_value = (state[i].reg << ODCM_REGOFFSET) | ODCM_ENABLE; 376 else 377 msr.msr_value = 0; /* max state */ 378 379 xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL); 380 xc_wait(xc); 381 } 382 383 MODULE(MODULE_CLASS_DRIVER, odcm, NULL); 384 385 #ifdef _MODULE 386 #include "ioconf.c" 387 #endif 388 389 static int 390 odcm_modcmd(modcmd_t cmd, void *aux) 391 { 392 int error = 0; 393 394 switch (cmd) { 395 case MODULE_CMD_INIT: 396 #ifdef _MODULE 397 error = config_init_component(cfdriver_ioconf_odcm, 398 cfattach_ioconf_odcm, cfdata_ioconf_odcm); 399 #endif 400 return error; 401 case MODULE_CMD_FINI: 402 #ifdef _MODULE 403 error = config_fini_component(cfdriver_ioconf_odcm, 404 cfattach_ioconf_odcm, cfdata_ioconf_odcm); 405 #endif 406 return error; 407 default: 408 return ENOTTY; 409 } 410 } 411