xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision a64bf59c499c36895b9018d4124f3fcd71ac2cb8)
1fc1f75e5SRui Paulo /*-
2454e82d7SRui Paulo  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3454e82d7SRui Paulo  * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4074d80acSJung-uk Kim  * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
5fc1f75e5SRui Paulo  * All rights reserved.
6fc1f75e5SRui Paulo  *
7fc1f75e5SRui Paulo  * Redistribution and use in source and binary forms, with or without
8fc1f75e5SRui Paulo  * modification, are permitted provided that the following conditions
9fc1f75e5SRui Paulo  * are met:
10fc1f75e5SRui Paulo  * 1. Redistributions of source code must retain the above copyright
11fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer.
12fc1f75e5SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
13fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
14fc1f75e5SRui Paulo  *    documentation and/or other materials provided with the distribution.
15fc1f75e5SRui Paulo  *
16fc1f75e5SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17fc1f75e5SRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18fc1f75e5SRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19fc1f75e5SRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20fc1f75e5SRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21fc1f75e5SRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22fc1f75e5SRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fc1f75e5SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24fc1f75e5SRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25fc1f75e5SRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26fc1f75e5SRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
27fc1f75e5SRui Paulo  */
28fc1f75e5SRui Paulo 
29fc1f75e5SRui Paulo /*
30074d80acSJung-uk Kim  * Driver for the AMD CPU on-die thermal sensors.
31a4165bbaSJung-uk Kim  * Initially based on the k8temp Linux driver.
32fc1f75e5SRui Paulo  */
33fc1f75e5SRui Paulo 
34fc1f75e5SRui Paulo #include <sys/cdefs.h>
35fc1f75e5SRui Paulo __FBSDID("$FreeBSD$");
36fc1f75e5SRui Paulo 
37fc1f75e5SRui Paulo #include <sys/param.h>
38fc1f75e5SRui Paulo #include <sys/bus.h>
39fc1f75e5SRui Paulo #include <sys/conf.h>
40fc1f75e5SRui Paulo #include <sys/kernel.h>
41a4165bbaSJung-uk Kim #include <sys/module.h>
42fc1f75e5SRui Paulo #include <sys/sysctl.h>
43a4165bbaSJung-uk Kim #include <sys/systm.h>
44fc1f75e5SRui Paulo 
45fdfa6079SJung-uk Kim #include <machine/cpufunc.h>
46fc1f75e5SRui Paulo #include <machine/md_var.h>
47a4165bbaSJung-uk Kim #include <machine/specialreg.h>
48fc1f75e5SRui Paulo 
49fc1f75e5SRui Paulo #include <dev/pci/pcivar.h>
50074d80acSJung-uk Kim #include <x86/pci_cfgreg.h>
51fc1f75e5SRui Paulo 
52a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h>
53a03d621bSConrad Meyer 
54fc1f75e5SRui Paulo typedef enum {
55074d80acSJung-uk Kim 	CORE0_SENSOR0,
56074d80acSJung-uk Kim 	CORE0_SENSOR1,
57074d80acSJung-uk Kim 	CORE1_SENSOR0,
58074d80acSJung-uk Kim 	CORE1_SENSOR1,
59fc1f75e5SRui Paulo 	CORE0,
60fc1f75e5SRui Paulo 	CORE1
61454e82d7SRui Paulo } amdsensor_t;
62454e82d7SRui Paulo 
63454e82d7SRui Paulo struct amdtemp_softc {
64a4165bbaSJung-uk Kim 	int		sc_ncores;
65454e82d7SRui Paulo 	int		sc_ntemps;
66fdfa6079SJung-uk Kim 	int		sc_flags;
67074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
68074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
69074d80acSJung-uk Kim #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
70074d80acSJung-uk Kim 	int32_t		sc_offset;
71454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
72a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
73a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
74a03d621bSConrad Meyer 	device_t	sc_smn;
75454e82d7SRui Paulo };
76454e82d7SRui Paulo 
77454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
78454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
79454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
80454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
81534b11a1SJohn-Mark Gurney #define	DEVICEID_AMD_MISC12	0x1403
82074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
83074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
842b56f12bSChristian Brueffer #define	DEVICEID_AMD_MISC16	0x1533
85df20515dSLuiz Otavio O Souza #define	DEVICEID_AMD_MISC16_M30H	0x1583
869bfafa10SChristian Brueffer #define	DEVICEID_AMD_MISC17	0x141d
87a03d621bSConrad Meyer #define	DEVICEID_AMD_HOSTB17H	0x1450
88454e82d7SRui Paulo 
89454e82d7SRui Paulo static struct amdtemp_product {
90454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
91454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
92454e82d7SRui Paulo } amdtemp_products[] = {
93454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
94454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
95454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
96534b11a1SJohn-Mark Gurney 	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
97074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
98074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
992b56f12bSChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
100df20515dSLuiz Otavio O Souza 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H },
1019bfafa10SChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC17 },
102a03d621bSConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H },
103454e82d7SRui Paulo };
104454e82d7SRui Paulo 
105454e82d7SRui Paulo /*
106074d80acSJung-uk Kim  * Reported Temperature Control Register
107454e82d7SRui Paulo  */
108a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
109454e82d7SRui Paulo 
110454e82d7SRui Paulo /*
111a03d621bSConrad Meyer  * Reported Temperature, Family 17h
112a03d621bSConrad Meyer  */
113a03d621bSConrad Meyer #define	AMDTEMP_17H_CUR_TMP	0x59800
114a03d621bSConrad Meyer 
115a03d621bSConrad Meyer /*
116074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
117454e82d7SRui Paulo  */
118a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
119074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
120074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
121074d80acSJung-uk Kim 
122074d80acSJung-uk Kim /*
123074d80acSJung-uk Kim  * DRAM Configuration High Register
124074d80acSJung-uk Kim  */
125074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
126074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
127454e82d7SRui Paulo 
128a4165bbaSJung-uk Kim /*
129a4165bbaSJung-uk Kim  * CPU Family/Model Register
130a4165bbaSJung-uk Kim  */
131a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
132fc1f75e5SRui Paulo 
133fc1f75e5SRui Paulo /*
134fc1f75e5SRui Paulo  * Device methods.
135fc1f75e5SRui Paulo  */
136454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
137454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
138454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
139454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
140454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
141454e82d7SRui Paulo static int 	amdtemp_match(device_t dev);
142454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
143454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
144a03d621bSConrad Meyer static int32_t	amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
145454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
146fc1f75e5SRui Paulo 
147454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
148fc1f75e5SRui Paulo 	/* Device interface */
149454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
150454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
151454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
152454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
153fc1f75e5SRui Paulo 
15461bfd867SSofian Brabez 	DEVMETHOD_END
155fc1f75e5SRui Paulo };
156fc1f75e5SRui Paulo 
157454e82d7SRui Paulo static driver_t amdtemp_driver = {
158454e82d7SRui Paulo 	"amdtemp",
159454e82d7SRui Paulo 	amdtemp_methods,
160454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
161fc1f75e5SRui Paulo };
162fc1f75e5SRui Paulo 
163454e82d7SRui Paulo static devclass_t amdtemp_devclass;
164454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
165a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1);
166a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
167*a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
168*a64bf59cSConrad Meyer     sizeof(amdtemp_products[0]), nitems(amdtemp_products));
169fc1f75e5SRui Paulo 
170fc1f75e5SRui Paulo static int
171454e82d7SRui Paulo amdtemp_match(device_t dev)
172fc1f75e5SRui Paulo {
173fc1f75e5SRui Paulo 	int i;
174fc1f75e5SRui Paulo 	uint16_t vendor, devid;
175fc1f75e5SRui Paulo 
176fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
177fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
178fc1f75e5SRui Paulo 
179*a64bf59cSConrad Meyer 	for (i = 0; i < nitems(amdtemp_products); i++) {
180454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
181454e82d7SRui Paulo 		    devid == amdtemp_products[i].amdtemp_deviceid)
182fc1f75e5SRui Paulo 			return (1);
183fc1f75e5SRui Paulo 	}
184fc1f75e5SRui Paulo 
185fc1f75e5SRui Paulo 	return (0);
186fc1f75e5SRui Paulo }
187fc1f75e5SRui Paulo 
188fc1f75e5SRui Paulo static void
189454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
190fc1f75e5SRui Paulo {
191fc1f75e5SRui Paulo 	device_t child;
192fc1f75e5SRui Paulo 
193fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
194454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
195fc1f75e5SRui Paulo 		return;
196fc1f75e5SRui Paulo 
197454e82d7SRui Paulo 	if (amdtemp_match(parent)) {
198454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
199fc1f75e5SRui Paulo 		if (child == NULL)
200454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
201fc1f75e5SRui Paulo 	}
202fc1f75e5SRui Paulo }
203fc1f75e5SRui Paulo 
204fc1f75e5SRui Paulo static int
205454e82d7SRui Paulo amdtemp_probe(device_t dev)
206fc1f75e5SRui Paulo {
207fdfa6079SJung-uk Kim 	uint32_t family, model;
208fc1f75e5SRui Paulo 
209a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
210a8de37b0SEitan Adler 		return (ENXIO);
21140f7bccbSConrad Meyer 	if (!amdtemp_match(device_get_parent(dev)))
21240f7bccbSConrad Meyer 		return (ENXIO);
213a8de37b0SEitan Adler 
214fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
215fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
216a4165bbaSJung-uk Kim 
217a4165bbaSJung-uk Kim 	switch (family) {
218a4165bbaSJung-uk Kim 	case 0x0f:
219fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
220fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
221a4165bbaSJung-uk Kim 			return (ENXIO);
222a4165bbaSJung-uk Kim 		break;
223a4165bbaSJung-uk Kim 	case 0x10:
224a4165bbaSJung-uk Kim 	case 0x11:
225074d80acSJung-uk Kim 	case 0x12:
226074d80acSJung-uk Kim 	case 0x14:
227074d80acSJung-uk Kim 	case 0x15:
2282b56f12bSChristian Brueffer 	case 0x16:
229a03d621bSConrad Meyer 	case 0x17:
230a4165bbaSJung-uk Kim 		break;
231a4165bbaSJung-uk Kim 	default:
232fc1f75e5SRui Paulo 		return (ENXIO);
233fc1f75e5SRui Paulo 	}
234a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
235fc1f75e5SRui Paulo 
236fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
237fc1f75e5SRui Paulo }
238fc1f75e5SRui Paulo 
239fc1f75e5SRui Paulo static int
240454e82d7SRui Paulo amdtemp_attach(device_t dev)
241fc1f75e5SRui Paulo {
242074d80acSJung-uk Kim 	char tn[32];
243074d80acSJung-uk Kim 	u_int regs[4];
244454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
245fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
246fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
247a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
248074d80acSJung-uk Kim 	u_int bid;
249074d80acSJung-uk Kim 	int erratum319, unit;
250fc1f75e5SRui Paulo 
251074d80acSJung-uk Kim 	erratum319 = 0;
252fdfa6079SJung-uk Kim 
253fdfa6079SJung-uk Kim 	/*
254fdfa6079SJung-uk Kim 	 * CPUID Register is available from Revision F.
255fdfa6079SJung-uk Kim 	 */
256074d80acSJung-uk Kim 	cpuid = cpu_id;
257074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
258074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
259a03d621bSConrad Meyer 	if ((family != 0x0f || model >= 0x40) && family != 0x17) {
260a4165bbaSJung-uk Kim 		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
261a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
262a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
263fdfa6079SJung-uk Kim 	}
264a4165bbaSJung-uk Kim 
265a4165bbaSJung-uk Kim 	switch (family) {
266a4165bbaSJung-uk Kim 	case 0x0f:
267a4165bbaSJung-uk Kim 		/*
268fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
269fdfa6079SJung-uk Kim 		 *
270fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
271fdfa6079SJung-uk Kim 		 *
272fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
273fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
274fdfa6079SJung-uk Kim 		 *
275fdfa6079SJung-uk Kim 		 * - CurTmp
276a4165bbaSJung-uk Kim 		 *
277a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
278fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
279a4165bbaSJung-uk Kim 		 *
280fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
281fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
282fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
283fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
284fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
285fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
286fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
287074d80acSJung-uk Kim 		 *
288074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
289074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
290074d80acSJung-uk Kim 		 * documented anywhere.
291fc1f75e5SRui Paulo 		 */
292074d80acSJung-uk Kim 		if (model >= 0x40)
293fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
294074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
295074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
296074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
297074d80acSJung-uk Kim 			switch (model) {
298074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
299074d80acSJung-uk Kim 			case 0x6c:
300074d80acSJung-uk Kim 			case 0x7c:
301074d80acSJung-uk Kim 				break;
302074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
303074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
304074d80acSJung-uk Kim 					sc->sc_flags |=
305074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
306074d80acSJung-uk Kim 				break;
307074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
308074d80acSJung-uk Kim 			case 0x7f:
309074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
310074d80acSJung-uk Kim 				    bid != 0x0c)
311074d80acSJung-uk Kim 					sc->sc_flags |=
312074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
313074d80acSJung-uk Kim 				break;
314074d80acSJung-uk Kim 			default:
315074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
316074d80acSJung-uk Kim 			}
317fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
318fdfa6079SJung-uk Kim 		}
319a4165bbaSJung-uk Kim 
320a4165bbaSJung-uk Kim 		/*
321a4165bbaSJung-uk Kim 		 * There are two sensors per core.
322a4165bbaSJung-uk Kim 		 */
323a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
324a4165bbaSJung-uk Kim 
325a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
326a4165bbaSJung-uk Kim 		break;
327a4165bbaSJung-uk Kim 	case 0x10:
328074d80acSJung-uk Kim 		/*
329074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
330074d80acSJung-uk Kim 		 *
331074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
332074d80acSJung-uk Kim 		 */
333074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
334074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
335074d80acSJung-uk Kim 		case 0:	/* Socket F */
336074d80acSJung-uk Kim 			erratum319 = 1;
337074d80acSJung-uk Kim 			break;
338074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
339074d80acSJung-uk Kim 			if ((pci_cfgregread(pci_get_bus(dev),
340074d80acSJung-uk Kim 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
341074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
342074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
343074d80acSJung-uk Kim 				break;
344074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
345074d80acSJung-uk Kim 			erratum319 = 1;
346074d80acSJung-uk Kim 			break;
347074d80acSJung-uk Kim 		}
348074d80acSJung-uk Kim 		/* FALLTHROUGH */
349a4165bbaSJung-uk Kim 	case 0x11:
350074d80acSJung-uk Kim 	case 0x12:
351074d80acSJung-uk Kim 	case 0x14:
352074d80acSJung-uk Kim 	case 0x15:
3532b56f12bSChristian Brueffer 	case 0x16:
354a4165bbaSJung-uk Kim 		/*
355a4165bbaSJung-uk Kim 		 * There is only one sensor per package.
356a4165bbaSJung-uk Kim 		 */
357a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
358a4165bbaSJung-uk Kim 
359a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp;
360a4165bbaSJung-uk Kim 		break;
361a03d621bSConrad Meyer 	case 0x17:
362a03d621bSConrad Meyer 		sc->sc_ntemps = 1;
363a03d621bSConrad Meyer 		sc->sc_gettemp = amdtemp_gettemp17h;
364a03d621bSConrad Meyer 		sc->sc_smn = device_find_child(
365a03d621bSConrad Meyer 		    device_get_parent(dev), "amdsmn", -1);
366a03d621bSConrad Meyer 		if (sc->sc_smn == NULL) {
367a03d621bSConrad Meyer 			if (bootverbose)
368a03d621bSConrad Meyer 				device_printf(dev, "No SMN device found\n");
369a03d621bSConrad Meyer 			return (ENXIO);
370a03d621bSConrad Meyer 		}
371a03d621bSConrad Meyer 		break;
372fc1f75e5SRui Paulo 	}
373fc1f75e5SRui Paulo 
374a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
375a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
376a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
377a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
378a4165bbaSJung-uk Kim 		return (ENXIO);
379a4165bbaSJung-uk Kim 
380074d80acSJung-uk Kim 	if (erratum319)
381074d80acSJung-uk Kim 		device_printf(dev,
382074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
383a4165bbaSJung-uk Kim 	if (bootverbose)
384a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
385a4165bbaSJung-uk Kim 		    sc->sc_ncores,
386a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
387454e82d7SRui Paulo 
388fc1f75e5SRui Paulo 	/*
389454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
390fc1f75e5SRui Paulo 	 */
391074d80acSJung-uk Kim 	unit = device_get_unit(dev);
392074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
393074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
394074d80acSJung-uk Kim 
395fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
396074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
397074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
398074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
399074d80acSJung-uk Kim 	    "Temperature sensor offset");
400fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
401a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
402074d80acSJung-uk Kim 	    "core0", CTLFLAG_RD, 0, "Core 0");
403fc1f75e5SRui Paulo 
404fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
405fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
406074d80acSJung-uk Kim 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
407074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
408074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
409fc1f75e5SRui Paulo 
410a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
411fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
412fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
413074d80acSJung-uk Kim 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
414074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
415074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
416fc1f75e5SRui Paulo 
417074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
418fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
419074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
420074d80acSJung-uk Kim 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
421fc1f75e5SRui Paulo 
422fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
423fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
424074d80acSJung-uk Kim 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
425074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
426074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
427fc1f75e5SRui Paulo 
428fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
429fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
430074d80acSJung-uk Kim 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
431074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
432074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
433074d80acSJung-uk Kim 		}
434a4165bbaSJung-uk Kim 	}
435a4165bbaSJung-uk Kim 
436a4165bbaSJung-uk Kim 	/*
437a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
438a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
439a4165bbaSJung-uk Kim 	 * after us.
440a4165bbaSJung-uk Kim 	 */
441a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
442a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
443a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
444a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
445a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
446a4165bbaSJung-uk Kim 		return (ENXIO);
447a4165bbaSJung-uk Kim 	}
448fc1f75e5SRui Paulo 
449fc1f75e5SRui Paulo 	return (0);
450fc1f75e5SRui Paulo }
451fc1f75e5SRui Paulo 
452fc1f75e5SRui Paulo void
453454e82d7SRui Paulo amdtemp_intrhook(void *arg)
454fc1f75e5SRui Paulo {
455454e82d7SRui Paulo 	struct amdtemp_softc *sc;
456fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
457a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
458a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
459a4165bbaSJung-uk Kim 	amdsensor_t sensor;
460a4165bbaSJung-uk Kim 	int i;
461fc1f75e5SRui Paulo 
462fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
463fc1f75e5SRui Paulo 
464fc1f75e5SRui Paulo 	/*
465fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
466fc1f75e5SRui Paulo 	 */
467fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
468fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
469fc1f75e5SRui Paulo 
470a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
471a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
472a4165bbaSJung-uk Kim 			continue;
473fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
474a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
475a4165bbaSJung-uk Kim 		if (cpu != NULL) {
476fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
477fc1f75e5SRui Paulo 
478a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
479074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
480fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
481fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
482fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
483a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
484a4165bbaSJung-uk Kim 			    "Current temparature");
485fc1f75e5SRui Paulo 		}
486fc1f75e5SRui Paulo 	}
487a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
488fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
489fc1f75e5SRui Paulo }
490fc1f75e5SRui Paulo 
491fc1f75e5SRui Paulo int
492454e82d7SRui Paulo amdtemp_detach(device_t dev)
493fc1f75e5SRui Paulo {
494454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
495a4165bbaSJung-uk Kim 	int i;
496fc1f75e5SRui Paulo 
497a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
498a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
499fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
500fc1f75e5SRui Paulo 
501454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
502fc1f75e5SRui Paulo 
503fc1f75e5SRui Paulo 	return (0);
504fc1f75e5SRui Paulo }
505fc1f75e5SRui Paulo 
506fc1f75e5SRui Paulo static int
507454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
508fc1f75e5SRui Paulo {
509fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
510454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
511a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
512a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
513fc1f75e5SRui Paulo 	int error;
514fc1f75e5SRui Paulo 
515a4165bbaSJung-uk Kim 	switch (sensor) {
516fc1f75e5SRui Paulo 	case CORE0:
517074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
518074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
519fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
520fc1f75e5SRui Paulo 		break;
521fc1f75e5SRui Paulo 	case CORE1:
522074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
523074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
524fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
525fc1f75e5SRui Paulo 		break;
526fc1f75e5SRui Paulo 	default:
527a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
528fc1f75e5SRui Paulo 		break;
529fc1f75e5SRui Paulo 	}
530fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
531fc1f75e5SRui Paulo 
532fc1f75e5SRui Paulo 	return (error);
533fc1f75e5SRui Paulo }
534fc1f75e5SRui Paulo 
5359d6672e1SLuiz Otavio O Souza #define	AMDTEMP_ZERO_C_TO_K	2731
536a4165bbaSJung-uk Kim 
537fc1f75e5SRui Paulo static int32_t
538454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
539fc1f75e5SRui Paulo {
540a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
541074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
542fc1f75e5SRui Paulo 
543a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
544074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
545074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
546fc1f75e5SRui Paulo 	switch (sensor) {
547074d80acSJung-uk Kim 	case CORE0_SENSOR1:
548074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5497ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
550074d80acSJung-uk Kim 	case CORE0_SENSOR0:
551a4165bbaSJung-uk Kim 	case CORE0:
552fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
553074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
554fc1f75e5SRui Paulo 		break;
555074d80acSJung-uk Kim 	case CORE1_SENSOR1:
556074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5577ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
558074d80acSJung-uk Kim 	case CORE1_SENSOR0:
559a4165bbaSJung-uk Kim 	case CORE1:
560fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
561074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
562fc1f75e5SRui Paulo 		break;
563fc1f75e5SRui Paulo 	}
564074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
565a4165bbaSJung-uk Kim 
566fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
567074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
568074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
569074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
570074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
571454e82d7SRui Paulo 
572454e82d7SRui Paulo 	return (temp);
573454e82d7SRui Paulo }
574454e82d7SRui Paulo 
575454e82d7SRui Paulo static int32_t
576454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
577454e82d7SRui Paulo {
578074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
579454e82d7SRui Paulo 	uint32_t temp;
580a4165bbaSJung-uk Kim 
581a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
582074d80acSJung-uk Kim 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
583074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
584fc1f75e5SRui Paulo 
585fc1f75e5SRui Paulo 	return (temp);
586fc1f75e5SRui Paulo }
587a03d621bSConrad Meyer 
588a03d621bSConrad Meyer static int32_t
589a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
590a03d621bSConrad Meyer {
591a03d621bSConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
592a03d621bSConrad Meyer 	uint32_t temp;
593a03d621bSConrad Meyer 	int error;
594a03d621bSConrad Meyer 
595a03d621bSConrad Meyer 	error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &temp);
596a03d621bSConrad Meyer 	KASSERT(error == 0, ("amdsmn_read"));
597a03d621bSConrad Meyer 
598a03d621bSConrad Meyer 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
599a03d621bSConrad Meyer 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
600a03d621bSConrad Meyer 
601a03d621bSConrad Meyer 	return (temp);
602a03d621bSConrad Meyer }
603