xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision 0572ccaa4543b0abef8ef81e384c1d04de9f3da1)
1 /*-
2  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3  * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4  * Copyright (c) 2009-2012 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.
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 #include <x86/pci_cfgreg.h>
51 
52 typedef enum {
53 	CORE0_SENSOR0,
54 	CORE0_SENSOR1,
55 	CORE1_SENSOR0,
56 	CORE1_SENSOR1,
57 	CORE0,
58 	CORE1
59 } amdsensor_t;
60 
61 struct amdtemp_softc {
62 	device_t	sc_dev;
63 	int		sc_ncores;
64 	int		sc_ntemps;
65 	int		sc_flags;
66 #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
67 #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
68 #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
69 	int32_t		sc_offset;
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 #define	DEVICEID_AMD_MISC12	0x1403
80 #define	DEVICEID_AMD_MISC14	0x1703
81 #define	DEVICEID_AMD_MISC15	0x1603
82 #define	DEVICEID_AMD_MISC16	0x1533
83 
84 static struct amdtemp_product {
85 	uint16_t	amdtemp_vendorid;
86 	uint16_t	amdtemp_deviceid;
87 } amdtemp_products[] = {
88 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
89 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
90 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
91 	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
92 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
93 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
94 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
95 	{ 0, 0 }
96 };
97 
98 /*
99  * Reported Temperature Control Register
100  */
101 #define	AMDTEMP_REPTMP_CTRL	0xa4
102 
103 /*
104  * Thermaltrip Status Register (Family 0Fh only)
105  */
106 #define	AMDTEMP_THERMTP_STAT	0xe4
107 #define	AMDTEMP_TTSR_SELCORE	0x04
108 #define	AMDTEMP_TTSR_SELSENSOR	0x40
109 
110 /*
111  * DRAM Configuration High Register
112  */
113 #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
114 #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
115 
116 /*
117  * CPU Family/Model Register
118  */
119 #define	AMDTEMP_CPUID		0xfc
120 
121 /*
122  * Device methods.
123  */
124 static void 	amdtemp_identify(driver_t *driver, device_t parent);
125 static int	amdtemp_probe(device_t dev);
126 static int	amdtemp_attach(device_t dev);
127 static void	amdtemp_intrhook(void *arg);
128 static int	amdtemp_detach(device_t dev);
129 static int 	amdtemp_match(device_t dev);
130 static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
131 static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
132 static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
133 
134 static device_method_t amdtemp_methods[] = {
135 	/* Device interface */
136 	DEVMETHOD(device_identify,	amdtemp_identify),
137 	DEVMETHOD(device_probe,		amdtemp_probe),
138 	DEVMETHOD(device_attach,	amdtemp_attach),
139 	DEVMETHOD(device_detach,	amdtemp_detach),
140 
141 	DEVMETHOD_END
142 };
143 
144 static driver_t amdtemp_driver = {
145 	"amdtemp",
146 	amdtemp_methods,
147 	sizeof(struct amdtemp_softc),
148 };
149 
150 static devclass_t amdtemp_devclass;
151 DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
152 
153 static int
154 amdtemp_match(device_t dev)
155 {
156 	int i;
157 	uint16_t vendor, devid;
158 
159 	vendor = pci_get_vendor(dev);
160 	devid = pci_get_device(dev);
161 
162 	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
163 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
164 		    devid == amdtemp_products[i].amdtemp_deviceid)
165 			return (1);
166 	}
167 
168 	return (0);
169 }
170 
171 static void
172 amdtemp_identify(driver_t *driver, device_t parent)
173 {
174 	device_t child;
175 
176 	/* Make sure we're not being doubly invoked. */
177 	if (device_find_child(parent, "amdtemp", -1) != NULL)
178 		return;
179 
180 	if (amdtemp_match(parent)) {
181 		child = device_add_child(parent, "amdtemp", -1);
182 		if (child == NULL)
183 			device_printf(parent, "add amdtemp child failed\n");
184 	}
185 }
186 
187 static int
188 amdtemp_probe(device_t dev)
189 {
190 	uint32_t family, model;
191 
192 	if (resource_disabled("amdtemp", 0))
193 		return (ENXIO);
194 
195 	family = CPUID_TO_FAMILY(cpu_id);
196 	model = CPUID_TO_MODEL(cpu_id);
197 
198 	switch (family) {
199 	case 0x0f:
200 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
201 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
202 			return (ENXIO);
203 		break;
204 	case 0x10:
205 	case 0x11:
206 	case 0x12:
207 	case 0x14:
208 	case 0x15:
209 	case 0x16:
210 		break;
211 	default:
212 		return (ENXIO);
213 	}
214 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
215 
216 	return (BUS_PROBE_GENERIC);
217 }
218 
219 static int
220 amdtemp_attach(device_t dev)
221 {
222 	char tn[32];
223 	u_int regs[4];
224 	struct amdtemp_softc *sc = device_get_softc(dev);
225 	struct sysctl_ctx_list *sysctlctx;
226 	struct sysctl_oid *sysctlnode;
227 	uint32_t cpuid, family, model;
228 	u_int bid;
229 	int erratum319, unit;
230 
231 	erratum319 = 0;
232 
233 	/*
234 	 * CPUID Register is available from Revision F.
235 	 */
236 	cpuid = cpu_id;
237 	family = CPUID_TO_FAMILY(cpuid);
238 	model = CPUID_TO_MODEL(cpuid);
239 	if (family != 0x0f || model >= 0x40) {
240 		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
241 		family = CPUID_TO_FAMILY(cpuid);
242 		model = CPUID_TO_MODEL(cpuid);
243 	}
244 
245 	switch (family) {
246 	case 0x0f:
247 		/*
248 		 * Thermaltrip Status Register
249 		 *
250 		 * - ThermSenseCoreSel
251 		 *
252 		 * Revision F & G:	0 - Core1, 1 - Core0
253 		 * Other:		0 - Core0, 1 - Core1
254 		 *
255 		 * - CurTmp
256 		 *
257 		 * Revision G:		bits 23-14
258 		 * Other:		bits 23-16
259 		 *
260 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
261 		 * ThermSenseCoreSel bits were introduced in Revision F
262 		 * but CurTmp seems working fine as early as Revision C.
263 		 * However, it is not clear whether ThermSenseSel and/or
264 		 * ThermSenseCoreSel work in undocumented cases as well.
265 		 * In fact, the Linux driver suggests it may not work but
266 		 * we just assume it does until we find otherwise.
267 		 *
268 		 * XXX According to Linux, CurTmp starts at -28C on
269 		 * Socket AM2 Revision G processors, which is not
270 		 * documented anywhere.
271 		 */
272 		if (model >= 0x40)
273 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
274 		if (model >= 0x60 && model != 0xc1) {
275 			do_cpuid(0x80000001, regs);
276 			bid = (regs[1] >> 9) & 0x1f;
277 			switch (model) {
278 			case 0x68: /* Socket S1g1 */
279 			case 0x6c:
280 			case 0x7c:
281 				break;
282 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
283 				if (bid != 0x0b && bid != 0x0c)
284 					sc->sc_flags |=
285 					    AMDTEMP_FLAG_ALT_OFFSET;
286 				break;
287 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
288 			case 0x7f:
289 				if (bid != 0x07 && bid != 0x09 &&
290 				    bid != 0x0c)
291 					sc->sc_flags |=
292 					    AMDTEMP_FLAG_ALT_OFFSET;
293 				break;
294 			default:
295 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
296 			}
297 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
298 		}
299 
300 		/*
301 		 * There are two sensors per core.
302 		 */
303 		sc->sc_ntemps = 2;
304 
305 		sc->sc_gettemp = amdtemp_gettemp0f;
306 		break;
307 	case 0x10:
308 		/*
309 		 * Erratum 319 Inaccurate Temperature Measurement
310 		 *
311 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
312 		 */
313 		do_cpuid(0x80000001, regs);
314 		switch ((regs[1] >> 28) & 0xf) {
315 		case 0:	/* Socket F */
316 			erratum319 = 1;
317 			break;
318 		case 1:	/* Socket AM2+ or AM3 */
319 			if ((pci_cfgregread(pci_get_bus(dev),
320 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
321 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
322 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
323 				break;
324 			/* XXX 00100F42h (RB-C2) exists in both formats. */
325 			erratum319 = 1;
326 			break;
327 		}
328 		/* FALLTHROUGH */
329 	case 0x11:
330 	case 0x12:
331 	case 0x14:
332 	case 0x15:
333 	case 0x16:
334 		/*
335 		 * There is only one sensor per package.
336 		 */
337 		sc->sc_ntemps = 1;
338 
339 		sc->sc_gettemp = amdtemp_gettemp;
340 		break;
341 	}
342 
343 	/* Find number of cores per package. */
344 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
345 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
346 	if (sc->sc_ncores > MAXCPU)
347 		return (ENXIO);
348 
349 	if (erratum319)
350 		device_printf(dev,
351 		    "Erratum 319: temperature measurement may be inaccurate\n");
352 	if (bootverbose)
353 		device_printf(dev, "Found %d cores and %d sensors.\n",
354 		    sc->sc_ncores,
355 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
356 
357 	/*
358 	 * dev.amdtemp.N tree.
359 	 */
360 	unit = device_get_unit(dev);
361 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
362 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
363 
364 	sysctlctx = device_get_sysctl_ctx(dev);
365 	SYSCTL_ADD_INT(sysctlctx,
366 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
367 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
368 	    "Temperature sensor offset");
369 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
370 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
371 	    "core0", CTLFLAG_RD, 0, "Core 0");
372 
373 	SYSCTL_ADD_PROC(sysctlctx,
374 	    SYSCTL_CHILDREN(sysctlnode),
375 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
376 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
377 	    "Core 0 / Sensor 0 temperature");
378 
379 	if (sc->sc_ntemps > 1) {
380 		SYSCTL_ADD_PROC(sysctlctx,
381 		    SYSCTL_CHILDREN(sysctlnode),
382 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
383 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
384 		    "Core 0 / Sensor 1 temperature");
385 
386 		if (sc->sc_ncores > 1) {
387 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
388 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
389 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
390 
391 			SYSCTL_ADD_PROC(sysctlctx,
392 			    SYSCTL_CHILDREN(sysctlnode),
393 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
394 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
395 			    "Core 1 / Sensor 0 temperature");
396 
397 			SYSCTL_ADD_PROC(sysctlctx,
398 			    SYSCTL_CHILDREN(sysctlnode),
399 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
400 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
401 			    "Core 1 / Sensor 1 temperature");
402 		}
403 	}
404 
405 	/*
406 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
407 	 * This is needed because the cpu driver may be loaded late on boot,
408 	 * after us.
409 	 */
410 	amdtemp_intrhook(dev);
411 	sc->sc_ich.ich_func = amdtemp_intrhook;
412 	sc->sc_ich.ich_arg = dev;
413 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
414 		device_printf(dev, "config_intrhook_establish failed!\n");
415 		return (ENXIO);
416 	}
417 
418 	return (0);
419 }
420 
421 void
422 amdtemp_intrhook(void *arg)
423 {
424 	struct amdtemp_softc *sc;
425 	struct sysctl_ctx_list *sysctlctx;
426 	device_t dev = (device_t)arg;
427 	device_t acpi, cpu, nexus;
428 	amdsensor_t sensor;
429 	int i;
430 
431 	sc = device_get_softc(dev);
432 
433 	/*
434 	 * dev.cpu.N.temperature.
435 	 */
436 	nexus = device_find_child(root_bus, "nexus", 0);
437 	acpi = device_find_child(nexus, "acpi", 0);
438 
439 	for (i = 0; i < sc->sc_ncores; i++) {
440 		if (sc->sc_sysctl_cpu[i] != NULL)
441 			continue;
442 		cpu = device_find_child(acpi, "cpu",
443 		    device_get_unit(dev) * sc->sc_ncores + i);
444 		if (cpu != NULL) {
445 			sysctlctx = device_get_sysctl_ctx(cpu);
446 
447 			sensor = sc->sc_ntemps > 1 ?
448 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
449 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
450 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
451 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
452 			    dev, sensor, amdtemp_sysctl, "IK",
453 			    "Current temparature");
454 		}
455 	}
456 	if (sc->sc_ich.ich_arg != NULL)
457 		config_intrhook_disestablish(&sc->sc_ich);
458 }
459 
460 int
461 amdtemp_detach(device_t dev)
462 {
463 	struct amdtemp_softc *sc = device_get_softc(dev);
464 	int i;
465 
466 	for (i = 0; i < sc->sc_ncores; i++)
467 		if (sc->sc_sysctl_cpu[i] != NULL)
468 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
469 
470 	/* NewBus removes the dev.amdtemp.N tree by itself. */
471 
472 	return (0);
473 }
474 
475 static int
476 amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
477 {
478 	device_t dev = (device_t)arg1;
479 	struct amdtemp_softc *sc = device_get_softc(dev);
480 	amdsensor_t sensor = (amdsensor_t)arg2;
481 	int32_t auxtemp[2], temp;
482 	int error;
483 
484 	switch (sensor) {
485 	case CORE0:
486 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
487 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
488 		temp = imax(auxtemp[0], auxtemp[1]);
489 		break;
490 	case CORE1:
491 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
492 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
493 		temp = imax(auxtemp[0], auxtemp[1]);
494 		break;
495 	default:
496 		temp = sc->sc_gettemp(dev, sensor);
497 		break;
498 	}
499 	error = sysctl_handle_int(oidp, &temp, 0, req);
500 
501 	return (error);
502 }
503 
504 #define	AMDTEMP_ZERO_C_TO_K	2732
505 
506 static int32_t
507 amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
508 {
509 	struct amdtemp_softc *sc = device_get_softc(dev);
510 	uint32_t mask, offset, temp;
511 
512 	/* Set Sensor/Core selector. */
513 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
514 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
515 	switch (sensor) {
516 	case CORE0_SENSOR1:
517 		temp |= AMDTEMP_TTSR_SELSENSOR;
518 		/* FALLTHROUGH */
519 	case CORE0_SENSOR0:
520 	case CORE0:
521 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
522 			temp |= AMDTEMP_TTSR_SELCORE;
523 		break;
524 	case CORE1_SENSOR1:
525 		temp |= AMDTEMP_TTSR_SELSENSOR;
526 		/* FALLTHROUGH */
527 	case CORE1_SENSOR0:
528 	case CORE1:
529 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
530 			temp |= AMDTEMP_TTSR_SELCORE;
531 		break;
532 	}
533 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
534 
535 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
536 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
537 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
538 	temp = ((temp >> 14) & mask) * 5 / 2;
539 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
540 
541 	return (temp);
542 }
543 
544 static int32_t
545 amdtemp_gettemp(device_t dev, amdsensor_t sensor)
546 {
547 	struct amdtemp_softc *sc = device_get_softc(dev);
548 	uint32_t temp;
549 
550 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
551 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
552 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
553 
554 	return (temp);
555 }
556