amdtemp.c revision 197205
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 197205 2009-09-14 23:08:19Z 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/cpufunc.h>
46#include <machine/md_var.h>
47#include <machine/specialreg.h>
48
49#include <dev/pci/pcivar.h>
50
51typedef enum {
52	SENSOR0_CORE0,
53	SENSOR0_CORE1,
54	SENSOR1_CORE0,
55	SENSOR1_CORE1,
56	CORE0,
57	CORE1
58} amdsensor_t;
59
60struct 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
80static 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 */
110static void 	amdtemp_identify(driver_t *driver, device_t parent);
111static int	amdtemp_probe(device_t dev);
112static int	amdtemp_attach(device_t dev);
113static void	amdtemp_intrhook(void *arg);
114static int	amdtemp_detach(device_t dev);
115static int 	amdtemp_match(device_t dev);
116static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
117static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
118static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
119
120static 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
130static driver_t amdtemp_driver = {
131	"amdtemp",
132	amdtemp_methods,
133	sizeof(struct amdtemp_softc),
134};
135
136static devclass_t amdtemp_devclass;
137DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
138
139static int
140amdtemp_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
157static void
158amdtemp_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
173static int
174amdtemp_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
201static int
202amdtemp_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
353void
354amdtemp_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
392int
393amdtemp_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
407static int
408amdtemp_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
438static int32_t
439amdtemp_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
493static int32_t
494amdtemp_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