xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision e49ec461147e161f167b3d758cd0fd44b2b6f581)
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.
8*e49ec461SConrad Meyer  * Copyright (c) 2017-2019 Conrad Meyer <cem@FreeBSD.org>
9*e49ec461SConrad Meyer  * All rights reserved.
10fc1f75e5SRui Paulo  *
11fc1f75e5SRui Paulo  * Redistribution and use in source and binary forms, with or without
12fc1f75e5SRui Paulo  * modification, are permitted provided that the following conditions
13fc1f75e5SRui Paulo  * are met:
14fc1f75e5SRui Paulo  * 1. Redistributions of source code must retain the above copyright
15fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer.
16fc1f75e5SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
17fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
18fc1f75e5SRui Paulo  *    documentation and/or other materials provided with the distribution.
19fc1f75e5SRui Paulo  *
20fc1f75e5SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21fc1f75e5SRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22fc1f75e5SRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23fc1f75e5SRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24fc1f75e5SRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25fc1f75e5SRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26fc1f75e5SRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27fc1f75e5SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28fc1f75e5SRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29fc1f75e5SRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30fc1f75e5SRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
31fc1f75e5SRui Paulo  */
32fc1f75e5SRui Paulo 
33fc1f75e5SRui Paulo /*
34074d80acSJung-uk Kim  * Driver for the AMD CPU on-die thermal sensors.
35a4165bbaSJung-uk Kim  * Initially based on the k8temp Linux driver.
36fc1f75e5SRui Paulo  */
37fc1f75e5SRui Paulo 
38fc1f75e5SRui Paulo #include <sys/cdefs.h>
39fc1f75e5SRui Paulo __FBSDID("$FreeBSD$");
40fc1f75e5SRui Paulo 
41fc1f75e5SRui Paulo #include <sys/param.h>
42fc1f75e5SRui Paulo #include <sys/bus.h>
43fc1f75e5SRui Paulo #include <sys/conf.h>
44fc1f75e5SRui Paulo #include <sys/kernel.h>
45a4165bbaSJung-uk Kim #include <sys/module.h>
46fc1f75e5SRui Paulo #include <sys/sysctl.h>
47a4165bbaSJung-uk Kim #include <sys/systm.h>
48fc1f75e5SRui Paulo 
49fdfa6079SJung-uk Kim #include <machine/cpufunc.h>
50fc1f75e5SRui Paulo #include <machine/md_var.h>
51a4165bbaSJung-uk Kim #include <machine/specialreg.h>
52fc1f75e5SRui Paulo 
53fc1f75e5SRui Paulo #include <dev/pci/pcivar.h>
54074d80acSJung-uk Kim #include <x86/pci_cfgreg.h>
55fc1f75e5SRui Paulo 
56a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h>
57a03d621bSConrad Meyer 
58fc1f75e5SRui Paulo typedef enum {
59074d80acSJung-uk Kim 	CORE0_SENSOR0,
60074d80acSJung-uk Kim 	CORE0_SENSOR1,
61074d80acSJung-uk Kim 	CORE1_SENSOR0,
62074d80acSJung-uk Kim 	CORE1_SENSOR1,
63fc1f75e5SRui Paulo 	CORE0,
64fc1f75e5SRui Paulo 	CORE1
65454e82d7SRui Paulo } amdsensor_t;
66454e82d7SRui Paulo 
67454e82d7SRui Paulo struct amdtemp_softc {
68a4165bbaSJung-uk Kim 	int		sc_ncores;
69454e82d7SRui Paulo 	int		sc_ntemps;
70fdfa6079SJung-uk Kim 	int		sc_flags;
71074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
72074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
73074d80acSJung-uk Kim #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
74074d80acSJung-uk Kim 	int32_t		sc_offset;
75454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
76a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
77a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
78a03d621bSConrad Meyer 	device_t	sc_smn;
79454e82d7SRui Paulo };
80454e82d7SRui Paulo 
81*e49ec461SConrad Meyer /*
82*e49ec461SConrad Meyer  * N.B. The numbers in macro names below are significant and represent CPU
83*e49ec461SConrad Meyer  * family and model numbers.  Do not make up fictitious family or model numbers
84*e49ec461SConrad Meyer  * when adding support for new devices.
85*e49ec461SConrad Meyer  */
86454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
87454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
88454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
89454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
90074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
91074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
92*e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M10H	0x1403
93*e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M30H	0x141d
94*e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M60H_ROOT	0x1576
952b56f12bSChristian Brueffer #define	DEVICEID_AMD_MISC16	0x1533
96df20515dSLuiz Otavio O Souza #define	DEVICEID_AMD_MISC16_M30H	0x1583
979d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_ROOT	0x1450
989d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M10H_ROOT	0x15d0
99454e82d7SRui Paulo 
100*e49ec461SConrad Meyer static const struct amdtemp_product {
101454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
102454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
103*e49ec461SConrad Meyer 	/*
104*e49ec461SConrad Meyer 	 * 0xFC register is only valid on the D18F3 PCI device; SMN temp
105*e49ec461SConrad Meyer 	 * drivers do not attach to that device.
106*e49ec461SConrad Meyer 	 */
107*e49ec461SConrad Meyer 	bool		amdtemp_has_cpuid;
108454e82d7SRui Paulo } amdtemp_products[] = {
109*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F, true },
110*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10, true },
111*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11, true },
112*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14, true },
113*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15, true },
114*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M10H, true },
115*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M30H, true },
116*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M60H_ROOT, false },
117*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16, true },
118*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H, true },
119*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_ROOT, false },
120*e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M10H_ROOT, false },
121454e82d7SRui Paulo };
122454e82d7SRui Paulo 
123454e82d7SRui Paulo /*
124*e49ec461SConrad Meyer  * Reported Temperature Control Register, family 0Fh-15h (some models), 16h.
125454e82d7SRui Paulo  */
126a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
127454e82d7SRui Paulo 
128*e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_CURTMP_MASK	0x7ff
129*e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_CURTMP_SHIFT	21
130*e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_TJSEL_MASK	0x3
131*e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_TJSEL_SHIFT	16
132*e49ec461SConrad Meyer 
133*e49ec461SConrad Meyer /*
134*e49ec461SConrad Meyer  * Reported Temperature, Family 15h, M60+
135*e49ec461SConrad Meyer  *
136*e49ec461SConrad Meyer  * Same register bit definitions as other Family 15h CPUs, but access is
137*e49ec461SConrad Meyer  * indirect via SMN, like Family 17h.
138*e49ec461SConrad Meyer  */
139*e49ec461SConrad Meyer #define	AMDTEMP_15H_M60H_REPTMP_CTRL	0xd8200ca4
140*e49ec461SConrad Meyer 
141454e82d7SRui Paulo /*
142a03d621bSConrad Meyer  * Reported Temperature, Family 17h
143fbd5d782SConrad Meyer  *
144fbd5d782SConrad Meyer  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
145fbd5d782SConrad Meyer  * provide the current temp.  bit 19, when clear, means the temp is reported in
146fbd5d782SConrad Meyer  * a range 0.."225C" (probable typo for 255C), and when set changes the range
147fbd5d782SConrad Meyer  * to -49..206C.
148a03d621bSConrad Meyer  */
149a03d621bSConrad Meyer #define	AMDTEMP_17H_CUR_TMP		0x59800
150fbd5d782SConrad Meyer #define	AMDTEMP_17H_CUR_TMP_RANGE_SEL	(1 << 19)
151*e49ec461SConrad Meyer 
152*e49ec461SConrad Meyer /*
153*e49ec461SConrad Meyer  * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius).
154*e49ec461SConrad Meyer  */
155*e49ec461SConrad Meyer #define	AMDTEMP_CURTMP_RANGE_ADJUST	490
156a03d621bSConrad Meyer 
157a03d621bSConrad Meyer /*
158074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
159454e82d7SRui Paulo  */
160a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
161074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
162074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
163074d80acSJung-uk Kim 
164074d80acSJung-uk Kim /*
165074d80acSJung-uk Kim  * DRAM Configuration High Register
166074d80acSJung-uk Kim  */
167074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
168074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
169454e82d7SRui Paulo 
170a4165bbaSJung-uk Kim /*
171a4165bbaSJung-uk Kim  * CPU Family/Model Register
172a4165bbaSJung-uk Kim  */
173a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
174fc1f75e5SRui Paulo 
175fc1f75e5SRui Paulo /*
176fc1f75e5SRui Paulo  * Device methods.
177fc1f75e5SRui Paulo  */
178454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
179454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
180454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
181454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
182454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
183454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
184454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
185*e49ec461SConrad Meyer static int32_t	amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor);
186a03d621bSConrad Meyer static int32_t	amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
187454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
188fc1f75e5SRui Paulo 
189454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
190fc1f75e5SRui Paulo 	/* Device interface */
191454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
192454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
193454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
194454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
195fc1f75e5SRui Paulo 
19661bfd867SSofian Brabez 	DEVMETHOD_END
197fc1f75e5SRui Paulo };
198fc1f75e5SRui Paulo 
199454e82d7SRui Paulo static driver_t amdtemp_driver = {
200454e82d7SRui Paulo 	"amdtemp",
201454e82d7SRui Paulo 	amdtemp_methods,
202454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
203fc1f75e5SRui Paulo };
204fc1f75e5SRui Paulo 
205454e82d7SRui Paulo static devclass_t amdtemp_devclass;
206454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
207a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1);
208a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
209a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
210329e817fSWarner Losh     nitems(amdtemp_products));
211fc1f75e5SRui Paulo 
212*e49ec461SConrad Meyer static bool
213*e49ec461SConrad Meyer amdtemp_match(device_t dev, const struct amdtemp_product **product_out)
214fc1f75e5SRui Paulo {
215fc1f75e5SRui Paulo 	int i;
216fc1f75e5SRui Paulo 	uint16_t vendor, devid;
217fc1f75e5SRui Paulo 
218fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
219fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
220fc1f75e5SRui Paulo 
221a64bf59cSConrad Meyer 	for (i = 0; i < nitems(amdtemp_products); i++) {
222454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
223*e49ec461SConrad Meyer 		    devid == amdtemp_products[i].amdtemp_deviceid) {
224*e49ec461SConrad Meyer 			if (product_out != NULL)
225*e49ec461SConrad Meyer 				*product_out = &amdtemp_products[i];
226*e49ec461SConrad Meyer 			return (true);
227fc1f75e5SRui Paulo 		}
228*e49ec461SConrad Meyer 	}
229*e49ec461SConrad Meyer 	return (false);
230fc1f75e5SRui Paulo }
231fc1f75e5SRui Paulo 
232fc1f75e5SRui Paulo static void
233454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
234fc1f75e5SRui Paulo {
235fc1f75e5SRui Paulo 	device_t child;
236fc1f75e5SRui Paulo 
237fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
238454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
239fc1f75e5SRui Paulo 		return;
240fc1f75e5SRui Paulo 
241*e49ec461SConrad Meyer 	if (amdtemp_match(parent, NULL)) {
242454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
243fc1f75e5SRui Paulo 		if (child == NULL)
244454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
245fc1f75e5SRui Paulo 	}
246fc1f75e5SRui Paulo }
247fc1f75e5SRui Paulo 
248fc1f75e5SRui Paulo static int
249454e82d7SRui Paulo amdtemp_probe(device_t dev)
250fc1f75e5SRui Paulo {
251fdfa6079SJung-uk Kim 	uint32_t family, model;
252fc1f75e5SRui Paulo 
253a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
254a8de37b0SEitan Adler 		return (ENXIO);
255*e49ec461SConrad Meyer 	if (!amdtemp_match(device_get_parent(dev), NULL))
25640f7bccbSConrad Meyer 		return (ENXIO);
257a8de37b0SEitan Adler 
258fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
259fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
260a4165bbaSJung-uk Kim 
261a4165bbaSJung-uk Kim 	switch (family) {
262a4165bbaSJung-uk Kim 	case 0x0f:
263fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
264fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
265a4165bbaSJung-uk Kim 			return (ENXIO);
266a4165bbaSJung-uk Kim 		break;
267a4165bbaSJung-uk Kim 	case 0x10:
268a4165bbaSJung-uk Kim 	case 0x11:
269074d80acSJung-uk Kim 	case 0x12:
270074d80acSJung-uk Kim 	case 0x14:
271074d80acSJung-uk Kim 	case 0x15:
2722b56f12bSChristian Brueffer 	case 0x16:
273a03d621bSConrad Meyer 	case 0x17:
274a4165bbaSJung-uk Kim 		break;
275a4165bbaSJung-uk Kim 	default:
276fc1f75e5SRui Paulo 		return (ENXIO);
277fc1f75e5SRui Paulo 	}
278a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
279fc1f75e5SRui Paulo 
280fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
281fc1f75e5SRui Paulo }
282fc1f75e5SRui Paulo 
283fc1f75e5SRui Paulo static int
284454e82d7SRui Paulo amdtemp_attach(device_t dev)
285fc1f75e5SRui Paulo {
286074d80acSJung-uk Kim 	char tn[32];
287074d80acSJung-uk Kim 	u_int regs[4];
288*e49ec461SConrad Meyer 	const struct amdtemp_product *product;
289*e49ec461SConrad Meyer 	struct amdtemp_softc *sc;
290fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
291fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
292a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
293074d80acSJung-uk Kim 	u_int bid;
294074d80acSJung-uk Kim 	int erratum319, unit;
295*e49ec461SConrad Meyer 	bool needsmn;
296fc1f75e5SRui Paulo 
297*e49ec461SConrad Meyer 	sc = device_get_softc(dev);
298074d80acSJung-uk Kim 	erratum319 = 0;
299*e49ec461SConrad Meyer 	needsmn = false;
300fdfa6079SJung-uk Kim 
301*e49ec461SConrad Meyer 	if (!amdtemp_match(device_get_parent(dev), &product))
302*e49ec461SConrad Meyer 		return (ENXIO);
303*e49ec461SConrad Meyer 
304074d80acSJung-uk Kim 	cpuid = cpu_id;
305074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
306074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
307*e49ec461SConrad Meyer 
308*e49ec461SConrad Meyer 	/*
309*e49ec461SConrad Meyer 	 * This checks for the byzantine condition of running a heterogenous
310*e49ec461SConrad Meyer 	 * revision multi-socket system where the attach thread is potentially
311*e49ec461SConrad Meyer 	 * probing a remote socket's PCI device.
312*e49ec461SConrad Meyer 	 *
313*e49ec461SConrad Meyer 	 * Currently, such scenarios are unsupported on models using the SMN
314*e49ec461SConrad Meyer 	 * (because on those models, amdtemp(4) attaches to a different PCI
315*e49ec461SConrad Meyer 	 * device than the one that contains AMDTEMP_CPUID).
316*e49ec461SConrad Meyer 	 *
317*e49ec461SConrad Meyer 	 * The ancient 0x0F family of devices only supports this register from
318*e49ec461SConrad Meyer 	 * models 40h+.
319*e49ec461SConrad Meyer 	 */
320*e49ec461SConrad Meyer 	if (product->amdtemp_has_cpuid && (family > 0x0f ||
321*e49ec461SConrad Meyer 	    (family == 0x0f && model >= 0x40))) {
322*e49ec461SConrad Meyer 		cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID,
323*e49ec461SConrad Meyer 		    4);
324a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
325a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
326fdfa6079SJung-uk Kim 	}
327a4165bbaSJung-uk Kim 
328a4165bbaSJung-uk Kim 	switch (family) {
329a4165bbaSJung-uk Kim 	case 0x0f:
330a4165bbaSJung-uk Kim 		/*
331fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
332fdfa6079SJung-uk Kim 		 *
333fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
334fdfa6079SJung-uk Kim 		 *
335fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
336fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
337fdfa6079SJung-uk Kim 		 *
338fdfa6079SJung-uk Kim 		 * - CurTmp
339a4165bbaSJung-uk Kim 		 *
340a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
341fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
342a4165bbaSJung-uk Kim 		 *
343fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
344fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
345fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
346fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
347fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
348fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
349fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
350074d80acSJung-uk Kim 		 *
351074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
352074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
353074d80acSJung-uk Kim 		 * documented anywhere.
354fc1f75e5SRui Paulo 		 */
355074d80acSJung-uk Kim 		if (model >= 0x40)
356fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
357074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
358074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
359074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
360074d80acSJung-uk Kim 			switch (model) {
361074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
362074d80acSJung-uk Kim 			case 0x6c:
363074d80acSJung-uk Kim 			case 0x7c:
364074d80acSJung-uk Kim 				break;
365074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
366074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
367074d80acSJung-uk Kim 					sc->sc_flags |=
368074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
369074d80acSJung-uk Kim 				break;
370074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
371074d80acSJung-uk Kim 			case 0x7f:
372074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
373074d80acSJung-uk Kim 				    bid != 0x0c)
374074d80acSJung-uk Kim 					sc->sc_flags |=
375074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
376074d80acSJung-uk Kim 				break;
377074d80acSJung-uk Kim 			default:
378074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
379074d80acSJung-uk Kim 			}
380fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
381fdfa6079SJung-uk Kim 		}
382a4165bbaSJung-uk Kim 
383a4165bbaSJung-uk Kim 		/*
384a4165bbaSJung-uk Kim 		 * There are two sensors per core.
385a4165bbaSJung-uk Kim 		 */
386a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
387a4165bbaSJung-uk Kim 
388a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
389a4165bbaSJung-uk Kim 		break;
390a4165bbaSJung-uk Kim 	case 0x10:
391074d80acSJung-uk Kim 		/*
392074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
393074d80acSJung-uk Kim 		 *
394074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
395074d80acSJung-uk Kim 		 */
396074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
397074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
398074d80acSJung-uk Kim 		case 0:	/* Socket F */
399074d80acSJung-uk Kim 			erratum319 = 1;
400074d80acSJung-uk Kim 			break;
401074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
402074d80acSJung-uk Kim 			if ((pci_cfgregread(pci_get_bus(dev),
403074d80acSJung-uk Kim 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
404074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
405074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
406074d80acSJung-uk Kim 				break;
407074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
408074d80acSJung-uk Kim 			erratum319 = 1;
409074d80acSJung-uk Kim 			break;
410074d80acSJung-uk Kim 		}
411074d80acSJung-uk Kim 		/* FALLTHROUGH */
412a4165bbaSJung-uk Kim 	case 0x11:
413074d80acSJung-uk Kim 	case 0x12:
414074d80acSJung-uk Kim 	case 0x14:
415074d80acSJung-uk Kim 	case 0x15:
4162b56f12bSChristian Brueffer 	case 0x16:
417a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
418*e49ec461SConrad Meyer 		/*
419*e49ec461SConrad Meyer 		 * Some later (60h+) models of family 15h use a similar SMN
420*e49ec461SConrad Meyer 		 * network as family 17h.  (However, the register index differs
421*e49ec461SConrad Meyer 		 * from 17h and the decoding matches other 10h-15h models,
422*e49ec461SConrad Meyer 		 * which differ from 17h.)
423*e49ec461SConrad Meyer 		 */
424*e49ec461SConrad Meyer 		if (family == 0x15 && model >= 0x60) {
425*e49ec461SConrad Meyer 			sc->sc_gettemp = amdtemp_gettemp15hm60h;
426*e49ec461SConrad Meyer 			needsmn = true;
427*e49ec461SConrad Meyer 		} else
428a4165bbaSJung-uk Kim 			sc->sc_gettemp = amdtemp_gettemp;
429a4165bbaSJung-uk Kim 		break;
430a03d621bSConrad Meyer 	case 0x17:
431a03d621bSConrad Meyer 		sc->sc_ntemps = 1;
432a03d621bSConrad Meyer 		sc->sc_gettemp = amdtemp_gettemp17h;
433*e49ec461SConrad Meyer 		needsmn = true;
434*e49ec461SConrad Meyer 		break;
435*e49ec461SConrad Meyer 	default:
436*e49ec461SConrad Meyer 		device_printf(dev, "Bogus family 0x%x\n", family);
437*e49ec461SConrad Meyer 		return (ENXIO);
438*e49ec461SConrad Meyer 	}
439*e49ec461SConrad Meyer 
440*e49ec461SConrad Meyer 	if (needsmn) {
441a03d621bSConrad Meyer 		sc->sc_smn = device_find_child(
442a03d621bSConrad Meyer 		    device_get_parent(dev), "amdsmn", -1);
443a03d621bSConrad Meyer 		if (sc->sc_smn == NULL) {
444a03d621bSConrad Meyer 			if (bootverbose)
445a03d621bSConrad Meyer 				device_printf(dev, "No SMN device found\n");
446a03d621bSConrad Meyer 			return (ENXIO);
447a03d621bSConrad Meyer 		}
448fc1f75e5SRui Paulo 	}
449fc1f75e5SRui Paulo 
450a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
451a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
452a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
453a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
454a4165bbaSJung-uk Kim 		return (ENXIO);
455a4165bbaSJung-uk Kim 
456074d80acSJung-uk Kim 	if (erratum319)
457074d80acSJung-uk Kim 		device_printf(dev,
458074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
459a4165bbaSJung-uk Kim 	if (bootverbose)
460a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
461a4165bbaSJung-uk Kim 		    sc->sc_ncores,
462a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
463454e82d7SRui Paulo 
464fc1f75e5SRui Paulo 	/*
465454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
466fc1f75e5SRui Paulo 	 */
467074d80acSJung-uk Kim 	unit = device_get_unit(dev);
468074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
469074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
470074d80acSJung-uk Kim 
471fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
472074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
473074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
474074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
475074d80acSJung-uk Kim 	    "Temperature sensor offset");
476fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
477a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
478074d80acSJung-uk Kim 	    "core0", CTLFLAG_RD, 0, "Core 0");
479fc1f75e5SRui Paulo 
480fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
481fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
482074d80acSJung-uk Kim 	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
483074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
484074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
485fc1f75e5SRui Paulo 
486a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
487fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
488fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
489074d80acSJung-uk Kim 		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
490074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
491074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
492fc1f75e5SRui Paulo 
493074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
494fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
495074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
496074d80acSJung-uk Kim 			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
497fc1f75e5SRui Paulo 
498fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
499fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
500074d80acSJung-uk Kim 			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
501074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
502074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
503fc1f75e5SRui Paulo 
504fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
505fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
506074d80acSJung-uk Kim 			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
507074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
508074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
509074d80acSJung-uk Kim 		}
510a4165bbaSJung-uk Kim 	}
511a4165bbaSJung-uk Kim 
512a4165bbaSJung-uk Kim 	/*
513a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
514a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
515a4165bbaSJung-uk Kim 	 * after us.
516a4165bbaSJung-uk Kim 	 */
517a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
518a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
519a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
520a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
521a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
522a4165bbaSJung-uk Kim 		return (ENXIO);
523a4165bbaSJung-uk Kim 	}
524fc1f75e5SRui Paulo 
525fc1f75e5SRui Paulo 	return (0);
526fc1f75e5SRui Paulo }
527fc1f75e5SRui Paulo 
528fc1f75e5SRui Paulo void
529454e82d7SRui Paulo amdtemp_intrhook(void *arg)
530fc1f75e5SRui Paulo {
531454e82d7SRui Paulo 	struct amdtemp_softc *sc;
532fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
533a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
534a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
535a4165bbaSJung-uk Kim 	amdsensor_t sensor;
536a4165bbaSJung-uk Kim 	int i;
537fc1f75e5SRui Paulo 
538fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
539fc1f75e5SRui Paulo 
540fc1f75e5SRui Paulo 	/*
541fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
542fc1f75e5SRui Paulo 	 */
543fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
544fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
545fc1f75e5SRui Paulo 
546a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
547a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
548a4165bbaSJung-uk Kim 			continue;
549fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
550a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
551a4165bbaSJung-uk Kim 		if (cpu != NULL) {
552fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
553fc1f75e5SRui Paulo 
554a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
555074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
556fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
557fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
558fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
559a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
560a4165bbaSJung-uk Kim 			    "Current temparature");
561fc1f75e5SRui Paulo 		}
562fc1f75e5SRui Paulo 	}
563a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
564fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
565fc1f75e5SRui Paulo }
566fc1f75e5SRui Paulo 
567fc1f75e5SRui Paulo int
568454e82d7SRui Paulo amdtemp_detach(device_t dev)
569fc1f75e5SRui Paulo {
570454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
571a4165bbaSJung-uk Kim 	int i;
572fc1f75e5SRui Paulo 
573a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
574a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
575fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
576fc1f75e5SRui Paulo 
577454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
578fc1f75e5SRui Paulo 
579fc1f75e5SRui Paulo 	return (0);
580fc1f75e5SRui Paulo }
581fc1f75e5SRui Paulo 
582fc1f75e5SRui Paulo static int
583454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
584fc1f75e5SRui Paulo {
585fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
586454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
587a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
588a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
589fc1f75e5SRui Paulo 	int error;
590fc1f75e5SRui Paulo 
591a4165bbaSJung-uk Kim 	switch (sensor) {
592fc1f75e5SRui Paulo 	case CORE0:
593074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
594074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
595fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
596fc1f75e5SRui Paulo 		break;
597fc1f75e5SRui Paulo 	case CORE1:
598074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
599074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
600fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
601fc1f75e5SRui Paulo 		break;
602fc1f75e5SRui Paulo 	default:
603a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
604fc1f75e5SRui Paulo 		break;
605fc1f75e5SRui Paulo 	}
606fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
607fc1f75e5SRui Paulo 
608fc1f75e5SRui Paulo 	return (error);
609fc1f75e5SRui Paulo }
610fc1f75e5SRui Paulo 
6119d6672e1SLuiz Otavio O Souza #define	AMDTEMP_ZERO_C_TO_K	2731
612a4165bbaSJung-uk Kim 
613fc1f75e5SRui Paulo static int32_t
614454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
615fc1f75e5SRui Paulo {
616a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
617074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
618fc1f75e5SRui Paulo 
619a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
620074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
621074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
622fc1f75e5SRui Paulo 	switch (sensor) {
623074d80acSJung-uk Kim 	case CORE0_SENSOR1:
624074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
6257ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
626074d80acSJung-uk Kim 	case CORE0_SENSOR0:
627a4165bbaSJung-uk Kim 	case CORE0:
628fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
629074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
630fc1f75e5SRui Paulo 		break;
631074d80acSJung-uk Kim 	case CORE1_SENSOR1:
632074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
6337ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
634074d80acSJung-uk Kim 	case CORE1_SENSOR0:
635a4165bbaSJung-uk Kim 	case CORE1:
636fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
637074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
638fc1f75e5SRui Paulo 		break;
639fc1f75e5SRui Paulo 	}
640074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
641a4165bbaSJung-uk Kim 
642fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
643074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
644074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
645074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
646074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
647454e82d7SRui Paulo 
648454e82d7SRui Paulo 	return (temp);
649454e82d7SRui Paulo }
650454e82d7SRui Paulo 
651*e49ec461SConrad Meyer static uint32_t
652*e49ec461SConrad Meyer amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val)
653*e49ec461SConrad Meyer {
654*e49ec461SConrad Meyer 	uint32_t temp;
655*e49ec461SConrad Meyer 
656*e49ec461SConrad Meyer 	/* Convert raw register subfield units (0.125C) to units of 0.1C. */
657*e49ec461SConrad Meyer 	temp = ((val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT) &
658*e49ec461SConrad Meyer 	    AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4;
659*e49ec461SConrad Meyer 
660*e49ec461SConrad Meyer 	/*
661*e49ec461SConrad Meyer 	 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
662*e49ec461SConrad Meyer 	 * adjusted down by 49.0 degrees Celsius.  (This adjustment is not
663*e49ec461SConrad Meyer 	 * documented in BKDGs prior to family 15h model 00h.)
664*e49ec461SConrad Meyer 	 */
665*e49ec461SConrad Meyer 	if (CPUID_TO_FAMILY(cpu_id) >= 0x15 &&
666*e49ec461SConrad Meyer 	    ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) &
667*e49ec461SConrad Meyer 	    AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3)
668*e49ec461SConrad Meyer 		temp -= AMDTEMP_CURTMP_RANGE_ADJUST;
669*e49ec461SConrad Meyer 
670*e49ec461SConrad Meyer 	temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10;
671*e49ec461SConrad Meyer 	return (temp);
672*e49ec461SConrad Meyer }
673*e49ec461SConrad Meyer 
674454e82d7SRui Paulo static int32_t
675454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
676454e82d7SRui Paulo {
677074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
678454e82d7SRui Paulo 	uint32_t temp;
679a4165bbaSJung-uk Kim 
680a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
681*e49ec461SConrad Meyer 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp));
682*e49ec461SConrad Meyer }
683fc1f75e5SRui Paulo 
684*e49ec461SConrad Meyer static int32_t
685*e49ec461SConrad Meyer amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor)
686*e49ec461SConrad Meyer {
687*e49ec461SConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
688*e49ec461SConrad Meyer 	uint32_t val;
689*e49ec461SConrad Meyer 	int error;
690*e49ec461SConrad Meyer 
691*e49ec461SConrad Meyer 	error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val);
692*e49ec461SConrad Meyer 	KASSERT(error == 0, ("amdsmn_read"));
693*e49ec461SConrad Meyer 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val));
694fc1f75e5SRui Paulo }
695a03d621bSConrad Meyer 
696a03d621bSConrad Meyer static int32_t
697a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
698a03d621bSConrad Meyer {
699a03d621bSConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
700fbd5d782SConrad Meyer 	uint32_t temp, val;
701a03d621bSConrad Meyer 	int error;
702a03d621bSConrad Meyer 
703fbd5d782SConrad Meyer 	error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val);
704a03d621bSConrad Meyer 	KASSERT(error == 0, ("amdsmn_read"));
705a03d621bSConrad Meyer 
706fbd5d782SConrad Meyer 	temp = ((val >> 21) & 0x7ff) * 5 / 4;
707fbd5d782SConrad Meyer 	if ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0)
708*e49ec461SConrad Meyer 		temp -= AMDTEMP_CURTMP_RANGE_ADJUST;
709a03d621bSConrad Meyer 	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
710a03d621bSConrad Meyer 
711a03d621bSConrad Meyer 	return (temp);
712a03d621bSConrad Meyer }
713