1 /*- 2 * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> 3 * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> 4 * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs. 31 * Initially based on the k8temp Linux driver. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/conf.h> 40 #include <sys/kernel.h> 41 #include <sys/module.h> 42 #include <sys/sysctl.h> 43 #include <sys/systm.h> 44 45 #include <machine/cpufunc.h> 46 #include <machine/md_var.h> 47 #include <machine/specialreg.h> 48 49 #include <dev/pci/pcivar.h> 50 51 typedef enum { 52 SENSOR0_CORE0, 53 SENSOR0_CORE1, 54 SENSOR1_CORE0, 55 SENSOR1_CORE1, 56 CORE0, 57 CORE1 58 } amdsensor_t; 59 60 struct amdtemp_softc { 61 device_t sc_dev; 62 int sc_ncores; 63 int sc_ntemps; 64 int sc_flags; 65 #define AMDTEMP_FLAG_DO_QUIRK 0x01 /* DiodeOffset may be incorrect. */ 66 #define AMDTEMP_FLAG_DO_ZERO 0x02 /* DiodeOffset starts from 0C. */ 67 #define AMDTEMP_FLAG_DO_SIGN 0x04 /* DiodeOffsetSignBit is present. */ 68 #define AMDTEMP_FLAG_CS_SWAP 0x08 /* ThermSenseCoreSel is inverted. */ 69 #define AMDTEMP_FLAG_CT_10BIT 0x10 /* CurTmp is 10-bit wide. */ 70 int32_t (*sc_gettemp)(device_t, amdsensor_t); 71 struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 72 struct intr_config_hook sc_ich; 73 }; 74 75 #define VENDORID_AMD 0x1022 76 #define DEVICEID_AMD_MISC0F 0x1103 77 #define DEVICEID_AMD_MISC10 0x1203 78 #define DEVICEID_AMD_MISC11 0x1303 79 80 static struct amdtemp_product { 81 uint16_t amdtemp_vendorid; 82 uint16_t amdtemp_deviceid; 83 } amdtemp_products[] = { 84 { VENDORID_AMD, DEVICEID_AMD_MISC0F }, 85 { VENDORID_AMD, DEVICEID_AMD_MISC10 }, 86 { VENDORID_AMD, DEVICEID_AMD_MISC11 }, 87 { 0, 0 } 88 }; 89 90 /* 91 * Reported Temperature Control Register (Family 10h/11h only) 92 */ 93 #define AMDTEMP_REPTMP_CTRL 0xa4 94 95 /* 96 * Thermaltrip Status Register 97 */ 98 #define AMDTEMP_THERMTP_STAT 0xe4 99 #define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */ 100 #define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */ 101 102 /* 103 * CPU Family/Model Register 104 */ 105 #define AMDTEMP_CPUID 0xfc 106 107 /* 108 * Device methods. 109 */ 110 static void amdtemp_identify(driver_t *driver, device_t parent); 111 static int amdtemp_probe(device_t dev); 112 static int amdtemp_attach(device_t dev); 113 static void amdtemp_intrhook(void *arg); 114 static int amdtemp_detach(device_t dev); 115 static int amdtemp_match(device_t dev); 116 static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 117 static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 118 static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 119 120 static device_method_t amdtemp_methods[] = { 121 /* Device interface */ 122 DEVMETHOD(device_identify, amdtemp_identify), 123 DEVMETHOD(device_probe, amdtemp_probe), 124 DEVMETHOD(device_attach, amdtemp_attach), 125 DEVMETHOD(device_detach, amdtemp_detach), 126 127 {0, 0} 128 }; 129 130 static driver_t amdtemp_driver = { 131 "amdtemp", 132 amdtemp_methods, 133 sizeof(struct amdtemp_softc), 134 }; 135 136 static devclass_t amdtemp_devclass; 137 DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 138 139 static int 140 amdtemp_match(device_t dev) 141 { 142 int i; 143 uint16_t vendor, devid; 144 145 vendor = pci_get_vendor(dev); 146 devid = pci_get_device(dev); 147 148 for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) { 149 if (vendor == amdtemp_products[i].amdtemp_vendorid && 150 devid == amdtemp_products[i].amdtemp_deviceid) 151 return (1); 152 } 153 154 return (0); 155 } 156 157 static void 158 amdtemp_identify(driver_t *driver, device_t parent) 159 { 160 device_t child; 161 162 /* Make sure we're not being doubly invoked. */ 163 if (device_find_child(parent, "amdtemp", -1) != NULL) 164 return; 165 166 if (amdtemp_match(parent)) { 167 child = device_add_child(parent, "amdtemp", -1); 168 if (child == NULL) 169 device_printf(parent, "add amdtemp child failed\n"); 170 } 171 } 172 173 static int 174 amdtemp_probe(device_t dev) 175 { 176 uint32_t family, model; 177 178 if (resource_disabled("amdtemp", 0)) 179 return (ENXIO); 180 181 family = CPUID_TO_FAMILY(cpu_id); 182 model = CPUID_TO_MODEL(cpu_id); 183 184 switch (family) { 185 case 0x0f: 186 if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 187 (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 188 return (ENXIO); 189 break; 190 case 0x10: 191 case 0x11: 192 break; 193 default: 194 return (ENXIO); 195 } 196 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 197 198 return (BUS_PROBE_GENERIC); 199 } 200 201 static int 202 amdtemp_attach(device_t dev) 203 { 204 struct amdtemp_softc *sc = device_get_softc(dev); 205 struct sysctl_ctx_list *sysctlctx; 206 struct sysctl_oid *sysctlnode; 207 uint32_t regs[4]; 208 uint32_t cpuid, family, model; 209 210 /* 211 * Errata #154: Incorect Diode Offset 212 */ 213 if (cpu_id == 0x20f32) { 214 do_cpuid(0x80000001, regs); 215 if ((regs[1] & 0xfff) == 0x2c) 216 sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK; 217 } 218 219 /* 220 * CPUID Register is available from Revision F. 221 */ 222 family = CPUID_TO_FAMILY(cpu_id); 223 model = CPUID_TO_MODEL(cpu_id); 224 if (family != 0x0f || model >= 0x40) { 225 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); 226 family = CPUID_TO_FAMILY(cpuid); 227 model = CPUID_TO_MODEL(cpuid); 228 } 229 230 switch (family) { 231 case 0x0f: 232 /* 233 * Thermaltrip Status Register 234 * 235 * - DiodeOffsetSignBit 236 * 237 * Revision D & E: bit 24 238 * Other: N/A 239 * 240 * - ThermSenseCoreSel 241 * 242 * Revision F & G: 0 - Core1, 1 - Core0 243 * Other: 0 - Core0, 1 - Core1 244 * 245 * - CurTmp 246 * 247 * Revision G: bits 23-14 248 * Other: bits 23-16 249 * 250 * XXX According to the BKDG, CurTmp, ThermSenseSel and 251 * ThermSenseCoreSel bits were introduced in Revision F 252 * but CurTmp seems working fine as early as Revision C. 253 * However, it is not clear whether ThermSenseSel and/or 254 * ThermSenseCoreSel work in undocumented cases as well. 255 * In fact, the Linux driver suggests it may not work but 256 * we just assume it does until we find otherwise. 257 */ 258 if (model < 0x40) { 259 sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO; 260 if (model >= 0x10) 261 sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN; 262 } else { 263 sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 264 if (model >= 0x60 && model != 0xc1) 265 sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 266 } 267 268 /* 269 * There are two sensors per core. 270 */ 271 sc->sc_ntemps = 2; 272 273 sc->sc_gettemp = amdtemp_gettemp0f; 274 break; 275 case 0x10: 276 case 0x11: 277 /* 278 * There is only one sensor per package. 279 */ 280 sc->sc_ntemps = 1; 281 282 sc->sc_gettemp = amdtemp_gettemp; 283 break; 284 } 285 286 /* Find number of cores per package. */ 287 sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 288 (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 289 if (sc->sc_ncores > MAXCPU) 290 return (ENXIO); 291 292 if (bootverbose) 293 device_printf(dev, "Found %d cores and %d sensors.\n", 294 sc->sc_ncores, 295 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 296 297 /* 298 * dev.amdtemp.N tree. 299 */ 300 sysctlctx = device_get_sysctl_ctx(dev); 301 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 302 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 303 "sensor0", CTLFLAG_RD, 0, "Sensor 0"); 304 305 SYSCTL_ADD_PROC(sysctlctx, 306 SYSCTL_CHILDREN(sysctlnode), 307 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 308 dev, SENSOR0_CORE0, amdtemp_sysctl, "IK", 309 "Sensor 0 / Core 0 temperature"); 310 311 if (sc->sc_ntemps > 1) { 312 if (sc->sc_ncores > 1) 313 SYSCTL_ADD_PROC(sysctlctx, 314 SYSCTL_CHILDREN(sysctlnode), 315 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 316 dev, SENSOR0_CORE1, amdtemp_sysctl, "IK", 317 "Sensor 0 / Core 1 temperature"); 318 319 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 320 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 321 "sensor1", CTLFLAG_RD, 0, "Sensor 1"); 322 323 SYSCTL_ADD_PROC(sysctlctx, 324 SYSCTL_CHILDREN(sysctlnode), 325 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 326 dev, SENSOR1_CORE0, amdtemp_sysctl, "IK", 327 "Sensor 1 / Core 0 temperature"); 328 329 if (sc->sc_ncores > 1) 330 SYSCTL_ADD_PROC(sysctlctx, 331 SYSCTL_CHILDREN(sysctlnode), 332 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 333 dev, SENSOR1_CORE1, amdtemp_sysctl, "IK", 334 "Sensor 1 / Core 1 temperature"); 335 } 336 337 /* 338 * Try to create dev.cpu sysctl entries and setup intrhook function. 339 * This is needed because the cpu driver may be loaded late on boot, 340 * after us. 341 */ 342 amdtemp_intrhook(dev); 343 sc->sc_ich.ich_func = amdtemp_intrhook; 344 sc->sc_ich.ich_arg = dev; 345 if (config_intrhook_establish(&sc->sc_ich) != 0) { 346 device_printf(dev, "config_intrhook_establish failed!\n"); 347 return (ENXIO); 348 } 349 350 return (0); 351 } 352 353 void 354 amdtemp_intrhook(void *arg) 355 { 356 struct amdtemp_softc *sc; 357 struct sysctl_ctx_list *sysctlctx; 358 device_t dev = (device_t)arg; 359 device_t acpi, cpu, nexus; 360 amdsensor_t sensor; 361 int i; 362 363 sc = device_get_softc(dev); 364 365 /* 366 * dev.cpu.N.temperature. 367 */ 368 nexus = device_find_child(root_bus, "nexus", 0); 369 acpi = device_find_child(nexus, "acpi", 0); 370 371 for (i = 0; i < sc->sc_ncores; i++) { 372 if (sc->sc_sysctl_cpu[i] != NULL) 373 continue; 374 cpu = device_find_child(acpi, "cpu", 375 device_get_unit(dev) * sc->sc_ncores + i); 376 if (cpu != NULL) { 377 sysctlctx = device_get_sysctl_ctx(cpu); 378 379 sensor = sc->sc_ntemps > 1 ? 380 (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0; 381 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 382 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 383 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 384 dev, sensor, amdtemp_sysctl, "IK", 385 "Current temparature"); 386 } 387 } 388 if (sc->sc_ich.ich_arg != NULL) 389 config_intrhook_disestablish(&sc->sc_ich); 390 } 391 392 int 393 amdtemp_detach(device_t dev) 394 { 395 struct amdtemp_softc *sc = device_get_softc(dev); 396 int i; 397 398 for (i = 0; i < sc->sc_ncores; i++) 399 if (sc->sc_sysctl_cpu[i] != NULL) 400 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 401 402 /* NewBus removes the dev.amdtemp.N tree by itself. */ 403 404 return (0); 405 } 406 407 static int 408 amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 409 { 410 device_t dev = (device_t)arg1; 411 struct amdtemp_softc *sc = device_get_softc(dev); 412 amdsensor_t sensor = (amdsensor_t)arg2; 413 int32_t auxtemp[2], temp; 414 int error; 415 416 switch (sensor) { 417 case CORE0: 418 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0); 419 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0); 420 temp = imax(auxtemp[0], auxtemp[1]); 421 break; 422 case CORE1: 423 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1); 424 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1); 425 temp = imax(auxtemp[0], auxtemp[1]); 426 break; 427 default: 428 temp = sc->sc_gettemp(dev, sensor); 429 break; 430 } 431 error = sysctl_handle_int(oidp, &temp, 0, req); 432 433 return (error); 434 } 435 436 #define AMDTEMP_ZERO_C_TO_K 2732 437 438 static int32_t 439 amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 440 { 441 struct amdtemp_softc *sc = device_get_softc(dev); 442 uint32_t mask, temp; 443 int32_t diode_offset, offset; 444 uint8_t cfg, sel; 445 446 /* Set Sensor/Core selector. */ 447 sel = 0; 448 switch (sensor) { 449 case SENSOR1_CORE0: 450 sel |= AMDTEMP_TTSR_SELSENSOR; 451 /* FALLTHROUGH */ 452 case SENSOR0_CORE0: 453 case CORE0: 454 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 455 sel |= AMDTEMP_TTSR_SELCORE; 456 break; 457 case SENSOR1_CORE1: 458 sel |= AMDTEMP_TTSR_SELSENSOR; 459 /* FALLTHROUGH */ 460 case SENSOR0_CORE1: 461 case CORE1: 462 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 463 sel |= AMDTEMP_TTSR_SELCORE; 464 break; 465 } 466 cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 467 cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE); 468 pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1); 469 470 /* CurTmp starts from -49C. */ 471 offset = AMDTEMP_ZERO_C_TO_K - 490; 472 473 /* Adjust offset if DiodeOffset is set and valid. */ 474 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 475 diode_offset = (temp >> 8) & 0x3f; 476 if ((sc->sc_flags & AMDTEMP_FLAG_DO_ZERO) != 0) { 477 if ((sc->sc_flags & AMDTEMP_FLAG_DO_SIGN) != 0 && 478 ((temp >> 24) & 0x1) != 0) 479 diode_offset *= -1; 480 if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 && 481 ((temp >> 25) & 0xf) <= 2) 482 diode_offset += 10; 483 offset += diode_offset * 10; 484 } else if (diode_offset != 0) 485 offset += (diode_offset - 11) * 10; 486 487 mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 488 temp = ((temp >> 14) & mask) * 5 / 2 + offset; 489 490 return (temp); 491 } 492 493 static int32_t 494 amdtemp_gettemp(device_t dev, amdsensor_t sensor) 495 { 496 uint32_t temp; 497 int32_t diode_offset, offset; 498 499 /* CurTmp starts from 0C. */ 500 offset = AMDTEMP_ZERO_C_TO_K; 501 502 /* Adjust offset if DiodeOffset is set and valid. */ 503 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 504 diode_offset = (temp >> 8) & 0x7f; 505 if (diode_offset > 0 && diode_offset < 0x40) 506 offset += (diode_offset - 11) * 10; 507 508 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 509 temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset; 510 511 return (temp); 512 } 513