xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision fbd5d782096a2ab5a9b212ca7fdcf70050f89f56)
1fc1f75e5SRui Paulo /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4454e82d7SRui Paulo  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
5454e82d7SRui Paulo  * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
6074d80acSJung-uk Kim  * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
7fc1f75e5SRui Paulo  * All rights reserved.
8fc1f75e5SRui Paulo  *
9fc1f75e5SRui Paulo  * Redistribution and use in source and binary forms, with or without
10fc1f75e5SRui Paulo  * modification, are permitted provided that the following conditions
11fc1f75e5SRui Paulo  * are met:
12fc1f75e5SRui Paulo  * 1. Redistributions of source code must retain the above copyright
13fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer.
14fc1f75e5SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
15fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
16fc1f75e5SRui Paulo  *    documentation and/or other materials provided with the distribution.
17fc1f75e5SRui Paulo  *
18fc1f75e5SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19fc1f75e5SRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20fc1f75e5SRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21fc1f75e5SRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22fc1f75e5SRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23fc1f75e5SRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24fc1f75e5SRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25fc1f75e5SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26fc1f75e5SRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27fc1f75e5SRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28fc1f75e5SRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
29fc1f75e5SRui Paulo  */
30fc1f75e5SRui Paulo 
31fc1f75e5SRui Paulo /*
32074d80acSJung-uk Kim  * Driver for the AMD CPU on-die thermal sensors.
33a4165bbaSJung-uk Kim  * Initially based on the k8temp Linux driver.
34fc1f75e5SRui Paulo  */
35fc1f75e5SRui Paulo 
36fc1f75e5SRui Paulo #include <sys/cdefs.h>
37fc1f75e5SRui Paulo __FBSDID("$FreeBSD$");
38fc1f75e5SRui Paulo 
39fc1f75e5SRui Paulo #include <sys/param.h>
40fc1f75e5SRui Paulo #include <sys/bus.h>
41fc1f75e5SRui Paulo #include <sys/conf.h>
42fc1f75e5SRui Paulo #include <sys/kernel.h>
43a4165bbaSJung-uk Kim #include <sys/module.h>
44fc1f75e5SRui Paulo #include <sys/sysctl.h>
45a4165bbaSJung-uk Kim #include <sys/systm.h>
46fc1f75e5SRui Paulo 
47fdfa6079SJung-uk Kim #include <machine/cpufunc.h>
48fc1f75e5SRui Paulo #include <machine/md_var.h>
49a4165bbaSJung-uk Kim #include <machine/specialreg.h>
50fc1f75e5SRui Paulo 
51fc1f75e5SRui Paulo #include <dev/pci/pcivar.h>
52074d80acSJung-uk Kim #include <x86/pci_cfgreg.h>
53fc1f75e5SRui Paulo 
54a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h>
55a03d621bSConrad Meyer 
56fc1f75e5SRui Paulo typedef enum {
57074d80acSJung-uk Kim 	CORE0_SENSOR0,
58074d80acSJung-uk Kim 	CORE0_SENSOR1,
59074d80acSJung-uk Kim 	CORE1_SENSOR0,
60074d80acSJung-uk Kim 	CORE1_SENSOR1,
61fc1f75e5SRui Paulo 	CORE0,
62fc1f75e5SRui Paulo 	CORE1
63454e82d7SRui Paulo } amdsensor_t;
64454e82d7SRui Paulo 
65454e82d7SRui Paulo struct amdtemp_softc {
66a4165bbaSJung-uk Kim 	int		sc_ncores;
67454e82d7SRui Paulo 	int		sc_ntemps;
68fdfa6079SJung-uk Kim 	int		sc_flags;
69074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
70074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
71074d80acSJung-uk Kim #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
72074d80acSJung-uk Kim 	int32_t		sc_offset;
73454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
74a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
75a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
76a03d621bSConrad Meyer 	device_t	sc_smn;
77454e82d7SRui Paulo };
78454e82d7SRui Paulo 
79454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
80454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
81454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
82454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
83534b11a1SJohn-Mark Gurney #define	DEVICEID_AMD_MISC12	0x1403
84074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
85074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
862b56f12bSChristian Brueffer #define	DEVICEID_AMD_MISC16	0x1533
87df20515dSLuiz Otavio O Souza #define	DEVICEID_AMD_MISC16_M30H	0x1583
889bfafa10SChristian Brueffer #define	DEVICEID_AMD_MISC17	0x141d
899d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_ROOT	0x1450
909d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_DF_F3	0x1463
919d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M10H_ROOT	0x15d0
929d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M10H_DF_F3 0x15eb
93454e82d7SRui Paulo 
94454e82d7SRui Paulo static struct amdtemp_product {
95454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
96454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
97454e82d7SRui Paulo } amdtemp_products[] = {
98454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
99454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
100454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
101534b11a1SJohn-Mark Gurney 	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
102074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
103074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
1042b56f12bSChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
105df20515dSLuiz Otavio O Souza 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H },
1069bfafa10SChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC17 },
1079d49c422SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_ROOT },
1089d49c422SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M10H_ROOT },
109454e82d7SRui Paulo };
110454e82d7SRui Paulo 
111454e82d7SRui Paulo /*
112074d80acSJung-uk Kim  * Reported Temperature Control Register
113454e82d7SRui Paulo  */
114a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
115454e82d7SRui Paulo 
116454e82d7SRui Paulo /*
117a03d621bSConrad Meyer  * Reported Temperature, Family 17h
118*fbd5d782SConrad Meyer  *
119*fbd5d782SConrad Meyer  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
120*fbd5d782SConrad Meyer  * provide the current temp.  bit 19, when clear, means the temp is reported in
121*fbd5d782SConrad Meyer  * a range 0.."225C" (probable typo for 255C), and when set changes the range
122*fbd5d782SConrad Meyer  * to -49..206C.
123a03d621bSConrad Meyer  */
124a03d621bSConrad Meyer #define	AMDTEMP_17H_CUR_TMP		0x59800
125*fbd5d782SConrad Meyer #define	AMDTEMP_17H_CUR_TMP_RANGE_SEL	(1 << 19)
126*fbd5d782SConrad Meyer #define	AMDTEMP_17H_CUR_TMP_RANGE_OFF	490
127a03d621bSConrad Meyer 
128a03d621bSConrad Meyer /*
129074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
130454e82d7SRui Paulo  */
131a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
132074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
133074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
134074d80acSJung-uk Kim 
135074d80acSJung-uk Kim /*
136074d80acSJung-uk Kim  * DRAM Configuration High Register
137074d80acSJung-uk Kim  */
138074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
139074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
140454e82d7SRui Paulo 
141a4165bbaSJung-uk Kim /*
142a4165bbaSJung-uk Kim  * CPU Family/Model Register
143a4165bbaSJung-uk Kim  */
144a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
145fc1f75e5SRui Paulo 
146fc1f75e5SRui Paulo /*
147fc1f75e5SRui Paulo  * Device methods.
148fc1f75e5SRui Paulo  */
149454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
150454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
151454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
152454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
153454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
154454e82d7SRui Paulo static int 	amdtemp_match(device_t dev);
155454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
156454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
157a03d621bSConrad Meyer static int32_t	amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
158454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
159fc1f75e5SRui Paulo 
160454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
161fc1f75e5SRui Paulo 	/* Device interface */
162454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
163454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
164454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
165454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
166fc1f75e5SRui Paulo 
16761bfd867SSofian Brabez 	DEVMETHOD_END
168fc1f75e5SRui Paulo };
169fc1f75e5SRui Paulo 
170454e82d7SRui Paulo static driver_t amdtemp_driver = {
171454e82d7SRui Paulo 	"amdtemp",
172454e82d7SRui Paulo 	amdtemp_methods,
173454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
174fc1f75e5SRui Paulo };
175fc1f75e5SRui Paulo 
176454e82d7SRui Paulo static devclass_t amdtemp_devclass;
177454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
178a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1);
179a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
180a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
181329e817fSWarner Losh     nitems(amdtemp_products));
182fc1f75e5SRui Paulo 
183fc1f75e5SRui Paulo static int
184454e82d7SRui Paulo amdtemp_match(device_t dev)
185fc1f75e5SRui Paulo {
186fc1f75e5SRui Paulo 	int i;
187fc1f75e5SRui Paulo 	uint16_t vendor, devid;
188fc1f75e5SRui Paulo 
189fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
190fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
191fc1f75e5SRui Paulo 
192a64bf59cSConrad Meyer 	for (i = 0; i < nitems(amdtemp_products); i++) {
193454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
194454e82d7SRui Paulo 		    devid == amdtemp_products[i].amdtemp_deviceid)
195fc1f75e5SRui Paulo 			return (1);
196fc1f75e5SRui Paulo 	}
197fc1f75e5SRui Paulo 
198fc1f75e5SRui Paulo 	return (0);
199fc1f75e5SRui Paulo }
200fc1f75e5SRui Paulo 
201fc1f75e5SRui Paulo static void
202454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
203fc1f75e5SRui Paulo {
204fc1f75e5SRui Paulo 	device_t child;
205fc1f75e5SRui Paulo 
206fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
207454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
208fc1f75e5SRui Paulo 		return;
209fc1f75e5SRui Paulo 
210454e82d7SRui Paulo 	if (amdtemp_match(parent)) {
211454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
212fc1f75e5SRui Paulo 		if (child == NULL)
213454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
214fc1f75e5SRui Paulo 	}
215fc1f75e5SRui Paulo }
216fc1f75e5SRui Paulo 
217fc1f75e5SRui Paulo static int
218454e82d7SRui Paulo amdtemp_probe(device_t dev)
219fc1f75e5SRui Paulo {
220fdfa6079SJung-uk Kim 	uint32_t family, model;
221fc1f75e5SRui Paulo 
222a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
223a8de37b0SEitan Adler 		return (ENXIO);
22440f7bccbSConrad Meyer 	if (!amdtemp_match(device_get_parent(dev)))
22540f7bccbSConrad Meyer 		return (ENXIO);
226a8de37b0SEitan Adler 
227fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
228fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
229a4165bbaSJung-uk Kim 
230a4165bbaSJung-uk Kim 	switch (family) {
231a4165bbaSJung-uk Kim 	case 0x0f:
232fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
233fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
234a4165bbaSJung-uk Kim 			return (ENXIO);
235a4165bbaSJung-uk Kim 		break;
236a4165bbaSJung-uk Kim 	case 0x10:
237a4165bbaSJung-uk Kim 	case 0x11:
238074d80acSJung-uk Kim 	case 0x12:
239074d80acSJung-uk Kim 	case 0x14:
240074d80acSJung-uk Kim 	case 0x15:
2412b56f12bSChristian Brueffer 	case 0x16:
242a03d621bSConrad Meyer 	case 0x17:
243a4165bbaSJung-uk Kim 		break;
244a4165bbaSJung-uk Kim 	default:
245fc1f75e5SRui Paulo 		return (ENXIO);
246fc1f75e5SRui Paulo 	}
247a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
248fc1f75e5SRui Paulo 
249fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
250fc1f75e5SRui Paulo }
251fc1f75e5SRui Paulo 
252fc1f75e5SRui Paulo static int
253454e82d7SRui Paulo amdtemp_attach(device_t dev)
254fc1f75e5SRui Paulo {
255074d80acSJung-uk Kim 	char tn[32];
256074d80acSJung-uk Kim 	u_int regs[4];
257454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
258fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
259fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
260a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
261074d80acSJung-uk Kim 	u_int bid;
262074d80acSJung-uk Kim 	int erratum319, unit;
263fc1f75e5SRui Paulo 
264074d80acSJung-uk Kim 	erratum319 = 0;
265fdfa6079SJung-uk Kim 
266fdfa6079SJung-uk Kim 	/*
267fdfa6079SJung-uk Kim 	 * CPUID Register is available from Revision F.
268fdfa6079SJung-uk Kim 	 */
269074d80acSJung-uk Kim 	cpuid = cpu_id;
270074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
271074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
272a03d621bSConrad Meyer 	if ((family != 0x0f || model >= 0x40) && family != 0x17) {
273a4165bbaSJung-uk Kim 		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
274a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
275a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
276fdfa6079SJung-uk Kim 	}
277a4165bbaSJung-uk Kim 
278a4165bbaSJung-uk Kim 	switch (family) {
279a4165bbaSJung-uk Kim 	case 0x0f:
280a4165bbaSJung-uk Kim 		/*
281fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
282fdfa6079SJung-uk Kim 		 *
283fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
284fdfa6079SJung-uk Kim 		 *
285fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
286fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
287fdfa6079SJung-uk Kim 		 *
288fdfa6079SJung-uk Kim 		 * - CurTmp
289a4165bbaSJung-uk Kim 		 *
290a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
291fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
292a4165bbaSJung-uk Kim 		 *
293fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
294fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
295fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
296fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
297fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
298fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
299fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
300074d80acSJung-uk Kim 		 *
301074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
302074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
303074d80acSJung-uk Kim 		 * documented anywhere.
304fc1f75e5SRui Paulo 		 */
305074d80acSJung-uk Kim 		if (model >= 0x40)
306fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
307074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
308074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
309074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
310074d80acSJung-uk Kim 			switch (model) {
311074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
312074d80acSJung-uk Kim 			case 0x6c:
313074d80acSJung-uk Kim 			case 0x7c:
314074d80acSJung-uk Kim 				break;
315074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
316074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
317074d80acSJung-uk Kim 					sc->sc_flags |=
318074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
319074d80acSJung-uk Kim 				break;
320074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
321074d80acSJung-uk Kim 			case 0x7f:
322074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
323074d80acSJung-uk Kim 				    bid != 0x0c)
324074d80acSJung-uk Kim 					sc->sc_flags |=
325074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
326074d80acSJung-uk Kim 				break;
327074d80acSJung-uk Kim 			default:
328074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
329074d80acSJung-uk Kim 			}
330fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
331fdfa6079SJung-uk Kim 		}
332a4165bbaSJung-uk Kim 
333a4165bbaSJung-uk Kim 		/*
334a4165bbaSJung-uk Kim 		 * There are two sensors per core.
335a4165bbaSJung-uk Kim 		 */
336a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
337a4165bbaSJung-uk Kim 
338a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
339a4165bbaSJung-uk Kim 		break;
340a4165bbaSJung-uk Kim 	case 0x10:
341074d80acSJung-uk Kim 		/*
342074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
343074d80acSJung-uk Kim 		 *
344074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
345074d80acSJung-uk Kim 		 */
346074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
347074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
348074d80acSJung-uk Kim 		case 0:	/* Socket F */
349074d80acSJung-uk Kim 			erratum319 = 1;
350074d80acSJung-uk Kim 			break;
351074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
352074d80acSJung-uk Kim 			if ((pci_cfgregread(pci_get_bus(dev),
353074d80acSJung-uk Kim 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
354074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
355074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
356074d80acSJung-uk Kim 				break;
357074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
358074d80acSJung-uk Kim 			erratum319 = 1;
359074d80acSJung-uk Kim 			break;
360074d80acSJung-uk Kim 		}
361074d80acSJung-uk Kim 		/* FALLTHROUGH */
362a4165bbaSJung-uk Kim 	case 0x11:
363074d80acSJung-uk Kim 	case 0x12:
364074d80acSJung-uk Kim 	case 0x14:
365074d80acSJung-uk Kim 	case 0x15:
3662b56f12bSChristian Brueffer 	case 0x16:
367a4165bbaSJung-uk Kim 		/*
368a4165bbaSJung-uk Kim 		 * There is only one sensor per package.
369a4165bbaSJung-uk Kim 		 */
370a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
371a4165bbaSJung-uk Kim 
372a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp;
373a4165bbaSJung-uk Kim 		break;
374a03d621bSConrad Meyer 	case 0x17:
375a03d621bSConrad Meyer 		sc->sc_ntemps = 1;
376a03d621bSConrad Meyer 		sc->sc_gettemp = amdtemp_gettemp17h;
377a03d621bSConrad Meyer 		sc->sc_smn = device_find_child(
378a03d621bSConrad Meyer 		    device_get_parent(dev), "amdsmn", -1);
379a03d621bSConrad Meyer 		if (sc->sc_smn == NULL) {
380a03d621bSConrad Meyer 			if (bootverbose)
381a03d621bSConrad Meyer 				device_printf(dev, "No SMN device found\n");
382a03d621bSConrad Meyer 			return (ENXIO);
383a03d621bSConrad Meyer 		}
384a03d621bSConrad Meyer 		break;
385fc1f75e5SRui Paulo 	}
386fc1f75e5SRui Paulo 
387a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
388a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
389a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
390a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
391a4165bbaSJung-uk Kim 		return (ENXIO);
392a4165bbaSJung-uk Kim 
393074d80acSJung-uk Kim 	if (erratum319)
394074d80acSJung-uk Kim 		device_printf(dev,
395074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
396a4165bbaSJung-uk Kim 	if (bootverbose)
397a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
398a4165bbaSJung-uk Kim 		    sc->sc_ncores,
399a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
400454e82d7SRui Paulo 
401fc1f75e5SRui Paulo 	/*
402454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
403fc1f75e5SRui Paulo 	 */
404074d80acSJung-uk Kim 	unit = device_get_unit(dev);
405074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
406074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
407074d80acSJung-uk Kim 
408fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
409074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
410074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
411074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
412074d80acSJung-uk Kim 	    "Temperature sensor offset");
413fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
414a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
415074d80acSJung-uk Kim 	    "core0", CTLFLAG_RD, 0, "Core 0");
416fc1f75e5SRui Paulo 
417fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
418fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
419074d80acSJung-uk Kim 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
420074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
421074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
422fc1f75e5SRui Paulo 
423a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
424fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
425fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
426074d80acSJung-uk Kim 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
427074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
428074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
429fc1f75e5SRui Paulo 
430074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
431fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
432074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
433074d80acSJung-uk Kim 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
434fc1f75e5SRui Paulo 
435fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
436fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
437074d80acSJung-uk Kim 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
438074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
439074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
440fc1f75e5SRui Paulo 
441fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
442fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
443074d80acSJung-uk Kim 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
444074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
445074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
446074d80acSJung-uk Kim 		}
447a4165bbaSJung-uk Kim 	}
448a4165bbaSJung-uk Kim 
449a4165bbaSJung-uk Kim 	/*
450a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
451a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
452a4165bbaSJung-uk Kim 	 * after us.
453a4165bbaSJung-uk Kim 	 */
454a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
455a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
456a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
457a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
458a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
459a4165bbaSJung-uk Kim 		return (ENXIO);
460a4165bbaSJung-uk Kim 	}
461fc1f75e5SRui Paulo 
462fc1f75e5SRui Paulo 	return (0);
463fc1f75e5SRui Paulo }
464fc1f75e5SRui Paulo 
465fc1f75e5SRui Paulo void
466454e82d7SRui Paulo amdtemp_intrhook(void *arg)
467fc1f75e5SRui Paulo {
468454e82d7SRui Paulo 	struct amdtemp_softc *sc;
469fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
470a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
471a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
472a4165bbaSJung-uk Kim 	amdsensor_t sensor;
473a4165bbaSJung-uk Kim 	int i;
474fc1f75e5SRui Paulo 
475fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
476fc1f75e5SRui Paulo 
477fc1f75e5SRui Paulo 	/*
478fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
479fc1f75e5SRui Paulo 	 */
480fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
481fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
482fc1f75e5SRui Paulo 
483a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
484a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
485a4165bbaSJung-uk Kim 			continue;
486fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
487a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
488a4165bbaSJung-uk Kim 		if (cpu != NULL) {
489fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
490fc1f75e5SRui Paulo 
491a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
492074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
493fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
494fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
495fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
496a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
497a4165bbaSJung-uk Kim 			    "Current temparature");
498fc1f75e5SRui Paulo 		}
499fc1f75e5SRui Paulo 	}
500a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
501fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
502fc1f75e5SRui Paulo }
503fc1f75e5SRui Paulo 
504fc1f75e5SRui Paulo int
505454e82d7SRui Paulo amdtemp_detach(device_t dev)
506fc1f75e5SRui Paulo {
507454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
508a4165bbaSJung-uk Kim 	int i;
509fc1f75e5SRui Paulo 
510a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
511a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
512fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
513fc1f75e5SRui Paulo 
514454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
515fc1f75e5SRui Paulo 
516fc1f75e5SRui Paulo 	return (0);
517fc1f75e5SRui Paulo }
518fc1f75e5SRui Paulo 
519fc1f75e5SRui Paulo static int
520454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
521fc1f75e5SRui Paulo {
522fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
523454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
524a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
525a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
526fc1f75e5SRui Paulo 	int error;
527fc1f75e5SRui Paulo 
528a4165bbaSJung-uk Kim 	switch (sensor) {
529fc1f75e5SRui Paulo 	case CORE0:
530074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
531074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
532fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
533fc1f75e5SRui Paulo 		break;
534fc1f75e5SRui Paulo 	case CORE1:
535074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
536074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
537fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
538fc1f75e5SRui Paulo 		break;
539fc1f75e5SRui Paulo 	default:
540a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
541fc1f75e5SRui Paulo 		break;
542fc1f75e5SRui Paulo 	}
543fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
544fc1f75e5SRui Paulo 
545fc1f75e5SRui Paulo 	return (error);
546fc1f75e5SRui Paulo }
547fc1f75e5SRui Paulo 
5489d6672e1SLuiz Otavio O Souza #define	AMDTEMP_ZERO_C_TO_K	2731
549a4165bbaSJung-uk Kim 
550fc1f75e5SRui Paulo static int32_t
551454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
552fc1f75e5SRui Paulo {
553a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
554074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
555fc1f75e5SRui Paulo 
556a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
557074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
558074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
559fc1f75e5SRui Paulo 	switch (sensor) {
560074d80acSJung-uk Kim 	case CORE0_SENSOR1:
561074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5627ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
563074d80acSJung-uk Kim 	case CORE0_SENSOR0:
564a4165bbaSJung-uk Kim 	case CORE0:
565fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
566074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
567fc1f75e5SRui Paulo 		break;
568074d80acSJung-uk Kim 	case CORE1_SENSOR1:
569074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5707ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
571074d80acSJung-uk Kim 	case CORE1_SENSOR0:
572a4165bbaSJung-uk Kim 	case CORE1:
573fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
574074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
575fc1f75e5SRui Paulo 		break;
576fc1f75e5SRui Paulo 	}
577074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
578a4165bbaSJung-uk Kim 
579fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
580074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
581074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
582074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
583074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
584454e82d7SRui Paulo 
585454e82d7SRui Paulo 	return (temp);
586454e82d7SRui Paulo }
587454e82d7SRui Paulo 
588454e82d7SRui Paulo static int32_t
589454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
590454e82d7SRui Paulo {
591074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
592454e82d7SRui Paulo 	uint32_t temp;
593a4165bbaSJung-uk Kim 
594a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
595074d80acSJung-uk Kim 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
596074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
597fc1f75e5SRui Paulo 
598fc1f75e5SRui Paulo 	return (temp);
599fc1f75e5SRui Paulo }
600a03d621bSConrad Meyer 
601a03d621bSConrad Meyer static int32_t
602a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
603a03d621bSConrad Meyer {
604a03d621bSConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
605*fbd5d782SConrad Meyer 	uint32_t temp, val;
606a03d621bSConrad Meyer 	int error;
607a03d621bSConrad Meyer 
608*fbd5d782SConrad Meyer 	error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val);
609a03d621bSConrad Meyer 	KASSERT(error == 0, ("amdsmn_read"));
610a03d621bSConrad Meyer 
611*fbd5d782SConrad Meyer 	temp = ((val >> 21) & 0x7ff) * 5 / 4;
612*fbd5d782SConrad Meyer 	if ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0)
613*fbd5d782SConrad Meyer 		temp -= AMDTEMP_17H_CUR_TMP_RANGE_OFF;
614a03d621bSConrad Meyer 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
615a03d621bSConrad Meyer 
616a03d621bSConrad Meyer 	return (temp);
617a03d621bSConrad Meyer }
618