1/* $NetBSD: coretemp.c,v 1.27 2011/09/24 10:52:56 jruoho 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.27 2011/09/24 10:52:56 jruoho 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 105static int coretemp_match(device_t, cfdata_t, void *); 106static void coretemp_attach(device_t, device_t, void *); 107static int coretemp_detach(device_t, int); 108static int coretemp_quirks(struct cpu_info *); 109static void coretemp_tjmax(device_t); 110static void coretemp_refresh(struct sysmon_envsys *, envsys_data_t *); 111static void coretemp_refresh_xcall(void *, void *); 112 113struct coretemp_softc { 114 device_t sc_dev; 115 struct cpu_info *sc_ci; 116 struct sysmon_envsys *sc_sme; 117 envsys_data_t sc_sensor; 118 int sc_tjmax; 119}; 120 121CFATTACH_DECL_NEW(coretemp, sizeof(struct coretemp_softc), 122 coretemp_match, coretemp_attach, coretemp_detach, NULL); 123 124static int 125coretemp_match(device_t parent, cfdata_t cf, void *aux) 126{ 127 struct cpufeature_attach_args *cfaa = aux; 128 struct cpu_info *ci = cfaa->ci; 129 uint32_t regs[4]; 130 131 if (strcmp(cfaa->name, "temperature") != 0) 132 return 0; 133 134 if (cpu_vendor != CPUVENDOR_INTEL || cpuid_level < 0x06) 135 return 0; 136 137 /* 138 * Only attach on the first SMT ID. 139 */ 140 if (ci->ci_smt_id != 0) 141 return 0 ; 142 143 /* 144 * CPUID 0x06 returns 1 if the processor 145 * has on-die thermal sensors. EBX[0:3] 146 * contains the number of sensors. 147 */ 148 x86_cpuid(0x06, regs); 149 150 if ((regs[0] & CPUID_DSPM_DTS) == 0) 151 return 0; 152 153 return coretemp_quirks(ci); 154} 155 156static void 157coretemp_attach(device_t parent, device_t self, void *aux) 158{ 159 struct coretemp_softc *sc = device_private(self); 160 struct cpufeature_attach_args *cfaa = aux; 161 struct cpu_info *ci = cfaa->ci; 162 uint64_t msr; 163 164 sc->sc_ci = ci; 165 sc->sc_dev = self; 166 167 msr = rdmsr(MSR_THERM_STATUS); 168 msr = __SHIFTOUT(msr, MSR_THERM_STATUS_RESOLUTION); 169 170 aprint_naive("\n"); 171 aprint_normal(": thermal sensor, %u C resolution\n", (uint32_t)msr); 172 173 sc->sc_sensor.units = ENVSYS_STEMP; 174 sc->sc_sensor.flags = ENVSYS_FMONCRITICAL; 175 sc->sc_sensor.state = ENVSYS_SINVALID; 176 177 (void)pmf_device_register(self, NULL, NULL); 178 (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), 179 "%s temperature", device_xname(ci->ci_dev)); 180 181 sc->sc_sme = sysmon_envsys_create(); 182 183 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0) 184 goto fail; 185 186 sc->sc_sme->sme_cookie = sc; 187 sc->sc_sme->sme_name = device_xname(self); 188 sc->sc_sme->sme_refresh = coretemp_refresh; 189 190 if (sysmon_envsys_register(sc->sc_sme) != 0) 191 goto fail; 192 193 coretemp_tjmax(self); 194 return; 195 196fail: 197 sysmon_envsys_destroy(sc->sc_sme); 198 sc->sc_sme = NULL; 199} 200 201static int 202coretemp_detach(device_t self, int flags) 203{ 204 struct coretemp_softc *sc = device_private(self); 205 206 if (sc->sc_sme != NULL) 207 sysmon_envsys_unregister(sc->sc_sme); 208 209 pmf_device_deregister(self); 210 211 return 0; 212} 213 214static int 215coretemp_quirks(struct cpu_info *ci) 216{ 217 uint32_t model, stepping; 218 uint64_t msr; 219 220 model = CPUID2MODEL(ci->ci_signature); 221 stepping = CPUID2STEPPING(ci->ci_signature); 222 223 /* 224 * Check if the MSR contains thermal 225 * reading valid bit, this avoid false 226 * positives on systems that fake up 227 * a compatible CPU that doesn't have 228 * access to these MSRs; such as VMWare. 229 */ 230 msr = rdmsr(MSR_THERM_STATUS); 231 232 if ((msr & MSR_THERM_STATUS_VALID) == 0) 233 return 0; 234 235 /* 236 * Check for errata AE18, "Processor Digital 237 * Thermal Sensor (DTS) Readout Stops Updating 238 * upon Returning from C3/C4 State". 239 * 240 * Adapted from the Linux coretemp driver. 241 */ 242 if (model == 0x0E && stepping < 0x0C) { 243 244 msr = rdmsr(MSR_BIOS_SIGN); 245 msr = msr >> 32; 246 247 if (msr < 0x39) 248 return 0; 249 } 250 251 return 1; 252} 253 254void 255coretemp_tjmax(device_t self) 256{ 257 struct coretemp_softc *sc = device_private(self); 258 struct cpu_info *ci = sc->sc_ci; 259 uint32_t extmodel, model, stepping; 260 uint64_t msr; 261 262 model = CPUID2MODEL(ci->ci_signature); 263 extmodel = CPUID2EXTMODEL(ci->ci_signature); 264 stepping = CPUID2STEPPING(ci->ci_signature); 265 266 sc->sc_tjmax = 100; 267 268 /* 269 * The mobile Penryn family. 270 */ 271 if (model == 0x17 && stepping == 0x06) { 272 sc->sc_tjmax = 105; 273 return; 274 } 275 276 /* 277 * On some Core 2 CPUs, there is an undocumented 278 * MSR that tells if Tj(max) is 100 or 85. Note 279 * that MSR_IA32_EXT_CONFIG is not safe on all CPUs. 280 */ 281 if ((model == 0x0F && stepping >= 2) || 282 (model == 0x0E && extmodel != 1)) { 283 284 if (rdmsr_safe(MSR_IA32_EXT_CONFIG, &msr) == EFAULT) 285 return; 286 287 if ((msr & __BIT(30)) != 0) { 288 sc->sc_tjmax = 85; 289 return; 290 } 291 } 292 293 /* 294 * Attempt to get Tj(max) from IA32_TEMPERATURE_TARGET, 295 * but only consider the interval [70, 100] C as valid. 296 * It is not fully known which CPU models have the MSR. 297 */ 298 if (model == 0x0E && extmodel != 0) { 299 300 if (rdmsr_safe(MSR_TEMPERATURE_TARGET, &msr) == EFAULT) 301 return; 302 303 msr = __SHIFTOUT(msr, MSR_TEMP_TARGET_READOUT); 304 305 if (msr >= 70 && msr <= 100) { 306 sc->sc_tjmax = msr; 307 return; 308 } 309 } 310} 311 312static void 313coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 314{ 315 struct coretemp_softc *sc = sme->sme_cookie; 316 uint64_t xc; 317 318 xc = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci); 319 xc_wait(xc); 320} 321 322static void 323coretemp_refresh_xcall(void *arg0, void *arg1) 324{ 325 struct coretemp_softc *sc = arg0; 326 envsys_data_t *edata = arg1; 327 uint64_t msr; 328 329 msr = rdmsr(MSR_THERM_STATUS); 330 331 if ((msr & MSR_THERM_STATUS_VALID) == 0) 332 edata->state = ENVSYS_SINVALID; 333 else { 334 /* 335 * The temperature is computed by 336 * subtracting the reading by Tj(max). 337 */ 338 edata->value_cur = sc->sc_tjmax; 339 edata->value_cur -= __SHIFTOUT(msr, MSR_THERM_STATUS_READOUT); 340 341 /* 342 * Convert to mK. 343 */ 344 edata->value_cur *= 1000000; 345 edata->value_cur += 273150000; 346 edata->state = ENVSYS_SVALID; 347 } 348 349 if ((msr & MSR_THERM_STATUS_CRIT_STA) != 0) 350 edata->state = ENVSYS_SCRITICAL; 351} 352 353MODULE(MODULE_CLASS_DRIVER, coretemp, NULL); 354 355#ifdef _MODULE 356#include "ioconf.c" 357#endif 358 359static int 360coretemp_modcmd(modcmd_t cmd, void *aux) 361{ 362 int error = 0; 363 364 switch (cmd) { 365 case MODULE_CMD_INIT: 366#ifdef _MODULE 367 error = config_init_component(cfdriver_ioconf_coretemp, 368 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); 369#endif 370 return error; 371 case MODULE_CMD_FINI: 372#ifdef _MODULE 373 error = config_fini_component(cfdriver_ioconf_coretemp, 374 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); 375#endif 376 return error; 377 default: 378 return ENOTTY; 379 } 380} 381