1 /* $NetBSD: amdzentemp.c,v 1.22 2024/10/17 14:16:48 msaitoh 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.22 2024/10/17 14:16:48 msaitoh 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 F10_TEMP_CURTMP __BITS(31,21) /* XXX same as amdtemp.c */ 78 #define F10_TEMP_CURTMP_MASK 0x7ff 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 #define AMD_17H_CUR_TMP_RANGE_SEL __BIT(19) 99 #define AMD_17H_CCD_TMP_VALID __BIT(11) 100 101 struct amdzentemp_softc { 102 device_t sc_dev; 103 struct sysmon_envsys *sc_sme; 104 device_t sc_smn; 105 envsys_data_t *sc_sensor; 106 size_t sc_sensor_len; 107 size_t sc_numsensors; 108 int32_t sc_offset; 109 int32_t sc_ccd_offset; 110 }; 111 112 enum { 113 NOSENSOR = 0, 114 CORE0_SENSOR0, 115 CCD_BASE, 116 CCD0 = CCD_BASE, 117 CCD1, 118 CCD2, 119 CCD3, 120 CCD4, 121 CCD5, 122 CCD6, 123 CCD7, 124 CCD8, 125 CCD9, 126 CCD10, 127 CCD11, 128 CCD_MAX, 129 NUM_CCDS = CCD_MAX - CCD_BASE 130 }; 131 132 133 static int amdzentemp_match(device_t, cfdata_t, void *); 134 static void amdzentemp_attach(device_t, device_t, void *); 135 static int amdzentemp_detach(device_t, int); 136 137 static void amdzentemp_init(struct amdzentemp_softc *, int, int); 138 static void amdzentemp_setup_sensors(struct amdzentemp_softc *); 139 static void amdzentemp_family15_refresh(struct sysmon_envsys *, envsys_data_t *); 140 static void amdzentemp_family17_refresh(struct sysmon_envsys *, envsys_data_t *); 141 static int amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *, int, int); 142 static void amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *); 143 144 CFATTACH_DECL_NEW(amdzentemp, sizeof(struct amdzentemp_softc), 145 amdzentemp_match, amdzentemp_attach, amdzentemp_detach, NULL); 146 147 static int 148 amdzentemp_match(device_t parent, cfdata_t match, void *aux) 149 { 150 struct pci_attach_args *pa __diagused = aux; 151 152 KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD); 153 154 cfdata_t parent_cfdata = device_cfdata(parent); 155 156 /* Got AMD family 17h system management network */ 157 return parent_cfdata->cf_name && 158 memcmp(parent_cfdata->cf_name, "amdsmn", 6) == 0; 159 } 160 161 static void 162 amdzentemp_attach(device_t parent, device_t self, void *aux) 163 { 164 struct amdzentemp_softc *sc = device_private(self); 165 struct cpu_info *ci = curcpu(); 166 int family, model; 167 int error; 168 size_t i; 169 170 sc->sc_dev = self; 171 172 family = CPUID_TO_FAMILY(ci->ci_signature); 173 model = CPUID_TO_MODEL(ci->ci_signature); 174 aprint_naive("\n"); 175 aprint_normal(": AMD CPU Temperature Sensors (Family%xh)", family); 176 177 sc->sc_smn = parent; 178 179 amdzentemp_init(sc, family, model); 180 181 aprint_normal("\n"); 182 183 sc->sc_sme = sysmon_envsys_create(); 184 sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors; 185 sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP); 186 187 amdzentemp_setup_sensors(sc); 188 189 /* 190 * Set properties in sensors. 191 */ 192 for (i = 0; i < sc->sc_numsensors; i++) { 193 if (sc->sc_sensor[i].private == NOSENSOR) 194 continue; 195 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) 196 goto bad; 197 } 198 199 /* 200 * Register the sysmon_envsys device. 201 */ 202 sc->sc_sme->sme_name = device_xname(self); 203 sc->sc_sme->sme_cookie = sc; 204 205 switch (family) { 206 case 0x15: 207 sc->sc_sme->sme_refresh = amdzentemp_family15_refresh; 208 break; 209 case 0x17: 210 case 0x19: 211 case 0x1a: 212 sc->sc_sme->sme_refresh = amdzentemp_family17_refresh; 213 break; 214 default: 215 /* XXX panic */ 216 break; 217 } 218 219 error = sysmon_envsys_register(sc->sc_sme); 220 if (error) { 221 aprint_error_dev(self, "unable to register with sysmon " 222 "(error=%d)\n", error); 223 goto bad; 224 } 225 226 (void)pmf_device_register(self, NULL, NULL); 227 228 return; 229 230 bad: 231 if (sc->sc_sme != NULL) { 232 sysmon_envsys_destroy(sc->sc_sme); 233 sc->sc_sme = NULL; 234 } 235 236 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 237 sc->sc_sensor = NULL; 238 } 239 240 static int 241 amdzentemp_detach(device_t self, int flags) 242 { 243 struct amdzentemp_softc *sc = device_private(self); 244 245 pmf_device_deregister(self); 246 if (sc->sc_sme != NULL) 247 sysmon_envsys_unregister(sc->sc_sme); 248 249 if (sc->sc_sensor != NULL) 250 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 251 252 return 0; 253 } 254 255 256 static void 257 amdzentemp_init(struct amdzentemp_softc *sc, int family, int model) 258 { 259 260 sc->sc_numsensors = 1 + amdzentemp_probe_ccd_sensors(sc, family, model); 261 sc->sc_offset = 0; 262 263 if (strstr(cpu_brand_string, "AMD Ryzen 5 1600X") 264 || strstr(cpu_brand_string, "AMD Ryzen 7 1700X") 265 || strstr(cpu_brand_string, "AMD Ryzen 7 1800X")) 266 sc->sc_offset = -20000000; 267 else if (strstr(cpu_brand_string, "AMD Ryzen 7 2700X")) 268 sc->sc_offset = -10000000; 269 else if (strstr(cpu_brand_string, "AMD Ryzen Threadripper 19") 270 || strstr(cpu_brand_string, "AMD Ryzen Threadripper 29")) 271 sc->sc_offset = -27000000; 272 } 273 274 static void 275 amdzentemp_setup_sensors(struct amdzentemp_softc *sc) 276 { 277 sc->sc_sensor[0].units = ENVSYS_STEMP; 278 sc->sc_sensor[0].state = ENVSYS_SVALID; 279 sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY; 280 sc->sc_sensor[0].private = CORE0_SENSOR0; 281 282 snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc), 283 "cpu%u temperature", device_unit(sc->sc_dev)); 284 285 if (sc->sc_numsensors > 1) 286 amdzentemp_setup_ccd_sensors(sc); 287 } 288 289 static void 290 amdzentemp_family15_refresh(struct sysmon_envsys *sme, 291 envsys_data_t *edata) 292 { 293 struct amdzentemp_softc *sc = sme->sme_cookie; 294 uint32_t val, temp; 295 int error; 296 297 error = amdsmn_read(sc->sc_smn, AMD_15H_M60H_REPTMP_CTRL, &val); 298 if (error) { 299 edata->state = ENVSYS_SINVALID; 300 return; 301 } 302 303 /* from amdtemp.c:amdtemp_family10_refresh() */ 304 temp = __SHIFTOUT(val, F10_TEMP_CURTMP); 305 306 /* From Celsius to micro-Kelvin. */ 307 edata->value_cur = (temp * 125000) + 273150000; 308 309 /* 310 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 311 * adjusted down by 49.0 degrees Celsius. (This adjustment is not 312 * documented in BKDGs prior to family 15h model 00h.) 313 * 314 * XXX should be in amdtemp.c:amdtemp_family10_refresh() for f15 315 * as well?? 316 */ 317 if (__SHIFTOUT(val, F15M60_CURTMP_TJSEL) == 0x3) 318 edata->value_cur -= AMD_CURTMP_RANGE_ADJUST; 319 320 edata->state = ENVSYS_SVALID; 321 } 322 323 static void 324 amdzentemp_family17_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 325 { 326 struct amdzentemp_softc *sc = sme->sme_cookie; 327 uint32_t temp; 328 bool minus49; 329 int i, error; 330 331 switch (edata->private) { 332 case CORE0_SENSOR0: 333 /* Tctl */ 334 error = amdsmn_read(sc->sc_smn, AMD_17H_CUR_TMP, &temp); 335 if (error) { 336 edata->state = ENVSYS_SINVALID; 337 return; 338 } 339 minus49 = (temp & AMD_17H_CUR_TMP_RANGE_SEL) ? 340 true : false; 341 temp = __SHIFTOUT(temp, F10_TEMP_CURTMP); 342 break; 343 case CCD_BASE ... (CCD_MAX - 1): 344 /* Tccd */ 345 i = edata->private - CCD_BASE; 346 error = amdsmn_read(sc->sc_smn, 347 AMD_17H_CUR_TMP + sc->sc_ccd_offset + (i * sizeof(temp)), 348 &temp); 349 if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID)) { 350 edata->state = ENVSYS_SINVALID; 351 return; 352 } 353 minus49 = true; 354 temp &= F10_TEMP_CURTMP_MASK; 355 break; 356 default: 357 edata->state = ENVSYS_SINVALID; 358 return; 359 } 360 edata->state = ENVSYS_SVALID; 361 /* From C to uK. */ 362 edata->value_cur = (temp * 125000) + 273150000; 363 /* adjust for possible offset of 49K */ 364 if (minus49) 365 edata->value_cur -= AMD_CURTMP_RANGE_ADJUST; 366 edata->value_cur += sc->sc_offset; 367 } 368 369 static int 370 amdzentemp_probe_ccd_sensors17h(struct amdzentemp_softc *sc, int model) 371 { 372 int maxreg; 373 374 switch (model) { 375 case 0x00 ... 0x2f: /* Zen1, Zen+ */ 376 sc->sc_ccd_offset = 0x154; 377 maxreg = 4; 378 break; 379 case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */ 380 case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */ 381 case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */ 382 sc->sc_ccd_offset = 0x154; 383 maxreg = 8; 384 break; 385 case 0xa0 ... 0xaf: /* Zen2 Ryzen (Mendocino APU) */ 386 sc->sc_ccd_offset = 0x300; 387 maxreg = 8; 388 break; 389 default: 390 aprint_error_dev(sc->sc_dev, 391 "Unrecognized Family 17h Model: %02xh\n", model); 392 return 0; 393 } 394 395 return maxreg; 396 } 397 398 static int 399 amdzentemp_probe_ccd_sensors19h(struct amdzentemp_softc *sc, int model) 400 { 401 int maxreg; 402 403 switch (model) { 404 case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */ 405 case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */ 406 case 0x50 ... 0x5f: /* Zen3 Ryzen "Cezanne" */ 407 sc->sc_ccd_offset = 0x154; 408 maxreg = 8; 409 break; 410 case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */ 411 case 0x70 ... 0x7f: /* Zen4 Ryzen "Phoenix" */ 412 sc->sc_ccd_offset = 0x308; 413 maxreg = 8; 414 break; 415 case 0x40 ... 0x4f: /* Zen3+ "Rembrandt" */ 416 sc->sc_ccd_offset = 0x300; 417 maxreg = 8; 418 break; 419 case 0x10 ... 0x1f: /* Zen4 "Genoa" */ 420 case 0xa0 ... 0xaf: /* Zen4 "Siena" */ 421 sc->sc_ccd_offset = 0x300; 422 maxreg = 12; 423 break; 424 default: 425 aprint_error_dev(sc->sc_dev, 426 "Unrecognized Family 19h Model: %02xh\n", model); 427 return 0; 428 } 429 430 return maxreg; 431 } 432 433 static int 434 amdzentemp_probe_ccd_sensors1ah(struct amdzentemp_softc *sc, int model) 435 { 436 int maxreg; 437 438 switch (model) { 439 case 0x00 ... 0x0f: /* Zen5 "Turin Classic" */ 440 sc->sc_ccd_offset = 0x300; 441 maxreg = 16; 442 break; 443 case 0x10 ... 0x1f: /* Zen5 "Turin Dense" */ 444 sc->sc_ccd_offset = 0x300; 445 maxreg = 12; 446 break; 447 case 0x20 ... 0x2f: /* Zen5 "Strix Point" */ 448 sc->sc_ccd_offset = 0x300; 449 maxreg = 8; 450 break; 451 case 0x40 ... 0x4f: /* Zen5 "Granite Ridge "*/ 452 sc->sc_ccd_offset = 0x300; 453 maxreg = 8; 454 break; 455 default: 456 aprint_error_dev(sc->sc_dev, 457 "Unrecognized Family 19h Model: %02xh\n", model); 458 return 0; 459 } 460 461 return maxreg; 462 } 463 464 static int 465 amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *sc, int family, int model) 466 { 467 int nccd; 468 469 switch (family) { 470 case 0x17: 471 nccd = amdzentemp_probe_ccd_sensors17h(sc, model); 472 break; 473 case 0x19: 474 nccd = amdzentemp_probe_ccd_sensors19h(sc, model); 475 break; 476 case 0x1a: 477 nccd = amdzentemp_probe_ccd_sensors1ah(sc, model); 478 break; 479 default: 480 return 0; 481 } 482 483 return nccd; 484 } 485 486 static void 487 amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *sc) 488 { 489 envsys_data_t *edata; 490 size_t i; 491 uint32_t temp; 492 int error; 493 494 for (i = 0; i < sc->sc_numsensors - 1; i++) { 495 error = amdsmn_read(sc->sc_smn, 496 AMD_17H_CUR_TMP + sc->sc_ccd_offset + (i * sizeof(temp)), 497 &temp); 498 if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID)) 499 continue; 500 501 edata = &sc->sc_sensor[1 + i]; 502 edata->units = ENVSYS_STEMP; 503 edata->state = ENVSYS_SVALID; 504 edata->flags = ENVSYS_FHAS_ENTROPY; 505 edata->private = CCD_BASE + i; 506 snprintf(edata->desc, sizeof(edata->desc), 507 "cpu%u ccd%zu temperature", device_unit(sc->sc_dev), i); 508 } 509 } 510 511 MODULE(MODULE_CLASS_DRIVER, amdzentemp, "sysmon_envsys,amdsmn"); 512 513 #ifdef _MODULE 514 #include "ioconf.c" 515 #endif 516 517 static int 518 amdzentemp_modcmd(modcmd_t cmd, void *aux) 519 { 520 int error = 0; 521 522 switch (cmd) { 523 case MODULE_CMD_INIT: 524 #ifdef _MODULE 525 error = config_init_component(cfdriver_ioconf_amdzentemp, 526 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 527 #endif 528 return error; 529 case MODULE_CMD_FINI: 530 #ifdef _MODULE 531 error = config_fini_component(cfdriver_ioconf_amdzentemp, 532 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 533 #endif 534 return error; 535 default: 536 return ENOTTY; 537 } 538 } 539