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>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
30 * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs.
31 * Initially based on the k8temp Linux driver.
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/sysctl.h>
43 #include <sys/systm.h>
45 #include <machine/cpufunc.h>
46 #include <machine/md_var.h>
47 #include <machine/specialreg.h>
49 #include <dev/pci/pcivar.h>
60 struct amdtemp_softc {
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;
75 #define VENDORID_AMD 0x1022
76 #define DEVICEID_AMD_MISC0F 0x1103
77 #define DEVICEID_AMD_MISC10 0x1203
78 #define DEVICEID_AMD_MISC11 0x1303
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 },
91 * Reported Temperature Control Register (Family 10h/11h only)
93 #define AMDTEMP_REPTMP_CTRL 0xa4
96 * Thermaltrip Status Register
98 #define AMDTEMP_THERMTP_STAT 0xe4
99 #define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */
100 #define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */
103 * CPU Family/Model Register
105 #define AMDTEMP_CPUID 0xfc
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);
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),
130 static driver_t amdtemp_driver = {
133 sizeof(struct amdtemp_softc),
136 static devclass_t amdtemp_devclass;
137 DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
140 amdtemp_match(device_t dev)
143 uint16_t vendor, devid;
145 vendor = pci_get_vendor(dev);
146 devid = pci_get_device(dev);
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)
158 amdtemp_identify(driver_t *driver, device_t parent)
162 /* Make sure we're not being doubly invoked. */
163 if (device_find_child(parent, "amdtemp", -1) != NULL)
166 if (amdtemp_match(parent)) {
167 child = device_add_child(parent, "amdtemp", -1);
169 device_printf(parent, "add amdtemp child failed\n");
174 amdtemp_probe(device_t dev)
176 uint32_t family, model;
178 if (resource_disabled("amdtemp", 0))
181 family = CPUID_TO_FAMILY(cpu_id);
182 model = CPUID_TO_MODEL(cpu_id);
186 if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
187 (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
196 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
198 return (BUS_PROBE_GENERIC);
202 amdtemp_attach(device_t dev)
204 struct amdtemp_softc *sc = device_get_softc(dev);
205 struct sysctl_ctx_list *sysctlctx;
206 struct sysctl_oid *sysctlnode;
208 uint32_t cpuid, family, model;
211 * Errata #154: Incorect Diode Offset
213 if (cpu_id == 0x20f32) {
214 do_cpuid(0x80000001, regs);
215 if ((regs[1] & 0xfff) == 0x2c)
216 sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK;
220 * CPUID Register is available from Revision F.
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);
233 * Thermaltrip Status Register
235 * - DiodeOffsetSignBit
237 * Revision D & E: bit 24
240 * - ThermSenseCoreSel
242 * Revision F & G: 0 - Core1, 1 - Core0
243 * Other: 0 - Core0, 1 - Core1
247 * Revision G: bits 23-14
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.
259 sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO;
261 sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN;
263 sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
264 if (model >= 0x60 && model != 0xc1)
265 sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
269 * There are two sensors per core.
273 sc->sc_gettemp = amdtemp_gettemp0f;
278 * There is only one sensor per package.
282 sc->sc_gettemp = amdtemp_gettemp;
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)
293 device_printf(dev, "Found %d cores and %d sensors.\n",
295 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
298 * dev.amdtemp.N tree.
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");
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");
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");
319 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
320 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
321 "sensor1", CTLFLAG_RD, 0, "Sensor 1");
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");
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");
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,
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");
354 amdtemp_intrhook(void *arg)
356 struct amdtemp_softc *sc;
357 struct sysctl_ctx_list *sysctlctx;
358 device_t dev = (device_t)arg;
359 device_t acpi, cpu, nexus;
363 sc = device_get_softc(dev);
366 * dev.cpu.N.temperature.
368 nexus = device_find_child(root_bus, "nexus", 0);
369 acpi = device_find_child(nexus, "acpi", 0);
371 for (i = 0; i < sc->sc_ncores; i++) {
372 if (sc->sc_sysctl_cpu[i] != NULL)
374 cpu = device_find_child(acpi, "cpu",
375 device_get_unit(dev) * sc->sc_ncores + i);
377 sysctlctx = device_get_sysctl_ctx(cpu);
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");
388 if (sc->sc_ich.ich_arg != NULL)
389 config_intrhook_disestablish(&sc->sc_ich);
393 amdtemp_detach(device_t dev)
395 struct amdtemp_softc *sc = device_get_softc(dev);
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);
402 /* NewBus removes the dev.amdtemp.N tree by itself. */
408 amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
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;
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]);
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]);
428 temp = sc->sc_gettemp(dev, sensor);
431 error = sysctl_handle_int(oidp, &temp, 0, req);
436 #define AMDTEMP_ZERO_C_TO_K 2732
439 amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
441 struct amdtemp_softc *sc = device_get_softc(dev);
443 int32_t diode_offset, offset;
446 /* Set Sensor/Core selector. */
450 sel |= AMDTEMP_TTSR_SELSENSOR;
454 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
455 sel |= AMDTEMP_TTSR_SELCORE;
458 sel |= AMDTEMP_TTSR_SELSENSOR;
462 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
463 sel |= AMDTEMP_TTSR_SELCORE;
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);
470 /* CurTmp starts from -49C. */
471 offset = AMDTEMP_ZERO_C_TO_K - 490;
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)
480 if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 &&
481 ((temp >> 25) & 0xf) <= 2)
483 offset += diode_offset * 10;
484 } else if (diode_offset != 0)
485 offset += (diode_offset - 11) * 10;
487 mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
488 temp = ((temp >> 14) & mask) * 5 / 2 + offset;
494 amdtemp_gettemp(device_t dev, amdsensor_t sensor)
497 int32_t diode_offset, offset;
499 /* CurTmp starts from 0C. */
500 offset = AMDTEMP_ZERO_C_TO_K;
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;
508 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
509 temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset;