1 /* $NetBSD: amdzentemp.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $ */ 2 /* $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $ */ 3 4 /* 5 * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org> 9 * All rights reserved. 10 * 11 * This code is derived from software contributed to The NetBSD Foundation 12 * by Christoph Egger. 13 * 14 * NetBSD port by Ian Clark <mrrooster@gmail.com> 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 40 * 41 * Permission to use, copy, modify, and distribute this software for any 42 * purpose with or without fee is hereby granted, provided that the above 43 * copyright notice and this permission notice appear in all copies. 44 * 45 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 46 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 47 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 48 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 49 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 50 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 51 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 52 */ 53 54 55 #include <sys/cdefs.h> 56 __KERNEL_RCSID(0, "$NetBSD: amdzentemp.c,v 1.11 2020/04/25 15:26:18 bouyer Exp $ "); 57 58 #include <sys/param.h> 59 #include <sys/bus.h> 60 #include <sys/cpu.h> 61 #include <sys/systm.h> 62 #include <sys/device.h> 63 #include <sys/kmem.h> 64 #include <sys/module.h> 65 66 #include <machine/specialreg.h> 67 68 #include <dev/pci/pcireg.h> 69 #include <dev/pci/pcivar.h> 70 #include <dev/pci/pcidevs.h> 71 72 #include <dev/sysmon/sysmonvar.h> 73 74 #include "amdsmn.h" 75 76 #define AMD_CURTMP_RANGE_ADJUST 49000000 /* in microKelvins (ie, 49C) */ 77 #define AMD_CURTMP_RANGE_CHECK __BIT(19) 78 #define F10_TEMP_CURTMP __BITS(31,21) /* XXX same as amdtemp.c */ 79 #define F15M60_CURTMP_TJSEL __BITS(17,16) 80 81 /* 82 * Reported Temperature, Family 15h, M60+ 83 * 84 * Same register bit definitions as other Family 15h CPUs, but access is 85 * indirect via SMN, like Family 17h. 86 */ 87 #define AMD_15H_M60H_REPTMP_CTRL 0xd8200ca4 88 89 /* 90 * Reported Temperature, Family 17h 91 * 92 * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register 93 * provide the current temp. bit 19, when clear, means the temp is reported in 94 * a range 0.."225C" (probable typo for 255C), and when set changes the range 95 * to -49..206C. 96 */ 97 #define AMD_17H_CUR_TMP 0x59800 98 99 struct amdzentemp_softc { 100 struct sysmon_envsys *sc_sme; 101 device_t sc_smn; 102 envsys_data_t *sc_sensor; 103 size_t sc_sensor_len; 104 size_t sc_numsensors; 105 int32_t sc_offset; 106 }; 107 108 109 static int amdzentemp_match(device_t, cfdata_t, void *); 110 static void amdzentemp_attach(device_t, device_t, void *); 111 static int amdzentemp_detach(device_t, int); 112 113 static void amdzentemp_init(struct amdzentemp_softc *); 114 static void amdzentemp_setup_sensors(struct amdzentemp_softc *, int); 115 static void amdzentemp_family15_refresh(struct sysmon_envsys *, envsys_data_t *); 116 static void amdzentemp_family17_refresh(struct sysmon_envsys *, envsys_data_t *); 117 118 CFATTACH_DECL_NEW(amdzentemp, sizeof(struct amdzentemp_softc), 119 amdzentemp_match, amdzentemp_attach, amdzentemp_detach, NULL); 120 121 static int 122 amdzentemp_match(device_t parent, cfdata_t match, void *aux) 123 { 124 struct pci_attach_args *pa __diagused = aux; 125 126 KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD); 127 128 cfdata_t parent_cfdata = device_cfdata(parent); 129 130 /* Got AMD family 17h system management network */ 131 return parent_cfdata->cf_name && 132 memcmp(parent_cfdata->cf_name, "amdsmn", 6) == 0; 133 } 134 135 static void 136 amdzentemp_attach(device_t parent, device_t self, void *aux) 137 { 138 struct amdzentemp_softc *sc = device_private(self); 139 struct cpu_info *ci = curcpu(); 140 int family; 141 int error; 142 size_t i; 143 144 family = CPUID_TO_FAMILY(ci->ci_signature); 145 aprint_naive("\n"); 146 aprint_normal(": AMD CPU Temperature Sensors (Family%xh)", 147 family); 148 149 sc->sc_smn = parent; 150 151 amdzentemp_init(sc); 152 153 aprint_normal("\n"); 154 155 sc->sc_sme = sysmon_envsys_create(); 156 sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors; 157 sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP); 158 159 amdzentemp_setup_sensors(sc, device_unit(self)); 160 161 /* 162 * Set properties in sensors. 163 */ 164 for (i = 0; i < sc->sc_numsensors; i++) { 165 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) 166 goto bad; 167 } 168 169 /* 170 * Register the sysmon_envsys device. 171 */ 172 sc->sc_sme->sme_name = device_xname(self); 173 sc->sc_sme->sme_cookie = sc; 174 175 switch (family) { 176 case 0x15: 177 sc->sc_sme->sme_refresh = amdzentemp_family15_refresh; 178 break; 179 case 0x17: 180 sc->sc_sme->sme_refresh = amdzentemp_family17_refresh; 181 break; 182 default: 183 /* XXX panic */ 184 break; 185 } 186 187 error = sysmon_envsys_register(sc->sc_sme); 188 if (error) { 189 aprint_error_dev(self, "unable to register with sysmon " 190 "(error=%d)\n", error); 191 goto bad; 192 } 193 194 (void)pmf_device_register(self, NULL, NULL); 195 196 return; 197 198 bad: 199 if (sc->sc_sme != NULL) { 200 sysmon_envsys_destroy(sc->sc_sme); 201 sc->sc_sme = NULL; 202 } 203 204 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 205 sc->sc_sensor = NULL; 206 } 207 208 static int 209 amdzentemp_detach(device_t self, int flags) 210 { 211 struct amdzentemp_softc *sc = device_private(self); 212 213 pmf_device_deregister(self); 214 if (sc->sc_sme != NULL) 215 sysmon_envsys_unregister(sc->sc_sme); 216 217 if (sc->sc_sensor != NULL) 218 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 219 220 return 0; 221 } 222 223 224 static void 225 amdzentemp_init(struct amdzentemp_softc *sc) 226 { 227 228 sc->sc_numsensors = 1; 229 sc->sc_offset = 0; 230 231 if (strstr(cpu_brand_string, "AMD Ryzen 5 1600X") 232 || strstr(cpu_brand_string, "AMD Ryzen 7 1700X") 233 || strstr(cpu_brand_string, "AMD Ryzen 7 1800X")) 234 sc->sc_offset = -20000000; 235 else if (strstr(cpu_brand_string, "AMD Ryzen 7 2700X")) 236 sc->sc_offset = -10000000; 237 else if (strstr(cpu_brand_string, "AMD Ryzen Threadripper 19") 238 || strstr(cpu_brand_string, "AMD Ryzen Threadripper 29")) 239 sc->sc_offset = -27000000; 240 } 241 242 static void 243 amdzentemp_setup_sensors(struct amdzentemp_softc *sc, int dv_unit) 244 { 245 sc->sc_sensor[0].units = ENVSYS_STEMP; 246 sc->sc_sensor[0].state = ENVSYS_SVALID; 247 sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY; 248 249 snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc), 250 "cpu%u temperature", dv_unit); 251 } 252 253 static void 254 amdzentemp_family15_refresh(struct sysmon_envsys *sme, 255 envsys_data_t *edata) 256 { 257 struct amdzentemp_softc *sc = sme->sme_cookie; 258 uint32_t val, temp; 259 int error; 260 261 error = amdsmn_read(sc->sc_smn, AMD_15H_M60H_REPTMP_CTRL, &val); 262 if (error) { 263 edata->state = ENVSYS_SINVALID; 264 return; 265 } 266 267 /* from amdtemp.c:amdtemp_family10_refresh() */ 268 temp = __SHIFTOUT(val, F10_TEMP_CURTMP); 269 270 /* From Celsius to micro-Kelvin. */ 271 edata->value_cur = (temp * 125000) + 273150000; 272 273 /* 274 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 275 * adjusted down by 49.0 degrees Celsius. (This adjustment is not 276 * documented in BKDGs prior to family 15h model 00h.) 277 * 278 * XXX should be in amdtemp.c:amdtemp_family10_refresh() for f15 279 * as well?? 280 */ 281 if (__SHIFTOUT(val, F15M60_CURTMP_TJSEL) == 0x3) 282 edata->value_cur -= AMD_CURTMP_RANGE_ADJUST; 283 284 edata->state = ENVSYS_SVALID; 285 } 286 287 static void 288 amdzentemp_family17_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 289 { 290 struct amdzentemp_softc *sc = sme->sme_cookie; 291 uint32_t temp; 292 int error; 293 294 error = amdsmn_read(sc->sc_smn, AMD_17H_CUR_TMP, &temp); 295 if (error) { 296 edata->state = ENVSYS_SINVALID; 297 return; 298 } 299 edata->state = ENVSYS_SVALID; 300 /* From C to uK. */ 301 edata->value_cur = ((temp >> 21) * 125000) + 273150000; 302 /* adjust for possible offset of 49K */ 303 if (temp & AMD_CURTMP_RANGE_CHECK) 304 edata->value_cur -= AMD_CURTMP_RANGE_ADJUST; 305 edata->value_cur += sc->sc_offset; 306 } 307 308 MODULE(MODULE_CLASS_DRIVER, amdzentemp, "sysmon_envsys,amdsmn"); 309 310 #ifdef _MODULE 311 #include "ioconf.c" 312 #endif 313 314 static int 315 amdzentemp_modcmd(modcmd_t cmd, void *aux) 316 { 317 int error = 0; 318 319 switch (cmd) { 320 case MODULE_CMD_INIT: 321 #ifdef _MODULE 322 error = config_init_component(cfdriver_ioconf_amdzentemp, 323 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 324 #endif 325 return error; 326 case MODULE_CMD_FINI: 327 #ifdef _MODULE 328 error = config_fini_component(cfdriver_ioconf_amdzentemp, 329 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 330 #endif 331 return error; 332 default: 333 return ENOTTY; 334 } 335 } 336