amdtemp.c revision 197102
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: head/sys/dev/amdtemp/amdtemp.c 197102 2009-09-11 21:47:44Z jkim $"); 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/md_var.h> 46#include <machine/specialreg.h> 47 48#include <dev/pci/pcivar.h> 49 50typedef enum { 51 SENSOR0_CORE0, 52 SENSOR0_CORE1, 53 SENSOR1_CORE0, 54 SENSOR1_CORE1, 55 CORE0, 56 CORE1 57} amdsensor_t; 58 59struct amdtemp_softc { 60 device_t sc_dev; 61 uint32_t sc_mask; 62 int sc_ncores; 63 int sc_ntemps; 64 int sc_swap; 65 int32_t (*sc_gettemp)(device_t, amdsensor_t); 66 struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 67 struct intr_config_hook sc_ich; 68}; 69 70#define VENDORID_AMD 0x1022 71#define DEVICEID_AMD_MISC0F 0x1103 72#define DEVICEID_AMD_MISC10 0x1203 73#define DEVICEID_AMD_MISC11 0x1303 74 75static struct amdtemp_product { 76 uint16_t amdtemp_vendorid; 77 uint16_t amdtemp_deviceid; 78} amdtemp_products[] = { 79 { VENDORID_AMD, DEVICEID_AMD_MISC0F }, 80 { VENDORID_AMD, DEVICEID_AMD_MISC10 }, 81 { VENDORID_AMD, DEVICEID_AMD_MISC11 }, 82 { 0, 0 } 83}; 84 85/* 86 * Reported Temperature Control Register (Family 10h/11h only) 87 */ 88#define AMDTEMP_REPTMP_CTRL 0xa4 89 90/* 91 * Thermaltrip Status Register 92 */ 93#define AMDTEMP_THERMTP_STAT 0xe4 94#define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */ 95#define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */ 96 97/* 98 * CPU Family/Model Register 99 */ 100#define AMDTEMP_CPUID 0xfc 101 102/* 103 * Device methods. 104 */ 105static void amdtemp_identify(driver_t *driver, device_t parent); 106static int amdtemp_probe(device_t dev); 107static int amdtemp_attach(device_t dev); 108static void amdtemp_intrhook(void *arg); 109static int amdtemp_detach(device_t dev); 110static int amdtemp_match(device_t dev); 111static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 112static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 113static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 114 115static device_method_t amdtemp_methods[] = { 116 /* Device interface */ 117 DEVMETHOD(device_identify, amdtemp_identify), 118 DEVMETHOD(device_probe, amdtemp_probe), 119 DEVMETHOD(device_attach, amdtemp_attach), 120 DEVMETHOD(device_detach, amdtemp_detach), 121 122 {0, 0} 123}; 124 125static driver_t amdtemp_driver = { 126 "amdtemp", 127 amdtemp_methods, 128 sizeof(struct amdtemp_softc), 129}; 130 131static devclass_t amdtemp_devclass; 132DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 133 134static int 135amdtemp_match(device_t dev) 136{ 137 int i; 138 uint16_t vendor, devid; 139 140 vendor = pci_get_vendor(dev); 141 devid = pci_get_device(dev); 142 143 for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) { 144 if (vendor == amdtemp_products[i].amdtemp_vendorid && 145 devid == amdtemp_products[i].amdtemp_deviceid) 146 return (1); 147 } 148 149 return (0); 150} 151 152static void 153amdtemp_identify(driver_t *driver, device_t parent) 154{ 155 device_t child; 156 157 /* Make sure we're not being doubly invoked. */ 158 if (device_find_child(parent, "amdtemp", -1) != NULL) 159 return; 160 161 if (amdtemp_match(parent)) { 162 child = device_add_child(parent, "amdtemp", -1); 163 if (child == NULL) 164 device_printf(parent, "add amdtemp child failed\n"); 165 } 166} 167 168static int 169amdtemp_probe(device_t dev) 170{ 171 uint32_t cpuid, family, model, temp; 172 173 if (resource_disabled("amdtemp", 0)) 174 return (ENXIO); 175 176 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); 177 family = CPUID_TO_FAMILY(cpuid); 178 model = CPUID_TO_MODEL(cpuid); 179 180 switch (family) { 181 case 0x0f: 182 if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) || 183 (model == 0x05 && (cpuid & CPUID_STEPPING) <= 1)) 184 return (ENXIO); 185 break; 186 case 0x10: 187 case 0x11: 188 /* 189 * DiodeOffset must be non-zero if thermal diode is supported. 190 */ 191 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 192 temp = (temp >> 8) & 0x7f; 193 if (temp == 0) 194 return (ENXIO); 195 break; 196 default: 197 return (ENXIO); 198 } 199 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 200 201 return (BUS_PROBE_GENERIC); 202} 203 204static int 205amdtemp_attach(device_t dev) 206{ 207 struct amdtemp_softc *sc = device_get_softc(dev); 208 struct sysctl_ctx_list *sysctlctx; 209 struct sysctl_oid *sysctlnode; 210 uint32_t cpuid, family, model; 211 212 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); 213 family = CPUID_TO_FAMILY(cpuid); 214 model = CPUID_TO_MODEL(cpuid); 215 216 switch (family) { 217 case 0x0f: 218 /* 219 * Thermaltrip Status Register - CurTmp 220 * 221 * Revision G: bits 23-14 222 * Earlier: bits 23-16 223 */ 224 if (model >= 0x60 && model != 0xc1) 225 sc->sc_mask = 0x3ff << 14; 226 else 227 sc->sc_mask = 0xff << 16; 228 229 /* 230 * Thermaltrip Status Register - ThermSenseCoreSel 231 * 232 * Revision F: 0 - Core1, 1 - Core0 233 * Earlier: 0 - Core0, 1 - Core1 234 */ 235 sc->sc_swap = (model >= 0x40); 236 237 /* 238 * There are two sensors per core. 239 */ 240 sc->sc_ntemps = 2; 241 242 sc->sc_gettemp = amdtemp_gettemp0f; 243 break; 244 case 0x10: 245 case 0x11: 246 /* 247 * Reported Temperature Control Register - Curtmp 248 */ 249 sc->sc_mask = 0x3ff << 21; 250 251 /* 252 * There is only one sensor per package. 253 */ 254 sc->sc_ntemps = 1; 255 256 sc->sc_gettemp = amdtemp_gettemp; 257 break; 258 } 259 260 /* Find number of cores per package. */ 261 sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 262 (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 263 if (sc->sc_ncores > MAXCPU) 264 return (ENXIO); 265 266 if (bootverbose) 267 device_printf(dev, "Found %d cores and %d sensors.\n", 268 sc->sc_ncores, 269 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 270 271 /* 272 * dev.amdtemp.N tree. 273 */ 274 sysctlctx = device_get_sysctl_ctx(dev); 275 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 276 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 277 "sensor0", CTLFLAG_RD, 0, "Sensor 0"); 278 279 SYSCTL_ADD_PROC(sysctlctx, 280 SYSCTL_CHILDREN(sysctlnode), 281 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 282 dev, SENSOR0_CORE0, amdtemp_sysctl, "IK", 283 "Sensor 0 / Core 0 temperature"); 284 285 if (sc->sc_ntemps > 1) { 286 if (sc->sc_ncores > 1) 287 SYSCTL_ADD_PROC(sysctlctx, 288 SYSCTL_CHILDREN(sysctlnode), 289 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 290 dev, SENSOR0_CORE1, amdtemp_sysctl, "IK", 291 "Sensor 0 / Core 1 temperature"); 292 293 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 294 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 295 "sensor1", CTLFLAG_RD, 0, "Sensor 1"); 296 297 SYSCTL_ADD_PROC(sysctlctx, 298 SYSCTL_CHILDREN(sysctlnode), 299 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 300 dev, SENSOR1_CORE0, amdtemp_sysctl, "IK", 301 "Sensor 1 / Core 0 temperature"); 302 303 if (sc->sc_ncores > 1) 304 SYSCTL_ADD_PROC(sysctlctx, 305 SYSCTL_CHILDREN(sysctlnode), 306 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 307 dev, SENSOR1_CORE1, amdtemp_sysctl, "IK", 308 "Sensor 1 / Core 1 temperature"); 309 } 310 311 /* 312 * Try to create dev.cpu sysctl entries and setup intrhook function. 313 * This is needed because the cpu driver may be loaded late on boot, 314 * after us. 315 */ 316 amdtemp_intrhook(dev); 317 sc->sc_ich.ich_func = amdtemp_intrhook; 318 sc->sc_ich.ich_arg = dev; 319 if (config_intrhook_establish(&sc->sc_ich) != 0) { 320 device_printf(dev, "config_intrhook_establish failed!\n"); 321 return (ENXIO); 322 } 323 324 return (0); 325} 326 327void 328amdtemp_intrhook(void *arg) 329{ 330 struct amdtemp_softc *sc; 331 struct sysctl_ctx_list *sysctlctx; 332 device_t dev = (device_t)arg; 333 device_t acpi, cpu, nexus; 334 amdsensor_t sensor; 335 int i; 336 337 sc = device_get_softc(dev); 338 339 /* 340 * dev.cpu.N.temperature. 341 */ 342 nexus = device_find_child(root_bus, "nexus", 0); 343 acpi = device_find_child(nexus, "acpi", 0); 344 345 for (i = 0; i < sc->sc_ncores; i++) { 346 if (sc->sc_sysctl_cpu[i] != NULL) 347 continue; 348 cpu = device_find_child(acpi, "cpu", 349 device_get_unit(dev) * sc->sc_ncores + i); 350 if (cpu != NULL) { 351 sysctlctx = device_get_sysctl_ctx(cpu); 352 353 sensor = sc->sc_ntemps > 1 ? 354 (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0; 355 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 356 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 357 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 358 dev, sensor, amdtemp_sysctl, "IK", 359 "Current temparature"); 360 } 361 } 362 if (sc->sc_ich.ich_arg != NULL) 363 config_intrhook_disestablish(&sc->sc_ich); 364} 365 366int 367amdtemp_detach(device_t dev) 368{ 369 struct amdtemp_softc *sc = device_get_softc(dev); 370 int i; 371 372 for (i = 0; i < sc->sc_ncores; i++) 373 if (sc->sc_sysctl_cpu[i] != NULL) 374 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 375 376 /* NewBus removes the dev.amdtemp.N tree by itself. */ 377 378 return (0); 379} 380 381static int 382amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 383{ 384 device_t dev = (device_t)arg1; 385 struct amdtemp_softc *sc = device_get_softc(dev); 386 amdsensor_t sensor = (amdsensor_t)arg2; 387 int32_t auxtemp[2], temp; 388 int error; 389 390 switch (sensor) { 391 case CORE0: 392 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0); 393 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0); 394 temp = imax(auxtemp[0], auxtemp[1]); 395 break; 396 case CORE1: 397 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1); 398 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1); 399 temp = imax(auxtemp[0], auxtemp[1]); 400 break; 401 default: 402 temp = sc->sc_gettemp(dev, sensor); 403 break; 404 } 405 error = sysctl_handle_int(oidp, &temp, 0, req); 406 407 return (error); 408} 409 410#define AMDTEMP_ZERO_C_TO_K 2732 411 412static int32_t 413amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 414{ 415 struct amdtemp_softc *sc = device_get_softc(dev); 416 uint32_t temp; 417 int32_t diode_offset, offset; 418 uint8_t cfg, sel; 419 420 /* Set Sensor/Core selector. */ 421 sel = 0; 422 switch (sensor) { 423 case SENSOR1_CORE0: 424 sel |= AMDTEMP_TTSR_SELSENSOR; 425 /* FALLTROUGH */ 426 case SENSOR0_CORE0: 427 case CORE0: 428 if (sc->sc_swap) 429 sel |= AMDTEMP_TTSR_SELCORE; 430 break; 431 case SENSOR1_CORE1: 432 sel |= AMDTEMP_TTSR_SELSENSOR; 433 /* FALLTROUGH */ 434 case SENSOR0_CORE1: 435 case CORE1: 436 if (!sc->sc_swap) 437 sel |= AMDTEMP_TTSR_SELCORE; 438 break; 439 } 440 cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 441 cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE); 442 pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1); 443 444 /* CurTmp starts from -49C. */ 445 offset = AMDTEMP_ZERO_C_TO_K - 490; 446 447 /* Adjust offset if DiodeOffset is set and valid. */ 448 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 449 diode_offset = (temp >> 8) & 0x3f; 450 if (diode_offset != 0) 451 offset += (diode_offset - 11) * 10; 452 453 temp = ((temp & sc->sc_mask) >> 14) * 5 / 2 + offset; 454 455 return (temp); 456} 457 458static int32_t 459amdtemp_gettemp(device_t dev, amdsensor_t sensor) 460{ 461 struct amdtemp_softc *sc = device_get_softc(dev); 462 uint32_t temp; 463 int32_t diode_offset, offset; 464 465 /* CurTmp starts from 0C. */ 466 offset = AMDTEMP_ZERO_C_TO_K; 467 468 /* Adjust offset if DiodeOffset is set and valid. */ 469 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 470 diode_offset = (temp >> 8) & 0x7f; 471 if (diode_offset > 0 && diode_offset < 0x40) 472 offset += (diode_offset - 11) * 10; 473 474 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 475 temp = ((temp & sc->sc_mask) >> 21) * 5 / 4 + offset; 476 477 return (temp); 478} 479