xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision df20515d58eaa5c20dd12e565e41803ad3b8238a)
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
79534b11a1SJohn-Mark Gurney #define	DEVICEID_AMD_MISC12	0x1403
80074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
81074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
822b56f12bSChristian Brueffer #define	DEVICEID_AMD_MISC16	0x1533
83*df20515dSLuiz Otavio O Souza #define	DEVICEID_AMD_MISC16_M30H	0x1583
849bfafa10SChristian Brueffer #define	DEVICEID_AMD_MISC17	0x141d
85454e82d7SRui Paulo 
86454e82d7SRui Paulo static struct amdtemp_product {
87454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
88454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
89454e82d7SRui Paulo } amdtemp_products[] = {
90454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
91454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
92454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
93534b11a1SJohn-Mark Gurney 	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
94074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
95074d80acSJung-uk Kim 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
962b56f12bSChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
97*df20515dSLuiz Otavio O Souza 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H },
989bfafa10SChristian Brueffer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC17 },
99454e82d7SRui Paulo 	{ 0, 0 }
100454e82d7SRui Paulo };
101454e82d7SRui Paulo 
102454e82d7SRui Paulo /*
103074d80acSJung-uk Kim  * Reported Temperature Control Register
104454e82d7SRui Paulo  */
105a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
106454e82d7SRui Paulo 
107454e82d7SRui Paulo /*
108074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
109454e82d7SRui Paulo  */
110a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
111074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
112074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
113074d80acSJung-uk Kim 
114074d80acSJung-uk Kim /*
115074d80acSJung-uk Kim  * DRAM Configuration High Register
116074d80acSJung-uk Kim  */
117074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
118074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
119454e82d7SRui Paulo 
120a4165bbaSJung-uk Kim /*
121a4165bbaSJung-uk Kim  * CPU Family/Model Register
122a4165bbaSJung-uk Kim  */
123a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
124fc1f75e5SRui Paulo 
125fc1f75e5SRui Paulo /*
126fc1f75e5SRui Paulo  * Device methods.
127fc1f75e5SRui Paulo  */
128454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
129454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
130454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
131454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
132454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
133454e82d7SRui Paulo static int 	amdtemp_match(device_t dev);
134454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
135454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
136454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
137fc1f75e5SRui Paulo 
138454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
139fc1f75e5SRui Paulo 	/* Device interface */
140454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
141454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
142454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
143454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
144fc1f75e5SRui Paulo 
14561bfd867SSofian Brabez 	DEVMETHOD_END
146fc1f75e5SRui Paulo };
147fc1f75e5SRui Paulo 
148454e82d7SRui Paulo static driver_t amdtemp_driver = {
149454e82d7SRui Paulo 	"amdtemp",
150454e82d7SRui Paulo 	amdtemp_methods,
151454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
152fc1f75e5SRui Paulo };
153fc1f75e5SRui Paulo 
154454e82d7SRui Paulo static devclass_t amdtemp_devclass;
155454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
156fc1f75e5SRui Paulo 
157fc1f75e5SRui Paulo static int
158454e82d7SRui Paulo amdtemp_match(device_t dev)
159fc1f75e5SRui Paulo {
160fc1f75e5SRui Paulo 	int i;
161fc1f75e5SRui Paulo 	uint16_t vendor, devid;
162fc1f75e5SRui Paulo 
163fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
164fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
165fc1f75e5SRui Paulo 
166454e82d7SRui Paulo 	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
167454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
168454e82d7SRui Paulo 		    devid == amdtemp_products[i].amdtemp_deviceid)
169fc1f75e5SRui Paulo 			return (1);
170fc1f75e5SRui Paulo 	}
171fc1f75e5SRui Paulo 
172fc1f75e5SRui Paulo 	return (0);
173fc1f75e5SRui Paulo }
174fc1f75e5SRui Paulo 
175fc1f75e5SRui Paulo static void
176454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
177fc1f75e5SRui Paulo {
178fc1f75e5SRui Paulo 	device_t child;
179fc1f75e5SRui Paulo 
180fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
181454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
182fc1f75e5SRui Paulo 		return;
183fc1f75e5SRui Paulo 
184454e82d7SRui Paulo 	if (amdtemp_match(parent)) {
185454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
186fc1f75e5SRui Paulo 		if (child == NULL)
187454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
188fc1f75e5SRui Paulo 	}
189fc1f75e5SRui Paulo }
190fc1f75e5SRui Paulo 
191fc1f75e5SRui Paulo static int
192454e82d7SRui Paulo amdtemp_probe(device_t dev)
193fc1f75e5SRui Paulo {
194fdfa6079SJung-uk Kim 	uint32_t family, model;
195fc1f75e5SRui Paulo 
196a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
197a8de37b0SEitan Adler 		return (ENXIO);
198a8de37b0SEitan Adler 
199fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
200fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
201a4165bbaSJung-uk Kim 
202a4165bbaSJung-uk Kim 	switch (family) {
203a4165bbaSJung-uk Kim 	case 0x0f:
204fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
205fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
206a4165bbaSJung-uk Kim 			return (ENXIO);
207a4165bbaSJung-uk Kim 		break;
208a4165bbaSJung-uk Kim 	case 0x10:
209a4165bbaSJung-uk Kim 	case 0x11:
210074d80acSJung-uk Kim 	case 0x12:
211074d80acSJung-uk Kim 	case 0x14:
212074d80acSJung-uk Kim 	case 0x15:
2132b56f12bSChristian Brueffer 	case 0x16:
214a4165bbaSJung-uk Kim 		break;
215a4165bbaSJung-uk Kim 	default:
216fc1f75e5SRui Paulo 		return (ENXIO);
217fc1f75e5SRui Paulo 	}
218a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
219fc1f75e5SRui Paulo 
220fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
221fc1f75e5SRui Paulo }
222fc1f75e5SRui Paulo 
223fc1f75e5SRui Paulo static int
224454e82d7SRui Paulo amdtemp_attach(device_t dev)
225fc1f75e5SRui Paulo {
226074d80acSJung-uk Kim 	char tn[32];
227074d80acSJung-uk Kim 	u_int regs[4];
228454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
229fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
230fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
231a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
232074d80acSJung-uk Kim 	u_int bid;
233074d80acSJung-uk Kim 	int erratum319, unit;
234fc1f75e5SRui Paulo 
235074d80acSJung-uk Kim 	erratum319 = 0;
236fdfa6079SJung-uk Kim 
237fdfa6079SJung-uk Kim 	/*
238fdfa6079SJung-uk Kim 	 * CPUID Register is available from Revision F.
239fdfa6079SJung-uk Kim 	 */
240074d80acSJung-uk Kim 	cpuid = cpu_id;
241074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
242074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
243fdfa6079SJung-uk Kim 	if (family != 0x0f || model >= 0x40) {
244a4165bbaSJung-uk Kim 		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
245a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
246a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
247fdfa6079SJung-uk Kim 	}
248a4165bbaSJung-uk Kim 
249a4165bbaSJung-uk Kim 	switch (family) {
250a4165bbaSJung-uk Kim 	case 0x0f:
251a4165bbaSJung-uk Kim 		/*
252fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
253fdfa6079SJung-uk Kim 		 *
254fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
255fdfa6079SJung-uk Kim 		 *
256fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
257fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
258fdfa6079SJung-uk Kim 		 *
259fdfa6079SJung-uk Kim 		 * - CurTmp
260a4165bbaSJung-uk Kim 		 *
261a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
262fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
263a4165bbaSJung-uk Kim 		 *
264fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
265fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
266fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
267fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
268fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
269fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
270fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
271074d80acSJung-uk Kim 		 *
272074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
273074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
274074d80acSJung-uk Kim 		 * documented anywhere.
275fc1f75e5SRui Paulo 		 */
276074d80acSJung-uk Kim 		if (model >= 0x40)
277fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
278074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
279074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
280074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
281074d80acSJung-uk Kim 			switch (model) {
282074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
283074d80acSJung-uk Kim 			case 0x6c:
284074d80acSJung-uk Kim 			case 0x7c:
285074d80acSJung-uk Kim 				break;
286074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
287074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
288074d80acSJung-uk Kim 					sc->sc_flags |=
289074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
290074d80acSJung-uk Kim 				break;
291074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
292074d80acSJung-uk Kim 			case 0x7f:
293074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
294074d80acSJung-uk Kim 				    bid != 0x0c)
295074d80acSJung-uk Kim 					sc->sc_flags |=
296074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
297074d80acSJung-uk Kim 				break;
298074d80acSJung-uk Kim 			default:
299074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
300074d80acSJung-uk Kim 			}
301fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
302fdfa6079SJung-uk Kim 		}
303a4165bbaSJung-uk Kim 
304a4165bbaSJung-uk Kim 		/*
305a4165bbaSJung-uk Kim 		 * There are two sensors per core.
306a4165bbaSJung-uk Kim 		 */
307a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
308a4165bbaSJung-uk Kim 
309a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
310a4165bbaSJung-uk Kim 		break;
311a4165bbaSJung-uk Kim 	case 0x10:
312074d80acSJung-uk Kim 		/*
313074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
314074d80acSJung-uk Kim 		 *
315074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
316074d80acSJung-uk Kim 		 */
317074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
318074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
319074d80acSJung-uk Kim 		case 0:	/* Socket F */
320074d80acSJung-uk Kim 			erratum319 = 1;
321074d80acSJung-uk Kim 			break;
322074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
323074d80acSJung-uk Kim 			if ((pci_cfgregread(pci_get_bus(dev),
324074d80acSJung-uk Kim 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
325074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
326074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
327074d80acSJung-uk Kim 				break;
328074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
329074d80acSJung-uk Kim 			erratum319 = 1;
330074d80acSJung-uk Kim 			break;
331074d80acSJung-uk Kim 		}
332074d80acSJung-uk Kim 		/* FALLTHROUGH */
333a4165bbaSJung-uk Kim 	case 0x11:
334074d80acSJung-uk Kim 	case 0x12:
335074d80acSJung-uk Kim 	case 0x14:
336074d80acSJung-uk Kim 	case 0x15:
3372b56f12bSChristian Brueffer 	case 0x16:
338a4165bbaSJung-uk Kim 		/*
339a4165bbaSJung-uk Kim 		 * There is only one sensor per package.
340a4165bbaSJung-uk Kim 		 */
341a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
342a4165bbaSJung-uk Kim 
343a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp;
344a4165bbaSJung-uk Kim 		break;
345fc1f75e5SRui Paulo 	}
346fc1f75e5SRui Paulo 
347a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
348a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
349a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
350a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
351a4165bbaSJung-uk Kim 		return (ENXIO);
352a4165bbaSJung-uk Kim 
353074d80acSJung-uk Kim 	if (erratum319)
354074d80acSJung-uk Kim 		device_printf(dev,
355074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
356a4165bbaSJung-uk Kim 	if (bootverbose)
357a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
358a4165bbaSJung-uk Kim 		    sc->sc_ncores,
359a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
360454e82d7SRui Paulo 
361fc1f75e5SRui Paulo 	/*
362454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
363fc1f75e5SRui Paulo 	 */
364074d80acSJung-uk Kim 	unit = device_get_unit(dev);
365074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
366074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
367074d80acSJung-uk Kim 
368fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
369074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
370074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
371074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
372074d80acSJung-uk Kim 	    "Temperature sensor offset");
373fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
374a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
375074d80acSJung-uk Kim 	    "core0", CTLFLAG_RD, 0, "Core 0");
376fc1f75e5SRui Paulo 
377fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
378fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
379074d80acSJung-uk Kim 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
380074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
381074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
382fc1f75e5SRui Paulo 
383a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
384fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
385fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
386074d80acSJung-uk Kim 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
387074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
388074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
389fc1f75e5SRui Paulo 
390074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
391fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
392074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
393074d80acSJung-uk Kim 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
394fc1f75e5SRui Paulo 
395fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
396fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
397074d80acSJung-uk Kim 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
398074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
399074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
400fc1f75e5SRui Paulo 
401fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
402fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
403074d80acSJung-uk Kim 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
404074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
405074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
406074d80acSJung-uk Kim 		}
407a4165bbaSJung-uk Kim 	}
408a4165bbaSJung-uk Kim 
409a4165bbaSJung-uk Kim 	/*
410a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
411a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
412a4165bbaSJung-uk Kim 	 * after us.
413a4165bbaSJung-uk Kim 	 */
414a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
415a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
416a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
417a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
418a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
419a4165bbaSJung-uk Kim 		return (ENXIO);
420a4165bbaSJung-uk Kim 	}
421fc1f75e5SRui Paulo 
422fc1f75e5SRui Paulo 	return (0);
423fc1f75e5SRui Paulo }
424fc1f75e5SRui Paulo 
425fc1f75e5SRui Paulo void
426454e82d7SRui Paulo amdtemp_intrhook(void *arg)
427fc1f75e5SRui Paulo {
428454e82d7SRui Paulo 	struct amdtemp_softc *sc;
429fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
430a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
431a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
432a4165bbaSJung-uk Kim 	amdsensor_t sensor;
433a4165bbaSJung-uk Kim 	int i;
434fc1f75e5SRui Paulo 
435fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
436fc1f75e5SRui Paulo 
437fc1f75e5SRui Paulo 	/*
438fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
439fc1f75e5SRui Paulo 	 */
440fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
441fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
442fc1f75e5SRui Paulo 
443a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
444a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
445a4165bbaSJung-uk Kim 			continue;
446fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
447a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
448a4165bbaSJung-uk Kim 		if (cpu != NULL) {
449fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
450fc1f75e5SRui Paulo 
451a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
452074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
453fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
454fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
455fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
456a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
457a4165bbaSJung-uk Kim 			    "Current temparature");
458fc1f75e5SRui Paulo 		}
459fc1f75e5SRui Paulo 	}
460a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
461fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
462fc1f75e5SRui Paulo }
463fc1f75e5SRui Paulo 
464fc1f75e5SRui Paulo int
465454e82d7SRui Paulo amdtemp_detach(device_t dev)
466fc1f75e5SRui Paulo {
467454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
468a4165bbaSJung-uk Kim 	int i;
469fc1f75e5SRui Paulo 
470a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
471a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
472fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
473fc1f75e5SRui Paulo 
474454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
475fc1f75e5SRui Paulo 
476fc1f75e5SRui Paulo 	return (0);
477fc1f75e5SRui Paulo }
478fc1f75e5SRui Paulo 
479fc1f75e5SRui Paulo static int
480454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
481fc1f75e5SRui Paulo {
482fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
483454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
484a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
485a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
486fc1f75e5SRui Paulo 	int error;
487fc1f75e5SRui Paulo 
488a4165bbaSJung-uk Kim 	switch (sensor) {
489fc1f75e5SRui Paulo 	case CORE0:
490074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
491074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
492fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
493fc1f75e5SRui Paulo 		break;
494fc1f75e5SRui Paulo 	case CORE1:
495074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
496074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
497fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
498fc1f75e5SRui Paulo 		break;
499fc1f75e5SRui Paulo 	default:
500a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
501fc1f75e5SRui Paulo 		break;
502fc1f75e5SRui Paulo 	}
503fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
504fc1f75e5SRui Paulo 
505fc1f75e5SRui Paulo 	return (error);
506fc1f75e5SRui Paulo }
507fc1f75e5SRui Paulo 
508a4165bbaSJung-uk Kim #define	AMDTEMP_ZERO_C_TO_K	2732
509a4165bbaSJung-uk Kim 
510fc1f75e5SRui Paulo static int32_t
511454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
512fc1f75e5SRui Paulo {
513a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
514074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
515fc1f75e5SRui Paulo 
516a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
517074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
518074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
519fc1f75e5SRui Paulo 	switch (sensor) {
520074d80acSJung-uk Kim 	case CORE0_SENSOR1:
521074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5227ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
523074d80acSJung-uk Kim 	case CORE0_SENSOR0:
524a4165bbaSJung-uk Kim 	case CORE0:
525fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
526074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
527fc1f75e5SRui Paulo 		break;
528074d80acSJung-uk Kim 	case CORE1_SENSOR1:
529074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
5307ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
531074d80acSJung-uk Kim 	case CORE1_SENSOR0:
532a4165bbaSJung-uk Kim 	case CORE1:
533fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
534074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
535fc1f75e5SRui Paulo 		break;
536fc1f75e5SRui Paulo 	}
537074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
538a4165bbaSJung-uk Kim 
539fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
540074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
541074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
542074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
543074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
544454e82d7SRui Paulo 
545454e82d7SRui Paulo 	return (temp);
546454e82d7SRui Paulo }
547454e82d7SRui Paulo 
548454e82d7SRui Paulo static int32_t
549454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
550454e82d7SRui Paulo {
551074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
552454e82d7SRui Paulo 	uint32_t temp;
553a4165bbaSJung-uk Kim 
554a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
555074d80acSJung-uk Kim 	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
556074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
557fc1f75e5SRui Paulo 
558fc1f75e5SRui Paulo 	return (temp);
559fc1f75e5SRui Paulo }
560