1/* $NetBSD: coretemp.c,v 1.42 2024/07/15 01:57:23 gutteridge Exp $ */ 2 3/*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jukka Ruohonen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33/*- 34 * Copyright (c) 2007 Juan Romero Pardines. 35 * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org> 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 51 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 53 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 55 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 56 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 57 * POSSIBILITY OF SUCH DAMAGE. 58 * 59 * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.4 2007/10/15 20:00:21 netchild Exp $ 60 * 61 */ 62 63#include <sys/cdefs.h> 64__KERNEL_RCSID(0, "$NetBSD: coretemp.c,v 1.42 2024/07/15 01:57:23 gutteridge Exp $"); 65 66#include <sys/param.h> 67#include <sys/device.h> 68#include <sys/cpu.h> 69#include <sys/module.h> 70#include <sys/xcall.h> 71 72#include <dev/sysmon/sysmonvar.h> 73 74#include <machine/cpuvar.h> 75#include <machine/cpufunc.h> 76#include <machine/cputypes.h> 77#include <machine/specialreg.h> 78 79#define MSR_THERM_STATUS_STA __BIT(0) 80#define MSR_THERM_STATUS_LOG __BIT(1) 81#define MSR_THERM_STATUS_PROCHOT_EVT __BIT(2) 82#define MSR_THERM_STATUS_PROCHOT_LOG __BIT(3) 83#define MSR_THERM_STATUS_CRIT_STA __BIT(4) 84#define MSR_THERM_STATUS_CRIT_LOG __BIT(5) 85#define MSR_THERM_STATUS_TRIP1_STA __BIT(6) 86#define MSR_THERM_STATUS_TRIP1_LOG __BIT(7) 87#define MSR_THERM_STATUS_TRIP2_STA __BIT(8) 88#define MSR_THERM_STATUS_TRIP2_LOG __BIT(9) 89#define MSR_THERM_STATUS_READOUT __BITS(16, 22) 90#define MSR_THERM_STATUS_RESOLUTION __BITS(27, 30) 91#define MSR_THERM_STATUS_VALID __BIT(31) 92 93#define MSR_THERM_INTR_HITEMP __BIT(0) 94#define MSR_THERM_INTR_LOTEMPT __BIT(1) 95#define MSR_THERM_INTR_PROCHOT __BIT(2) 96#define MSR_THERM_INTR_FORCPR __BIT(3) 97#define MSR_THERM_INTR_OVERHEAT __BIT(4) 98#define MSR_THERM_INTR_TRIP1_VAL __BITS(8, 14) 99#define MSR_THERM_INTR_TRIP1 __BIT(15) 100#define MSR_THERM_INTR_TRIP2_VAL __BITS(16, 22) 101#define MSR_THERM_INTR_TRIP2 __BIT(23) 102 103#define MSR_TEMP_TARGET_READOUT __BITS(16, 23) 104 105#define TJMAX_DEFAULT 100 106#define TJMAX_LIMIT_LOW 60 107#define TJMAX_LIMIT_HIGH 120 108 109static int coretemp_match(device_t, cfdata_t, void *); 110static void coretemp_attach(device_t, device_t, void *); 111static int coretemp_detach(device_t, int); 112static int coretemp_quirks(struct cpu_info *); 113static int coretemp_tjmax(device_t); 114static void coretemp_refresh(struct sysmon_envsys *, envsys_data_t *); 115static void coretemp_refresh_xcall(void *, void *); 116 117struct coretemp_softc { 118 device_t sc_dev; 119 struct cpu_info *sc_ci; 120 struct sysmon_envsys *sc_sme; 121 envsys_data_t sc_sensor; 122 int sc_tjmax; 123}; 124 125CFATTACH_DECL_NEW(coretemp, sizeof(struct coretemp_softc), 126 coretemp_match, coretemp_attach, coretemp_detach, NULL); 127 128static int 129coretemp_match(device_t parent, cfdata_t cf, void *aux) 130{ 131 struct cpufeature_attach_args *cfaa = aux; 132 struct cpu_info *ci = cfaa->ci; 133 uint32_t regs[4]; 134 135 if (strcmp(cfaa->name, "temperature") != 0) 136 return 0; 137 138 if (cpu_vendor != CPUVENDOR_INTEL || cpuid_level < 0x06) 139 return 0; 140 141 /* 142 * Only attach on the first SMT ID. 143 */ 144 if (ci->ci_smt_id != 0) 145 return 0; 146 147 /* 148 * CPUID 0x06 returns 1 if the processor 149 * has on-die thermal sensors. EBX[0:3] 150 * contains the number of sensors. 151 */ 152 x86_cpuid(0x06, regs); 153 154 if ((regs[0] & CPUID_DSPM_DTS) == 0) 155 return 0; 156 157 return coretemp_quirks(ci); 158} 159 160static void 161coretemp_attach(device_t parent, device_t self, void *aux) 162{ 163 struct coretemp_softc *sc = device_private(self); 164 struct cpufeature_attach_args *cfaa = aux; 165 struct cpu_info *ci = cfaa->ci; 166 uint64_t msr; 167 168 sc->sc_ci = ci; 169 sc->sc_dev = self; 170 171 msr = rdmsr(MSR_THERM_STATUS); 172 msr = __SHIFTOUT(msr, MSR_THERM_STATUS_RESOLUTION); 173 174 aprint_naive("\n"); 175 aprint_normal(": thermal sensor, %u C resolution", (uint32_t)msr); 176 177 sc->sc_sensor.units = ENVSYS_STEMP; 178 sc->sc_sensor.state = ENVSYS_SINVALID; 179 sc->sc_sensor.flags = ENVSYS_FMONCRITICAL | ENVSYS_FHAS_ENTROPY; 180 181 (void)pmf_device_register(self, NULL, NULL); 182 (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), 183 "%s temperature", device_xname(ci->ci_dev)); 184 185 sc->sc_sme = sysmon_envsys_create(); 186 187 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0) 188 goto fail; 189 190 sc->sc_sme->sme_cookie = sc; 191 sc->sc_sme->sme_name = device_xname(self); 192 sc->sc_sme->sme_refresh = coretemp_refresh; 193 194 if (sysmon_envsys_register(sc->sc_sme) != 0) 195 goto fail; 196 197 if (coretemp_tjmax(self) == 0) { 198 aprint_verbose(", Tjmax=%d", sc->sc_tjmax); 199 aprint_normal("\n"); 200 } 201 return; 202 203fail: 204 sysmon_envsys_destroy(sc->sc_sme); 205 sc->sc_sme = NULL; 206 aprint_normal("\n"); 207} 208 209static int 210coretemp_detach(device_t self, int flags) 211{ 212 struct coretemp_softc *sc = device_private(self); 213 214 if (sc->sc_sme != NULL) 215 sysmon_envsys_unregister(sc->sc_sme); 216 217 pmf_device_deregister(self); 218 219 return 0; 220} 221 222static int 223coretemp_quirks(struct cpu_info *ci) 224{ 225 uint32_t model, stepping; 226 uint64_t msr; 227 228 model = CPUID_TO_MODEL(ci->ci_signature); 229 stepping = CPUID_TO_STEPPING(ci->ci_signature); 230 231 /* 232 * Check if the MSR contains thermal 233 * reading valid bit, this avoid false 234 * positives on systems that fake up 235 * a compatible CPU that doesn't have 236 * access to these MSRs; such as VMWare. 237 */ 238 msr = rdmsr(MSR_THERM_STATUS); 239 240 if ((msr & MSR_THERM_STATUS_VALID) == 0) 241 return 0; 242 243 /* 244 * Check for errata AE18, "Processor Digital 245 * Thermal Sensor (DTS) Readout Stops Updating 246 * upon Returning from C3/C4 State". 247 * 248 * Adapted from the Linux coretemp driver. 249 */ 250 if (model == 0x0E && stepping < 0x0C) { 251 252 msr = rdmsr(MSR_BIOS_SIGN); 253 msr = msr >> 32; 254 255 if (msr < 0x39) 256 return 0; 257 } 258 259 return 1; 260} 261 262static int 263coretemp_tjmax(device_t self) 264{ 265 struct coretemp_softc *sc = device_private(self); 266 struct cpu_info *ci = sc->sc_ci; 267 uint64_t msr; 268 uint32_t model, stepping; 269 int tjmax; 270 271 model = CPUID_TO_MODEL(ci->ci_signature); 272 stepping = CPUID_TO_STEPPING(ci->ci_signature); 273 274 /* Set the initial value. */ 275 sc->sc_tjmax = TJMAX_DEFAULT; 276 277 if ((model == 0x0f && stepping >= 2) || (model == 0x0e)) { 278 /* 279 * Check MSR_IA32_PLATFORM_ID(0x17) bit 28. It's not documented 280 * in the datasheet, but the following page describes the 281 * detail: 282 * https://web.archive.org/web/20110608131711/http://software.intel.com/ 283 * en-us/articles/mobile-intel-core2-processor-detection-table/ 284 * Was: http://softwarecommunity.intel.com/Wiki/Mobility/ 285 * 720.htm 286 */ 287 if (rdmsr_safe(MSR_IA32_PLATFORM_ID, &msr) != 0) 288 goto notee; 289 if ((msr & __BIT(28)) == 0) 290 goto notee; 291 292 if (rdmsr_safe(MSR_IA32_EXT_CONFIG, &msr) == EFAULT) { 293 aprint_normal("\n"); 294 aprint_error_dev(sc->sc_dev, 295 "Failed to read MSR_IA32_EXT_CONFIG MSR. " 296 "Using default (%d)\n", sc->sc_tjmax); 297 return 1; 298 } 299 300 if ((msr & __BIT(30)) != 0) 301 sc->sc_tjmax = 85; 302 } else if (model == 0x17 && stepping == 0x06) { 303 /* The mobile Penryn family. */ 304 sc->sc_tjmax = 105; 305 } else if (model == 0x1c) { 306 if (stepping == 0x0a) { 307 /* 45nm Atom D400, N400 and D500 series */ 308 sc->sc_tjmax = 100; 309 } else 310 sc->sc_tjmax = 90; 311 } else { 312notee: 313 /* 314 * Attempt to get Tj(max) from IA32_TEMPERATURE_TARGET. 315 * It is not fully known which CPU models have the MSR. 316 */ 317 if (rdmsr_safe(MSR_TEMPERATURE_TARGET, &msr) == EFAULT) { 318 aprint_normal("\n"); 319 aprint_error_dev(sc->sc_dev, 320 "Failed to read TEMPERATURE_TARGET MSR. " 321 "Using default (%d)\n", sc->sc_tjmax); 322 return 1; 323 } 324 325 tjmax = __SHIFTOUT(msr, MSR_TEMP_TARGET_READOUT); 326 if (tjmax < TJMAX_LIMIT_LOW) { 327 aprint_normal("\n"); 328 aprint_error_dev(sc->sc_dev, 329 "WARNING: Tjmax(%d) retrieved was below expected range, " 330 "using default (%d).\n", tjmax, sc->sc_tjmax); 331 return 1; 332 } 333 334 if (tjmax > TJMAX_LIMIT_HIGH) { 335 aprint_normal("\n"); 336 aprint_error_dev(sc->sc_dev, 337 "WARNING: Tjmax(%d) might exceed the limit.\n", 338 tjmax); 339 sc->sc_tjmax = tjmax; 340 return 1; 341 } 342 sc->sc_tjmax = tjmax; 343 } 344 345 return 0; 346} 347 348static void 349coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 350{ 351 struct coretemp_softc *sc = sme->sme_cookie; 352 uint64_t xc; 353 354 xc = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci); 355 xc_wait(xc); 356} 357 358static void 359coretemp_refresh_xcall(void *arg0, void *arg1) 360{ 361 struct coretemp_softc *sc = arg0; 362 envsys_data_t *edata = arg1; 363 uint64_t msr; 364 365 msr = rdmsr(MSR_THERM_STATUS); 366 367 if ((msr & MSR_THERM_STATUS_VALID) == 0) 368 edata->state = ENVSYS_SINVALID; 369 else { 370 /* 371 * The temperature is computed by 372 * subtracting the reading by Tj(max). 373 */ 374 edata->value_cur = sc->sc_tjmax; 375 edata->value_cur -= __SHIFTOUT(msr, MSR_THERM_STATUS_READOUT); 376 377 /* 378 * Convert to mK. 379 */ 380 edata->value_cur *= 1000000; 381 edata->value_cur += 273150000; 382 edata->state = ENVSYS_SVALID; 383 } 384 385 if ((msr & MSR_THERM_STATUS_CRIT_STA) != 0) 386 edata->state = ENVSYS_SCRITICAL; 387} 388 389MODULE(MODULE_CLASS_DRIVER, coretemp, "sysmon_envsys"); 390 391#ifdef _MODULE 392#include "ioconf.c" 393#endif 394 395static int 396coretemp_modcmd(modcmd_t cmd, void *aux) 397{ 398 int error = 0; 399 400 switch (cmd) { 401 case MODULE_CMD_INIT: 402#ifdef _MODULE 403 error = config_init_component(cfdriver_ioconf_coretemp, 404 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); 405#endif 406 return error; 407 case MODULE_CMD_FINI: 408#ifdef _MODULE 409 error = config_fini_component(cfdriver_ioconf_coretemp, 410 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); 411#endif 412 return error; 413 default: 414 return ENOTTY; 415 } 416} 417