/* $NetBSD: coretemp.c,v 1.35.10.3 2023/07/29 11:03:21 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jukka Ruohonen. * * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /*- * Copyright (c) 2007 Juan Romero Pardines. * Copyright (c) 2007 Rui Paulo * 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. * * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.4 2007/10/15 20:00:21 netchild Exp $ * */ #include __KERNEL_RCSID(0, "$NetBSD: coretemp.c,v 1.35.10.3 2023/07/29 11:03:21 martin Exp $"); #include #include #include #include #include #include #include #include #include #include #define MSR_THERM_STATUS_STA __BIT(0) #define MSR_THERM_STATUS_LOG __BIT(1) #define MSR_THERM_STATUS_PROCHOT_EVT __BIT(2) #define MSR_THERM_STATUS_PROCHOT_LOG __BIT(3) #define MSR_THERM_STATUS_CRIT_STA __BIT(4) #define MSR_THERM_STATUS_CRIT_LOG __BIT(5) #define MSR_THERM_STATUS_TRIP1_STA __BIT(6) #define MSR_THERM_STATUS_TRIP1_LOG __BIT(7) #define MSR_THERM_STATUS_TRIP2_STA __BIT(8) #define MSR_THERM_STATUS_TRIP2_LOG __BIT(9) #define MSR_THERM_STATUS_READOUT __BITS(16, 22) #define MSR_THERM_STATUS_RESOLUTION __BITS(27, 30) #define MSR_THERM_STATUS_VALID __BIT(31) #define MSR_THERM_INTR_HITEMP __BIT(0) #define MSR_THERM_INTR_LOTEMPT __BIT(1) #define MSR_THERM_INTR_PROCHOT __BIT(2) #define MSR_THERM_INTR_FORCPR __BIT(3) #define MSR_THERM_INTR_OVERHEAT __BIT(4) #define MSR_THERM_INTR_TRIP1_VAL __BITS(8, 14) #define MSR_THERM_INTR_TRIP1 __BIT(15) #define MSR_THERM_INTR_TRIP2_VAL __BITS(16, 22) #define MSR_THERM_INTR_TRIP2 __BIT(23) #define MSR_TEMP_TARGET_READOUT __BITS(16, 23) #define TJMAX_DEFAULT 100 #define TJMAX_LIMIT_LOW 60 #define TJMAX_LIMIT_HIGH 120 static int coretemp_match(device_t, cfdata_t, void *); static void coretemp_attach(device_t, device_t, void *); static int coretemp_detach(device_t, int); static int coretemp_quirks(struct cpu_info *); static void coretemp_tjmax(device_t); static void coretemp_refresh(struct sysmon_envsys *, envsys_data_t *); static void coretemp_refresh_xcall(void *, void *); struct coretemp_softc { device_t sc_dev; struct cpu_info *sc_ci; struct sysmon_envsys *sc_sme; envsys_data_t sc_sensor; int sc_tjmax; }; CFATTACH_DECL_NEW(coretemp, sizeof(struct coretemp_softc), coretemp_match, coretemp_attach, coretemp_detach, NULL); static int coretemp_match(device_t parent, cfdata_t cf, void *aux) { struct cpufeature_attach_args *cfaa = aux; struct cpu_info *ci = cfaa->ci; uint32_t regs[4]; if (strcmp(cfaa->name, "temperature") != 0) return 0; if (cpu_vendor != CPUVENDOR_INTEL || cpuid_level < 0x06) return 0; /* * Only attach on the first SMT ID. */ if (ci->ci_smt_id != 0) return 0; /* * CPUID 0x06 returns 1 if the processor * has on-die thermal sensors. EBX[0:3] * contains the number of sensors. */ x86_cpuid(0x06, regs); if ((regs[0] & CPUID_DSPM_DTS) == 0) return 0; return coretemp_quirks(ci); } static void coretemp_attach(device_t parent, device_t self, void *aux) { struct coretemp_softc *sc = device_private(self); struct cpufeature_attach_args *cfaa = aux; struct cpu_info *ci = cfaa->ci; uint64_t msr; sc->sc_ci = ci; sc->sc_dev = self; msr = rdmsr(MSR_THERM_STATUS); msr = __SHIFTOUT(msr, MSR_THERM_STATUS_RESOLUTION); aprint_naive("\n"); aprint_normal(": thermal sensor, %u C resolution", (uint32_t)msr); sc->sc_sensor.units = ENVSYS_STEMP; sc->sc_sensor.state = ENVSYS_SINVALID; sc->sc_sensor.flags = ENVSYS_FMONCRITICAL | ENVSYS_FHAS_ENTROPY; (void)pmf_device_register(self, NULL, NULL); (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "%s temperature", device_xname(ci->ci_dev)); sc->sc_sme = sysmon_envsys_create(); if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0) goto fail; sc->sc_sme->sme_cookie = sc; sc->sc_sme->sme_name = device_xname(self); sc->sc_sme->sme_refresh = coretemp_refresh; if (sysmon_envsys_register(sc->sc_sme) != 0) goto fail; coretemp_tjmax(self); aprint_verbose(", Tjmax=%d", sc->sc_tjmax); aprint_normal("\n"); return; fail: sysmon_envsys_destroy(sc->sc_sme); sc->sc_sme = NULL; aprint_normal("\n"); } static int coretemp_detach(device_t self, int flags) { struct coretemp_softc *sc = device_private(self); if (sc->sc_sme != NULL) sysmon_envsys_unregister(sc->sc_sme); pmf_device_deregister(self); return 0; } static int coretemp_quirks(struct cpu_info *ci) { uint32_t model, stepping; uint64_t msr; model = CPUID_TO_MODEL(ci->ci_signature); stepping = CPUID_TO_STEPPING(ci->ci_signature); /* * Check if the MSR contains thermal * reading valid bit, this avoid false * positives on systems that fake up * a compatible CPU that doesn't have * access to these MSRs; such as VMWare. */ msr = rdmsr(MSR_THERM_STATUS); if ((msr & MSR_THERM_STATUS_VALID) == 0) return 0; /* * Check for errata AE18, "Processor Digital * Thermal Sensor (DTS) Readout Stops Updating * upon Returning from C3/C4 State". * * Adapted from the Linux coretemp driver. */ if (model == 0x0E && stepping < 0x0C) { msr = rdmsr(MSR_BIOS_SIGN); msr = msr >> 32; if (msr < 0x39) return 0; } return 1; } void coretemp_tjmax(device_t self) { struct coretemp_softc *sc = device_private(self); struct cpu_info *ci = sc->sc_ci; uint64_t msr; uint32_t model, stepping; int tjmax; model = CPUID_TO_MODEL(ci->ci_signature); stepping = CPUID_TO_STEPPING(ci->ci_signature); /* Set the initial value. */ sc->sc_tjmax = TJMAX_DEFAULT; if ((model == 0x0f && stepping >= 2) || (model == 0x0e)) { /* * Check MSR_IA32_PLATFORM_ID(0x17) bit 28. It's not documented * in the datasheet, but the following page describes the * detail: * http://software.intel.com/en-us/articles/ * mobile-intel-core2-processor-detection-table/ * Was: http://softwarecommunity.intel.com/Wiki/Mobility/ * 720.htm */ if (rdmsr_safe(MSR_IA32_PLATFORM_ID, &msr) != 0) goto notee; if ((model < 0x17) && ((msr & __BIT(28)) == 0)) goto notee; if (rdmsr_safe(MSR_IA32_EXT_CONFIG, &msr) == EFAULT) return; if ((msr & __BIT(30)) != 0) { sc->sc_tjmax = 85; return; } } else if (model == 0x17 && stepping == 0x06) { /* The mobile Penryn family. */ sc->sc_tjmax = 105; return; } else if (model == 0x1c) { if (stepping == 0x0a) { /* 45nm Atom D400, N400 and D500 series */ sc->sc_tjmax = 100; } else sc->sc_tjmax = 90; } else { notee: /* Attempt to get Tj(max) from IA32_TEMPERATURE_TARGET. */ if (rdmsr_safe(MSR_TEMPERATURE_TARGET, &msr) == EFAULT) { aprint_error_dev(sc->sc_dev, "Failed to read TEMPERATURE_TARGET MSR. " "Use the default (%d)\n", sc->sc_tjmax); return; } tjmax = __SHIFTOUT(msr, MSR_TEMP_TARGET_READOUT); if ((tjmax < TJMAX_LIMIT_LOW) || (tjmax > TJMAX_LIMIT_HIGH)) aprint_error_dev(sc->sc_dev, "WARNING: Tjmax(%d) might exceeded the limit.\n", tjmax); sc->sc_tjmax = tjmax; } } static void coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { struct coretemp_softc *sc = sme->sme_cookie; uint64_t xc; xc = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci); xc_wait(xc); } static void coretemp_refresh_xcall(void *arg0, void *arg1) { struct coretemp_softc *sc = arg0; envsys_data_t *edata = arg1; uint64_t msr; msr = rdmsr(MSR_THERM_STATUS); if ((msr & MSR_THERM_STATUS_VALID) == 0) edata->state = ENVSYS_SINVALID; else { /* * The temperature is computed by * subtracting the reading by Tj(max). */ edata->value_cur = sc->sc_tjmax; edata->value_cur -= __SHIFTOUT(msr, MSR_THERM_STATUS_READOUT); /* * Convert to mK. */ edata->value_cur *= 1000000; edata->value_cur += 273150000; edata->state = ENVSYS_SVALID; } if ((msr & MSR_THERM_STATUS_CRIT_STA) != 0) edata->state = ENVSYS_SCRITICAL; } MODULE(MODULE_CLASS_DRIVER, coretemp, "sysmon_envsys"); #ifdef _MODULE #include "ioconf.c" #endif static int coretemp_modcmd(modcmd_t cmd, void *aux) { int error = 0; switch (cmd) { case MODULE_CMD_INIT: #ifdef _MODULE error = config_init_component(cfdriver_ioconf_coretemp, cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); #endif return error; case MODULE_CMD_FINI: #ifdef _MODULE error = config_fini_component(cfdriver_ioconf_coretemp, cfattach_ioconf_coretemp, cfdata_ioconf_coretemp); #endif return error; default: return ENOTTY; } }