xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision 61bfd867626dad25026bafcbc5fbc595d9e85417)
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 
52fc1f75e5SRui Paulo typedef enum {
53074d80acSJung-uk Kim 	CORE0_SENSOR0,
54074d80acSJung-uk Kim 	CORE0_SENSOR1,
55074d80acSJung-uk Kim 	CORE1_SENSOR0,
56074d80acSJung-uk Kim 	CORE1_SENSOR1,
57fc1f75e5SRui Paulo 	CORE0,
58fc1f75e5SRui Paulo 	CORE1
59454e82d7SRui Paulo } amdsensor_t;
60454e82d7SRui Paulo 
61454e82d7SRui Paulo struct amdtemp_softc {
62454e82d7SRui Paulo 	device_t	sc_dev;
63a4165bbaSJung-uk Kim 	int		sc_ncores;
64454e82d7SRui Paulo 	int		sc_ntemps;
65fdfa6079SJung-uk Kim 	int		sc_flags;
66074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
67074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
68074d80acSJung-uk Kim #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
69074d80acSJung-uk Kim 	int32_t		sc_offset;
70454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
71a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
72a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
73454e82d7SRui Paulo };
74454e82d7SRui Paulo 
75454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
76454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
77454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
78454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
79074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
80074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
81454e82d7SRui Paulo 
82454e82d7SRui Paulo static struct amdtemp_product {
83454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
84454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
85454e82d7SRui Paulo } amdtemp_products[] = {
86454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
87454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
88454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
89074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
90074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
91454e82d7SRui Paulo 	{ 0, 0 }
92454e82d7SRui Paulo };
93454e82d7SRui Paulo 
94454e82d7SRui Paulo /*
95074d80acSJung-uk Kim  * Reported Temperature Control Register
96454e82d7SRui Paulo  */
97a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
98454e82d7SRui Paulo 
99454e82d7SRui Paulo /*
100074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
101454e82d7SRui Paulo  */
102a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
103074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
104074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
105074d80acSJung-uk Kim 
106074d80acSJung-uk Kim /*
107074d80acSJung-uk Kim  * DRAM Configuration High Register
108074d80acSJung-uk Kim  */
109074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
110074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
111454e82d7SRui Paulo 
112a4165bbaSJung-uk Kim /*
113a4165bbaSJung-uk Kim  * CPU Family/Model Register
114a4165bbaSJung-uk Kim  */
115a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
116fc1f75e5SRui Paulo 
117fc1f75e5SRui Paulo /*
118fc1f75e5SRui Paulo  * Device methods.
119fc1f75e5SRui Paulo  */
120454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
121454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
122454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
123454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
124454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
125454e82d7SRui Paulo static int 	amdtemp_match(device_t dev);
126454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
127454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
128454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
129fc1f75e5SRui Paulo 
130454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
131fc1f75e5SRui Paulo 	/* Device interface */
132454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
133454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
134454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
135454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
136fc1f75e5SRui Paulo 
137*61bfd867SSofian Brabez 	DEVMETHOD_END
138fc1f75e5SRui Paulo };
139fc1f75e5SRui Paulo 
140454e82d7SRui Paulo static driver_t amdtemp_driver = {
141454e82d7SRui Paulo 	"amdtemp",
142454e82d7SRui Paulo 	amdtemp_methods,
143454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
144fc1f75e5SRui Paulo };
145fc1f75e5SRui Paulo 
146454e82d7SRui Paulo static devclass_t amdtemp_devclass;
147454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
148fc1f75e5SRui Paulo 
149fc1f75e5SRui Paulo static int
150454e82d7SRui Paulo amdtemp_match(device_t dev)
151fc1f75e5SRui Paulo {
152fc1f75e5SRui Paulo 	int i;
153fc1f75e5SRui Paulo 	uint16_t vendor, devid;
154fc1f75e5SRui Paulo 
155fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
156fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
157fc1f75e5SRui Paulo 
158454e82d7SRui Paulo 	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
159454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
160454e82d7SRui Paulo 		    devid == amdtemp_products[i].amdtemp_deviceid)
161fc1f75e5SRui Paulo 			return (1);
162fc1f75e5SRui Paulo 	}
163fc1f75e5SRui Paulo 
164fc1f75e5SRui Paulo 	return (0);
165fc1f75e5SRui Paulo }
166fc1f75e5SRui Paulo 
167fc1f75e5SRui Paulo static void
168454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
169fc1f75e5SRui Paulo {
170fc1f75e5SRui Paulo 	device_t child;
171fc1f75e5SRui Paulo 
172fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
173454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
174fc1f75e5SRui Paulo 		return;
175fc1f75e5SRui Paulo 
176454e82d7SRui Paulo 	if (amdtemp_match(parent)) {
177454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
178fc1f75e5SRui Paulo 		if (child == NULL)
179454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
180fc1f75e5SRui Paulo 	}
181fc1f75e5SRui Paulo }
182fc1f75e5SRui Paulo 
183fc1f75e5SRui Paulo static int
184454e82d7SRui Paulo amdtemp_probe(device_t dev)
185fc1f75e5SRui Paulo {
186fdfa6079SJung-uk Kim 	uint32_t family, model;
187fc1f75e5SRui Paulo 
188a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
189a8de37b0SEitan Adler 		return (ENXIO);
190a8de37b0SEitan Adler 
191fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
192fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
193a4165bbaSJung-uk Kim 
194a4165bbaSJung-uk Kim 	switch (family) {
195a4165bbaSJung-uk Kim 	case 0x0f:
196fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
197fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
198a4165bbaSJung-uk Kim 			return (ENXIO);
199a4165bbaSJung-uk Kim 		break;
200a4165bbaSJung-uk Kim 	case 0x10:
201a4165bbaSJung-uk Kim 	case 0x11:
202074d80acSJung-uk Kim 	case 0x12:
203074d80acSJung-uk Kim 	case 0x14:
204074d80acSJung-uk Kim 	case 0x15:
205a4165bbaSJung-uk Kim 		break;
206a4165bbaSJung-uk Kim 	default:
207fc1f75e5SRui Paulo 		return (ENXIO);
208fc1f75e5SRui Paulo 	}
209a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
210fc1f75e5SRui Paulo 
211fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
212fc1f75e5SRui Paulo }
213fc1f75e5SRui Paulo 
214fc1f75e5SRui Paulo static int
215454e82d7SRui Paulo amdtemp_attach(device_t dev)
216fc1f75e5SRui Paulo {
217074d80acSJung-uk Kim 	char tn[32];
218074d80acSJung-uk Kim 	u_int regs[4];
219454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
220fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
221fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
222a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
223074d80acSJung-uk Kim 	u_int bid;
224074d80acSJung-uk Kim 	int erratum319, unit;
225fc1f75e5SRui Paulo 
226074d80acSJung-uk Kim 	erratum319 = 0;
227fdfa6079SJung-uk Kim 
228fdfa6079SJung-uk Kim 	/*
229fdfa6079SJung-uk Kim 	 * CPUID Register is available from Revision F.
230fdfa6079SJung-uk Kim 	 */
231074d80acSJung-uk Kim 	cpuid = cpu_id;
232074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
233074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
234fdfa6079SJung-uk Kim 	if (family != 0x0f || model >= 0x40) {
235a4165bbaSJung-uk Kim 		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
236a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
237a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
238fdfa6079SJung-uk Kim 	}
239a4165bbaSJung-uk Kim 
240a4165bbaSJung-uk Kim 	switch (family) {
241a4165bbaSJung-uk Kim 	case 0x0f:
242a4165bbaSJung-uk Kim 		/*
243fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
244fdfa6079SJung-uk Kim 		 *
245fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
246fdfa6079SJung-uk Kim 		 *
247fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
248fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
249fdfa6079SJung-uk Kim 		 *
250fdfa6079SJung-uk Kim 		 * - CurTmp
251a4165bbaSJung-uk Kim 		 *
252a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
253fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
254a4165bbaSJung-uk Kim 		 *
255fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
256fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
257fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
258fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
259fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
260fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
261fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
262074d80acSJung-uk Kim 		 *
263074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
264074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
265074d80acSJung-uk Kim 		 * documented anywhere.
266fc1f75e5SRui Paulo 		 */
267074d80acSJung-uk Kim 		if (model >= 0x40)
268fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
269074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
270074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
271074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
272074d80acSJung-uk Kim 			switch (model) {
273074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
274074d80acSJung-uk Kim 			case 0x6c:
275074d80acSJung-uk Kim 			case 0x7c:
276074d80acSJung-uk Kim 				break;
277074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
278074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
279074d80acSJung-uk Kim 					sc->sc_flags |=
280074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
281074d80acSJung-uk Kim 				break;
282074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
283074d80acSJung-uk Kim 			case 0x7f:
284074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
285074d80acSJung-uk Kim 				    bid != 0x0c)
286074d80acSJung-uk Kim 					sc->sc_flags |=
287074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
288074d80acSJung-uk Kim 				break;
289074d80acSJung-uk Kim 			default:
290074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
291074d80acSJung-uk Kim 			}
292fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
293fdfa6079SJung-uk Kim 		}
294a4165bbaSJung-uk Kim 
295a4165bbaSJung-uk Kim 		/*
296a4165bbaSJung-uk Kim 		 * There are two sensors per core.
297a4165bbaSJung-uk Kim 		 */
298a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
299a4165bbaSJung-uk Kim 
300a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
301a4165bbaSJung-uk Kim 		break;
302a4165bbaSJung-uk Kim 	case 0x10:
303074d80acSJung-uk Kim 		/*
304074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
305074d80acSJung-uk Kim 		 *
306074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
307074d80acSJung-uk Kim 		 */
308074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
309074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
310074d80acSJung-uk Kim 		case 0:	/* Socket F */
311074d80acSJung-uk Kim 			erratum319 = 1;
312074d80acSJung-uk Kim 			break;
313074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
314074d80acSJung-uk Kim 			if ((pci_cfgregread(pci_get_bus(dev),
315074d80acSJung-uk Kim 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
316074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
317074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
318074d80acSJung-uk Kim 				break;
319074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
320074d80acSJung-uk Kim 			erratum319 = 1;
321074d80acSJung-uk Kim 			break;
322074d80acSJung-uk Kim 		}
323074d80acSJung-uk Kim 		/* FALLTHROUGH */
324a4165bbaSJung-uk Kim 	case 0x11:
325074d80acSJung-uk Kim 	case 0x12:
326074d80acSJung-uk Kim 	case 0x14:
327074d80acSJung-uk Kim 	case 0x15:
328a4165bbaSJung-uk Kim 		/*
329a4165bbaSJung-uk Kim 		 * There is only one sensor per package.
330a4165bbaSJung-uk Kim 		 */
331a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
332a4165bbaSJung-uk Kim 
333a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp;
334a4165bbaSJung-uk Kim 		break;
335fc1f75e5SRui Paulo 	}
336fc1f75e5SRui Paulo 
337a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
338a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
339a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
340a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
341a4165bbaSJung-uk Kim 		return (ENXIO);
342a4165bbaSJung-uk Kim 
343074d80acSJung-uk Kim 	if (erratum319)
344074d80acSJung-uk Kim 		device_printf(dev,
345074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
346a4165bbaSJung-uk Kim 	if (bootverbose)
347a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
348a4165bbaSJung-uk Kim 		    sc->sc_ncores,
349a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
350454e82d7SRui Paulo 
351fc1f75e5SRui Paulo 	/*
352454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
353fc1f75e5SRui Paulo 	 */
354074d80acSJung-uk Kim 	unit = device_get_unit(dev);
355074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
356074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
357074d80acSJung-uk Kim 
358fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
359074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
360074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
361074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
362074d80acSJung-uk Kim 	    "Temperature sensor offset");
363fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
364a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
365074d80acSJung-uk Kim 	    "core0", CTLFLAG_RD, 0, "Core 0");
366fc1f75e5SRui Paulo 
367fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
368fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
369074d80acSJung-uk Kim 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
370074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
371074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
372fc1f75e5SRui Paulo 
373a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
374fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
375fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
376074d80acSJung-uk Kim 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
377074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
378074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
379fc1f75e5SRui Paulo 
380074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
381fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
382074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
383074d80acSJung-uk Kim 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
384fc1f75e5SRui Paulo 
385fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
386fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
387074d80acSJung-uk Kim 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
388074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
389074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
390fc1f75e5SRui Paulo 
391fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
392fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
393074d80acSJung-uk Kim 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
394074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
395074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
396074d80acSJung-uk Kim 		}
397a4165bbaSJung-uk Kim 	}
398a4165bbaSJung-uk Kim 
399a4165bbaSJung-uk Kim 	/*
400a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
401a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
402a4165bbaSJung-uk Kim 	 * after us.
403a4165bbaSJung-uk Kim 	 */
404a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
405a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
406a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
407a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
408a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
409a4165bbaSJung-uk Kim 		return (ENXIO);
410a4165bbaSJung-uk Kim 	}
411fc1f75e5SRui Paulo 
412fc1f75e5SRui Paulo 	return (0);
413fc1f75e5SRui Paulo }
414fc1f75e5SRui Paulo 
415fc1f75e5SRui Paulo void
416454e82d7SRui Paulo amdtemp_intrhook(void *arg)
417fc1f75e5SRui Paulo {
418454e82d7SRui Paulo 	struct amdtemp_softc *sc;
419fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
420a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
421a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
422a4165bbaSJung-uk Kim 	amdsensor_t sensor;
423a4165bbaSJung-uk Kim 	int i;
424fc1f75e5SRui Paulo 
425fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
426fc1f75e5SRui Paulo 
427fc1f75e5SRui Paulo 	/*
428fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
429fc1f75e5SRui Paulo 	 */
430fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
431fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
432fc1f75e5SRui Paulo 
433a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
434a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
435a4165bbaSJung-uk Kim 			continue;
436fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
437a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
438a4165bbaSJung-uk Kim 		if (cpu != NULL) {
439fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
440fc1f75e5SRui Paulo 
441a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
442074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
443fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
444fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
445fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
446a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
447a4165bbaSJung-uk Kim 			    "Current temparature");
448fc1f75e5SRui Paulo 		}
449fc1f75e5SRui Paulo 	}
450a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
451fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
452fc1f75e5SRui Paulo }
453fc1f75e5SRui Paulo 
454fc1f75e5SRui Paulo int
455454e82d7SRui Paulo amdtemp_detach(device_t dev)
456fc1f75e5SRui Paulo {
457454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
458a4165bbaSJung-uk Kim 	int i;
459fc1f75e5SRui Paulo 
460a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
461a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
462fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
463fc1f75e5SRui Paulo 
464454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
465fc1f75e5SRui Paulo 
466fc1f75e5SRui Paulo 	return (0);
467fc1f75e5SRui Paulo }
468fc1f75e5SRui Paulo 
469fc1f75e5SRui Paulo static int
470454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
471fc1f75e5SRui Paulo {
472fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
473454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
474a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
475a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
476fc1f75e5SRui Paulo 	int error;
477fc1f75e5SRui Paulo 
478a4165bbaSJung-uk Kim 	switch (sensor) {
479fc1f75e5SRui Paulo 	case CORE0:
480074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
481074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
482fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
483fc1f75e5SRui Paulo 		break;
484fc1f75e5SRui Paulo 	case CORE1:
485074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
486074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
487fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
488fc1f75e5SRui Paulo 		break;
489fc1f75e5SRui Paulo 	default:
490a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
491fc1f75e5SRui Paulo 		break;
492fc1f75e5SRui Paulo 	}
493fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
494fc1f75e5SRui Paulo 
495fc1f75e5SRui Paulo 	return (error);
496fc1f75e5SRui Paulo }
497fc1f75e5SRui Paulo 
498a4165bbaSJung-uk Kim #define	AMDTEMP_ZERO_C_TO_K	2732
499a4165bbaSJung-uk Kim 
500fc1f75e5SRui Paulo static int32_t
501454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
502fc1f75e5SRui Paulo {
503a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
504074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
505fc1f75e5SRui Paulo 
506a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
507074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
508074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
509fc1f75e5SRui Paulo 	switch (sensor) {
510074d80acSJung-uk Kim 	case CORE0_SENSOR1:
511074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5127ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
513074d80acSJung-uk Kim 	case CORE0_SENSOR0:
514a4165bbaSJung-uk Kim 	case CORE0:
515fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
516074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
517fc1f75e5SRui Paulo 		break;
518074d80acSJung-uk Kim 	case CORE1_SENSOR1:
519074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5207ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
521074d80acSJung-uk Kim 	case CORE1_SENSOR0:
522a4165bbaSJung-uk Kim 	case CORE1:
523fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
524074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
525fc1f75e5SRui Paulo 		break;
526fc1f75e5SRui Paulo 	}
527074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
528a4165bbaSJung-uk Kim 
529fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
530074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
531074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
532074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
533074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
534454e82d7SRui Paulo 
535454e82d7SRui Paulo 	return (temp);
536454e82d7SRui Paulo }
537454e82d7SRui Paulo 
538454e82d7SRui Paulo static int32_t
539454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
540454e82d7SRui Paulo {
541074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
542454e82d7SRui Paulo 	uint32_t temp;
543a4165bbaSJung-uk Kim 
544a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
545074d80acSJung-uk Kim 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
546074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
547fc1f75e5SRui Paulo 
548fc1f75e5SRui Paulo 	return (temp);
549fc1f75e5SRui Paulo }
550