xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision 2e1417489338b971e5fd599ff48b5f65df9e8d3b)
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