xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision ecbe99e1627627b6797153899e4d95fde3e230e2)
1fc1f75e5SRui Paulo /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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.
8c59b9a4fSConrad Meyer  * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
9fc1f75e5SRui Paulo  *
10fc1f75e5SRui Paulo  * Redistribution and use in source and binary forms, with or without
11fc1f75e5SRui Paulo  * modification, are permitted provided that the following conditions
12fc1f75e5SRui Paulo  * are met:
13fc1f75e5SRui Paulo  * 1. Redistributions of source code must retain the above copyright
14fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer.
15fc1f75e5SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
16fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
17fc1f75e5SRui Paulo  *    documentation and/or other materials provided with the distribution.
18fc1f75e5SRui Paulo  *
19fc1f75e5SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20fc1f75e5SRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21fc1f75e5SRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22fc1f75e5SRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23fc1f75e5SRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24fc1f75e5SRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25fc1f75e5SRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26fc1f75e5SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27fc1f75e5SRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28fc1f75e5SRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fc1f75e5SRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
30fc1f75e5SRui Paulo  */
31fc1f75e5SRui Paulo 
32fc1f75e5SRui Paulo /*
33074d80acSJung-uk Kim  * Driver for the AMD CPU on-die thermal sensors.
34a4165bbaSJung-uk Kim  * Initially based on the k8temp Linux driver.
35fc1f75e5SRui Paulo  */
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>
416c101ed7SAlexander Motin #include <sys/lock.h>
42a4165bbaSJung-uk Kim #include <sys/module.h>
436c101ed7SAlexander Motin #include <sys/mutex.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,
62c59b9a4fSConrad Meyer 	CORE1,
63c59b9a4fSConrad Meyer 	CCD1,
64c59b9a4fSConrad Meyer 	CCD_BASE = CCD1,
65c59b9a4fSConrad Meyer 	CCD2,
66c59b9a4fSConrad Meyer 	CCD3,
67c59b9a4fSConrad Meyer 	CCD4,
68c59b9a4fSConrad Meyer 	CCD5,
69c59b9a4fSConrad Meyer 	CCD6,
70c59b9a4fSConrad Meyer 	CCD7,
71c59b9a4fSConrad Meyer 	CCD8,
7251c69c86SXin LI 	CCD9,
7351c69c86SXin LI 	CCD10,
7451c69c86SXin LI 	CCD11,
7551c69c86SXin LI 	CCD12,
7651c69c86SXin LI 	CCD_MAX = CCD12,
77c59b9a4fSConrad Meyer 	NUM_CCDS = CCD_MAX - CCD_BASE + 1,
78454e82d7SRui Paulo } amdsensor_t;
79454e82d7SRui Paulo 
80454e82d7SRui Paulo struct amdtemp_softc {
81a4165bbaSJung-uk Kim 	int		sc_ncores;
82454e82d7SRui Paulo 	int		sc_ntemps;
83fdfa6079SJung-uk Kim 	int		sc_flags;
84074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
85074d80acSJung-uk Kim #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
86074d80acSJung-uk Kim #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
87074d80acSJung-uk Kim 	int32_t		sc_offset;
88323a94afSAkio Morita 	int32_t		sc_temp_base;
89454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
90a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
91a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
92a03d621bSConrad Meyer 	device_t	sc_smn;
936c101ed7SAlexander Motin 	struct mtx	sc_lock;
94454e82d7SRui Paulo };
95454e82d7SRui Paulo 
96e49ec461SConrad Meyer /*
97e49ec461SConrad Meyer  * N.B. The numbers in macro names below are significant and represent CPU
98e49ec461SConrad Meyer  * family and model numbers.  Do not make up fictitious family or model numbers
99e49ec461SConrad Meyer  * when adding support for new devices.
100e49ec461SConrad Meyer  */
101454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
102454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
103454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
104454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
105074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC14	0x1703
106074d80acSJung-uk Kim #define	DEVICEID_AMD_MISC15	0x1603
107e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M10H	0x1403
108e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M30H	0x141d
109e49ec461SConrad Meyer #define	DEVICEID_AMD_MISC15_M60H_ROOT	0x1576
1102b56f12bSChristian Brueffer #define	DEVICEID_AMD_MISC16	0x1533
111df20515dSLuiz Otavio O Souza #define	DEVICEID_AMD_MISC16_M30H	0x1583
1129d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_ROOT	0x1450
1139d49c422SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M10H_ROOT	0x15d0
114ea6189d3SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M30H_ROOT	0x1480	/* Also M70H, F19H M00H/M20H */
1155b505170SConrad Meyer #define	DEVICEID_AMD_HOSTB17H_M60H_ROOT	0x1630
11651c69c86SXin LI #define	DEVICEID_AMD_HOSTB19H_M10H_ROOT	0x14a4
117*ecbe99e1SMatthias Lanter #define	DEVICEID_AMD_HOSTB19H_M40H_ROOT	0x14b5
118323a94afSAkio Morita #define	DEVICEID_AMD_HOSTB19H_M60H_ROOT	0x14d8
119ef3f8aa0SOliver Fromme #define	DEVICEID_AMD_HOSTB19H_M70H_ROOT	0x14e8
120454e82d7SRui Paulo 
121e49ec461SConrad Meyer static const struct amdtemp_product {
122454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
123454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
124e49ec461SConrad Meyer 	/*
125e49ec461SConrad Meyer 	 * 0xFC register is only valid on the D18F3 PCI device; SMN temp
126e49ec461SConrad Meyer 	 * drivers do not attach to that device.
127e49ec461SConrad Meyer 	 */
128e49ec461SConrad Meyer 	bool		amdtemp_has_cpuid;
129454e82d7SRui Paulo } amdtemp_products[] = {
130e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F, true },
131e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10, true },
132e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11, true },
133e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14, true },
134e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15, true },
135e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M10H, true },
136e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M30H, true },
137e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M60H_ROOT, false },
138e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16, true },
139e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H, true },
140e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_ROOT, false },
141e49ec461SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M10H_ROOT, false },
14285dbddbeSConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M30H_ROOT, false },
1435b505170SConrad Meyer 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M60H_ROOT, false },
14451c69c86SXin LI 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB19H_M10H_ROOT, false },
145*ecbe99e1SMatthias Lanter 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M40H_ROOT, false },
146323a94afSAkio Morita 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB19H_M60H_ROOT, false },
147ef3f8aa0SOliver Fromme 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB19H_M70H_ROOT, false },
148454e82d7SRui Paulo };
149454e82d7SRui Paulo 
150454e82d7SRui Paulo /*
151e49ec461SConrad Meyer  * Reported Temperature Control Register, family 0Fh-15h (some models), 16h.
152454e82d7SRui Paulo  */
153a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
154454e82d7SRui Paulo 
155e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_CURTMP_MASK	0x7ff
156e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_CURTMP_SHIFT	21
157e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_TJSEL_MASK	0x3
158e49ec461SConrad Meyer #define	AMDTEMP_REPTMP10H_TJSEL_SHIFT	16
159e49ec461SConrad Meyer 
160e49ec461SConrad Meyer /*
161e49ec461SConrad Meyer  * Reported Temperature, Family 15h, M60+
162e49ec461SConrad Meyer  *
163e49ec461SConrad Meyer  * Same register bit definitions as other Family 15h CPUs, but access is
164e49ec461SConrad Meyer  * indirect via SMN, like Family 17h.
165e49ec461SConrad Meyer  */
166e49ec461SConrad Meyer #define	AMDTEMP_15H_M60H_REPTMP_CTRL	0xd8200ca4
167e49ec461SConrad Meyer 
168454e82d7SRui Paulo /*
169a03d621bSConrad Meyer  * Reported Temperature, Family 17h
170fbd5d782SConrad Meyer  *
171fbd5d782SConrad Meyer  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
172fbd5d782SConrad Meyer  * provide the current temp.  bit 19, when clear, means the temp is reported in
173fbd5d782SConrad Meyer  * a range 0.."225C" (probable typo for 255C), and when set changes the range
174fbd5d782SConrad Meyer  * to -49..206C.
175a03d621bSConrad Meyer  */
176a03d621bSConrad Meyer #define	AMDTEMP_17H_CUR_TMP		0x59800
177c59b9a4fSConrad Meyer #define	AMDTEMP_17H_CUR_TMP_RANGE_SEL	(1u << 19)
178c59b9a4fSConrad Meyer /*
179c1cbabe8SVal Packett  * Bits 16-17, when set, mean that CUR_TMP is read-write. When it is, the
180c1cbabe8SVal Packett  * 49 degree offset should apply as well. This was revealed in a Linux
181c1cbabe8SVal Packett  * patch from an AMD employee.
182c1cbabe8SVal Packett  */
183c1cbabe8SVal Packett #define	AMDTEMP_17H_CUR_TMP_TJ_SEL	((1u << 17) | (1u << 16))
184c1cbabe8SVal Packett /*
185c59b9a4fSConrad Meyer  * The following register set was discovered experimentally by Ondrej Čerman
186c59b9a4fSConrad Meyer  * and collaborators, but is not (yet) documented in a PPR/OSRR (other than
187c59b9a4fSConrad Meyer  * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to
188c59b9a4fSConrad Meyer  * SMU::THM).  It seems plausible and the Linux sensor folks have adopted it.
189c59b9a4fSConrad Meyer  */
190c59b9a4fSConrad Meyer #define	AMDTEMP_17H_CCD_TMP_BASE	0x59954
191c59b9a4fSConrad Meyer #define	AMDTEMP_17H_CCD_TMP_VALID	(1u << 11)
192e49ec461SConrad Meyer 
19351c69c86SXin LI #define	AMDTEMP_ZEN4_10H_CCD_TMP_BASE	0x59b00
194323a94afSAkio Morita #define	AMDTEMP_ZEN4_CCD_TMP_BASE	0x59b08
195323a94afSAkio Morita 
196e49ec461SConrad Meyer /*
197e49ec461SConrad Meyer  * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius).
198e49ec461SConrad Meyer  */
199e49ec461SConrad Meyer #define	AMDTEMP_CURTMP_RANGE_ADJUST	490
200a03d621bSConrad Meyer 
201a03d621bSConrad Meyer /*
202074d80acSJung-uk Kim  * Thermaltrip Status Register (Family 0Fh only)
203454e82d7SRui Paulo  */
204a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
205074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04
206074d80acSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40
207074d80acSJung-uk Kim 
208074d80acSJung-uk Kim /*
209074d80acSJung-uk Kim  * DRAM Configuration High Register
210074d80acSJung-uk Kim  */
211074d80acSJung-uk Kim #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
212074d80acSJung-uk Kim #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
213454e82d7SRui Paulo 
214a4165bbaSJung-uk Kim /*
215a4165bbaSJung-uk Kim  * CPU Family/Model Register
216a4165bbaSJung-uk Kim  */
217a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
218fc1f75e5SRui Paulo 
219fc1f75e5SRui Paulo /*
220fc1f75e5SRui Paulo  * Device methods.
221fc1f75e5SRui Paulo  */
222454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
223454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
224454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
225454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
226454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
227454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
228454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
229e49ec461SConrad Meyer static int32_t	amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor);
230a03d621bSConrad Meyer static int32_t	amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
231c59b9a4fSConrad Meyer static void	amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model);
232ea6189d3SConrad Meyer static void	amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model);
233454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
234fc1f75e5SRui Paulo 
235454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
236fc1f75e5SRui Paulo 	/* Device interface */
237454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
238454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
239454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
240454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
241fc1f75e5SRui Paulo 
24261bfd867SSofian Brabez 	DEVMETHOD_END
243fc1f75e5SRui Paulo };
244fc1f75e5SRui Paulo 
245454e82d7SRui Paulo static driver_t amdtemp_driver = {
246454e82d7SRui Paulo 	"amdtemp",
247454e82d7SRui Paulo 	amdtemp_methods,
248454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
249fc1f75e5SRui Paulo };
250fc1f75e5SRui Paulo 
25183a273efSJohn Baldwin DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, NULL, NULL);
252a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1);
253a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
254a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
255329e817fSWarner Losh     nitems(amdtemp_products));
256fc1f75e5SRui Paulo 
257e49ec461SConrad Meyer static bool
258e49ec461SConrad Meyer amdtemp_match(device_t dev, const struct amdtemp_product **product_out)
259fc1f75e5SRui Paulo {
260fc1f75e5SRui Paulo 	int i;
261fc1f75e5SRui Paulo 	uint16_t vendor, devid;
262fc1f75e5SRui Paulo 
263fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
264fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
265fc1f75e5SRui Paulo 
266a64bf59cSConrad Meyer 	for (i = 0; i < nitems(amdtemp_products); i++) {
267454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
268e49ec461SConrad Meyer 		    devid == amdtemp_products[i].amdtemp_deviceid) {
269e49ec461SConrad Meyer 			if (product_out != NULL)
270e49ec461SConrad Meyer 				*product_out = &amdtemp_products[i];
271e49ec461SConrad Meyer 			return (true);
272fc1f75e5SRui Paulo 		}
273e49ec461SConrad Meyer 	}
274e49ec461SConrad Meyer 	return (false);
275fc1f75e5SRui Paulo }
276fc1f75e5SRui Paulo 
277fc1f75e5SRui Paulo static void
278454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
279fc1f75e5SRui Paulo {
280fc1f75e5SRui Paulo 	device_t child;
281fc1f75e5SRui Paulo 
282fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
283454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
284fc1f75e5SRui Paulo 		return;
285fc1f75e5SRui Paulo 
286e49ec461SConrad Meyer 	if (amdtemp_match(parent, NULL)) {
2875b56413dSWarner Losh 		child = device_add_child(parent, "amdtemp", DEVICE_UNIT_ANY);
288fc1f75e5SRui Paulo 		if (child == NULL)
289454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
290fc1f75e5SRui Paulo 	}
291fc1f75e5SRui Paulo }
292fc1f75e5SRui Paulo 
293fc1f75e5SRui Paulo static int
294454e82d7SRui Paulo amdtemp_probe(device_t dev)
295fc1f75e5SRui Paulo {
296fdfa6079SJung-uk Kim 	uint32_t family, model;
297fc1f75e5SRui Paulo 
298a8de37b0SEitan Adler 	if (resource_disabled("amdtemp", 0))
299a8de37b0SEitan Adler 		return (ENXIO);
300e49ec461SConrad Meyer 	if (!amdtemp_match(device_get_parent(dev), NULL))
30140f7bccbSConrad Meyer 		return (ENXIO);
302a8de37b0SEitan Adler 
303fdfa6079SJung-uk Kim 	family = CPUID_TO_FAMILY(cpu_id);
304fdfa6079SJung-uk Kim 	model = CPUID_TO_MODEL(cpu_id);
305a4165bbaSJung-uk Kim 
306a4165bbaSJung-uk Kim 	switch (family) {
307a4165bbaSJung-uk Kim 	case 0x0f:
308fdfa6079SJung-uk Kim 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
309fdfa6079SJung-uk Kim 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
310a4165bbaSJung-uk Kim 			return (ENXIO);
311a4165bbaSJung-uk Kim 		break;
312a4165bbaSJung-uk Kim 	case 0x10:
313a4165bbaSJung-uk Kim 	case 0x11:
314074d80acSJung-uk Kim 	case 0x12:
315074d80acSJung-uk Kim 	case 0x14:
316074d80acSJung-uk Kim 	case 0x15:
3172b56f12bSChristian Brueffer 	case 0x16:
318a03d621bSConrad Meyer 	case 0x17:
319ea6189d3SConrad Meyer 	case 0x19:
320a4165bbaSJung-uk Kim 		break;
321a4165bbaSJung-uk Kim 	default:
322fc1f75e5SRui Paulo 		return (ENXIO);
323fc1f75e5SRui Paulo 	}
324a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
325fc1f75e5SRui Paulo 
326fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
327fc1f75e5SRui Paulo }
328fc1f75e5SRui Paulo 
329fc1f75e5SRui Paulo static int
330454e82d7SRui Paulo amdtemp_attach(device_t dev)
331fc1f75e5SRui Paulo {
332074d80acSJung-uk Kim 	char tn[32];
333074d80acSJung-uk Kim 	u_int regs[4];
334e49ec461SConrad Meyer 	const struct amdtemp_product *product;
335e49ec461SConrad Meyer 	struct amdtemp_softc *sc;
336fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
337fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
338a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
339074d80acSJung-uk Kim 	u_int bid;
340074d80acSJung-uk Kim 	int erratum319, unit;
341e49ec461SConrad Meyer 	bool needsmn;
342fc1f75e5SRui Paulo 
343e49ec461SConrad Meyer 	sc = device_get_softc(dev);
344074d80acSJung-uk Kim 	erratum319 = 0;
345e49ec461SConrad Meyer 	needsmn = false;
346fdfa6079SJung-uk Kim 
347e49ec461SConrad Meyer 	if (!amdtemp_match(device_get_parent(dev), &product))
348e49ec461SConrad Meyer 		return (ENXIO);
349e49ec461SConrad Meyer 
350074d80acSJung-uk Kim 	cpuid = cpu_id;
351074d80acSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
352074d80acSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
353e49ec461SConrad Meyer 
354e49ec461SConrad Meyer 	/*
355e49ec461SConrad Meyer 	 * This checks for the byzantine condition of running a heterogenous
356e49ec461SConrad Meyer 	 * revision multi-socket system where the attach thread is potentially
357e49ec461SConrad Meyer 	 * probing a remote socket's PCI device.
358e49ec461SConrad Meyer 	 *
359e49ec461SConrad Meyer 	 * Currently, such scenarios are unsupported on models using the SMN
360e49ec461SConrad Meyer 	 * (because on those models, amdtemp(4) attaches to a different PCI
361e49ec461SConrad Meyer 	 * device than the one that contains AMDTEMP_CPUID).
362e49ec461SConrad Meyer 	 *
363e49ec461SConrad Meyer 	 * The ancient 0x0F family of devices only supports this register from
364e49ec461SConrad Meyer 	 * models 40h+.
365e49ec461SConrad Meyer 	 */
366e49ec461SConrad Meyer 	if (product->amdtemp_has_cpuid && (family > 0x0f ||
367e49ec461SConrad Meyer 	    (family == 0x0f && model >= 0x40))) {
368e49ec461SConrad Meyer 		cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID,
369e49ec461SConrad Meyer 		    4);
370a4165bbaSJung-uk Kim 		family = CPUID_TO_FAMILY(cpuid);
371a4165bbaSJung-uk Kim 		model = CPUID_TO_MODEL(cpuid);
372fdfa6079SJung-uk Kim 	}
373a4165bbaSJung-uk Kim 
374a4165bbaSJung-uk Kim 	switch (family) {
375a4165bbaSJung-uk Kim 	case 0x0f:
376a4165bbaSJung-uk Kim 		/*
377fdfa6079SJung-uk Kim 		 * Thermaltrip Status Register
378fdfa6079SJung-uk Kim 		 *
379fdfa6079SJung-uk Kim 		 * - ThermSenseCoreSel
380fdfa6079SJung-uk Kim 		 *
381fdfa6079SJung-uk Kim 		 * Revision F & G:	0 - Core1, 1 - Core0
382fdfa6079SJung-uk Kim 		 * Other:		0 - Core0, 1 - Core1
383fdfa6079SJung-uk Kim 		 *
384fdfa6079SJung-uk Kim 		 * - CurTmp
385a4165bbaSJung-uk Kim 		 *
386a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
387fdfa6079SJung-uk Kim 		 * Other:		bits 23-16
388a4165bbaSJung-uk Kim 		 *
389fdfa6079SJung-uk Kim 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
390fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel bits were introduced in Revision F
391fdfa6079SJung-uk Kim 		 * but CurTmp seems working fine as early as Revision C.
392fdfa6079SJung-uk Kim 		 * However, it is not clear whether ThermSenseSel and/or
393fdfa6079SJung-uk Kim 		 * ThermSenseCoreSel work in undocumented cases as well.
394fdfa6079SJung-uk Kim 		 * In fact, the Linux driver suggests it may not work but
395fdfa6079SJung-uk Kim 		 * we just assume it does until we find otherwise.
396074d80acSJung-uk Kim 		 *
397074d80acSJung-uk Kim 		 * XXX According to Linux, CurTmp starts at -28C on
398074d80acSJung-uk Kim 		 * Socket AM2 Revision G processors, which is not
399074d80acSJung-uk Kim 		 * documented anywhere.
400fc1f75e5SRui Paulo 		 */
401074d80acSJung-uk Kim 		if (model >= 0x40)
402fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
403074d80acSJung-uk Kim 		if (model >= 0x60 && model != 0xc1) {
404074d80acSJung-uk Kim 			do_cpuid(0x80000001, regs);
405074d80acSJung-uk Kim 			bid = (regs[1] >> 9) & 0x1f;
406074d80acSJung-uk Kim 			switch (model) {
407074d80acSJung-uk Kim 			case 0x68: /* Socket S1g1 */
408074d80acSJung-uk Kim 			case 0x6c:
409074d80acSJung-uk Kim 			case 0x7c:
410074d80acSJung-uk Kim 				break;
411074d80acSJung-uk Kim 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
412074d80acSJung-uk Kim 				if (bid != 0x0b && bid != 0x0c)
413074d80acSJung-uk Kim 					sc->sc_flags |=
414074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
415074d80acSJung-uk Kim 				break;
416074d80acSJung-uk Kim 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
417074d80acSJung-uk Kim 			case 0x7f:
418074d80acSJung-uk Kim 				if (bid != 0x07 && bid != 0x09 &&
419074d80acSJung-uk Kim 				    bid != 0x0c)
420074d80acSJung-uk Kim 					sc->sc_flags |=
421074d80acSJung-uk Kim 					    AMDTEMP_FLAG_ALT_OFFSET;
422074d80acSJung-uk Kim 				break;
423074d80acSJung-uk Kim 			default:
424074d80acSJung-uk Kim 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
425074d80acSJung-uk Kim 			}
426fdfa6079SJung-uk Kim 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
427fdfa6079SJung-uk Kim 		}
428a4165bbaSJung-uk Kim 
429a4165bbaSJung-uk Kim 		/*
430a4165bbaSJung-uk Kim 		 * There are two sensors per core.
431a4165bbaSJung-uk Kim 		 */
432a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
433a4165bbaSJung-uk Kim 
434a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
435a4165bbaSJung-uk Kim 		break;
436a4165bbaSJung-uk Kim 	case 0x10:
437074d80acSJung-uk Kim 		/*
438074d80acSJung-uk Kim 		 * Erratum 319 Inaccurate Temperature Measurement
439074d80acSJung-uk Kim 		 *
440074d80acSJung-uk Kim 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
441074d80acSJung-uk Kim 		 */
442074d80acSJung-uk Kim 		do_cpuid(0x80000001, regs);
443074d80acSJung-uk Kim 		switch ((regs[1] >> 28) & 0xf) {
444074d80acSJung-uk Kim 		case 0:	/* Socket F */
445074d80acSJung-uk Kim 			erratum319 = 1;
446074d80acSJung-uk Kim 			break;
447074d80acSJung-uk Kim 		case 1:	/* Socket AM2+ or AM3 */
4481587a9dbSJohn Baldwin 			if ((pci_cfgregread(pci_get_domain(dev),
4491587a9dbSJohn Baldwin 			    pci_get_bus(dev), pci_get_slot(dev), 2,
4501587a9dbSJohn Baldwin 			    AMDTEMP_DRAM_CONF_HIGH, 2) &
451074d80acSJung-uk Kim 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
452074d80acSJung-uk Kim 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
453074d80acSJung-uk Kim 				break;
454074d80acSJung-uk Kim 			/* XXX 00100F42h (RB-C2) exists in both formats. */
455074d80acSJung-uk Kim 			erratum319 = 1;
456074d80acSJung-uk Kim 			break;
457074d80acSJung-uk Kim 		}
458074d80acSJung-uk Kim 		/* FALLTHROUGH */
459a4165bbaSJung-uk Kim 	case 0x11:
460074d80acSJung-uk Kim 	case 0x12:
461074d80acSJung-uk Kim 	case 0x14:
462074d80acSJung-uk Kim 	case 0x15:
4632b56f12bSChristian Brueffer 	case 0x16:
464a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
465e49ec461SConrad Meyer 		/*
466e49ec461SConrad Meyer 		 * Some later (60h+) models of family 15h use a similar SMN
467e49ec461SConrad Meyer 		 * network as family 17h.  (However, the register index differs
468e49ec461SConrad Meyer 		 * from 17h and the decoding matches other 10h-15h models,
469e49ec461SConrad Meyer 		 * which differ from 17h.)
470e49ec461SConrad Meyer 		 */
471e49ec461SConrad Meyer 		if (family == 0x15 && model >= 0x60) {
472e49ec461SConrad Meyer 			sc->sc_gettemp = amdtemp_gettemp15hm60h;
473e49ec461SConrad Meyer 			needsmn = true;
474e49ec461SConrad Meyer 		} else
475a4165bbaSJung-uk Kim 			sc->sc_gettemp = amdtemp_gettemp;
476a4165bbaSJung-uk Kim 		break;
477a03d621bSConrad Meyer 	case 0x17:
478ea6189d3SConrad Meyer 	case 0x19:
479a03d621bSConrad Meyer 		sc->sc_ntemps = 1;
480a03d621bSConrad Meyer 		sc->sc_gettemp = amdtemp_gettemp17h;
481e49ec461SConrad Meyer 		needsmn = true;
482e49ec461SConrad Meyer 		break;
483e49ec461SConrad Meyer 	default:
484e49ec461SConrad Meyer 		device_printf(dev, "Bogus family 0x%x\n", family);
485e49ec461SConrad Meyer 		return (ENXIO);
486e49ec461SConrad Meyer 	}
487e49ec461SConrad Meyer 
488e49ec461SConrad Meyer 	if (needsmn) {
489a03d621bSConrad Meyer 		sc->sc_smn = device_find_child(
490a03d621bSConrad Meyer 		    device_get_parent(dev), "amdsmn", -1);
491a03d621bSConrad Meyer 		if (sc->sc_smn == NULL) {
492a03d621bSConrad Meyer 			if (bootverbose)
493a03d621bSConrad Meyer 				device_printf(dev, "No SMN device found\n");
494a03d621bSConrad Meyer 			return (ENXIO);
495a03d621bSConrad Meyer 		}
496fc1f75e5SRui Paulo 	}
497fc1f75e5SRui Paulo 
498a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
499a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
500a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
501a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
502a4165bbaSJung-uk Kim 		return (ENXIO);
503a4165bbaSJung-uk Kim 
5046c101ed7SAlexander Motin 	mtx_init(&sc->sc_lock, "amdtemp", NULL, MTX_DEF);
505074d80acSJung-uk Kim 	if (erratum319)
506074d80acSJung-uk Kim 		device_printf(dev,
507074d80acSJung-uk Kim 		    "Erratum 319: temperature measurement may be inaccurate\n");
508a4165bbaSJung-uk Kim 	if (bootverbose)
509a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
510a4165bbaSJung-uk Kim 		    sc->sc_ncores,
511a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
512454e82d7SRui Paulo 
513fc1f75e5SRui Paulo 	/*
514454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
515fc1f75e5SRui Paulo 	 */
516074d80acSJung-uk Kim 	unit = device_get_unit(dev);
517074d80acSJung-uk Kim 	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
518074d80acSJung-uk Kim 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
519074d80acSJung-uk Kim 
520fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
521074d80acSJung-uk Kim 	SYSCTL_ADD_INT(sysctlctx,
522074d80acSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
523074d80acSJung-uk Kim 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
524074d80acSJung-uk Kim 	    "Temperature sensor offset");
525fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
526a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5277029da5cSPawel Biernacki 	    "core0", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Core 0");
528fc1f75e5SRui Paulo 
529fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
530fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
5317029da5cSPawel Biernacki 	    OID_AUTO, "sensor0",
5326c101ed7SAlexander Motin 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
533074d80acSJung-uk Kim 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
534074d80acSJung-uk Kim 	    "Core 0 / Sensor 0 temperature");
535fc1f75e5SRui Paulo 
536323a94afSAkio Morita 	sc->sc_temp_base = AMDTEMP_17H_CCD_TMP_BASE;
537323a94afSAkio Morita 
538c59b9a4fSConrad Meyer 	if (family == 0x17)
539c59b9a4fSConrad Meyer 		amdtemp_probe_ccd_sensors17h(dev, model);
540ea6189d3SConrad Meyer 	else if (family == 0x19)
541ea6189d3SConrad Meyer 		amdtemp_probe_ccd_sensors19h(dev, model);
542c59b9a4fSConrad Meyer 	else if (sc->sc_ntemps > 1) {
543fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
544fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
5457029da5cSPawel Biernacki 		    OID_AUTO, "sensor1",
5466c101ed7SAlexander Motin 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
547074d80acSJung-uk Kim 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
548074d80acSJung-uk Kim 		    "Core 0 / Sensor 1 temperature");
549fc1f75e5SRui Paulo 
550074d80acSJung-uk Kim 		if (sc->sc_ncores > 1) {
551fc1f75e5SRui Paulo 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
552074d80acSJung-uk Kim 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
5537029da5cSPawel Biernacki 			    OID_AUTO, "core1", CTLFLAG_RD | CTLFLAG_MPSAFE,
5547029da5cSPawel Biernacki 			    0, "Core 1");
555fc1f75e5SRui Paulo 
556fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
557fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
5587029da5cSPawel Biernacki 			    OID_AUTO, "sensor0",
5596c101ed7SAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
560074d80acSJung-uk Kim 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
561074d80acSJung-uk Kim 			    "Core 1 / Sensor 0 temperature");
562fc1f75e5SRui Paulo 
563fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
564fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
5657029da5cSPawel Biernacki 			    OID_AUTO, "sensor1",
5666c101ed7SAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
567074d80acSJung-uk Kim 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
568074d80acSJung-uk Kim 			    "Core 1 / Sensor 1 temperature");
569074d80acSJung-uk Kim 		}
570a4165bbaSJung-uk Kim 	}
571a4165bbaSJung-uk Kim 
572a4165bbaSJung-uk Kim 	/*
573a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
574a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
575a4165bbaSJung-uk Kim 	 * after us.
576a4165bbaSJung-uk Kim 	 */
577a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
578a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
579a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
580a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
581a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
582a4165bbaSJung-uk Kim 		return (ENXIO);
583a4165bbaSJung-uk Kim 	}
584fc1f75e5SRui Paulo 
585fc1f75e5SRui Paulo 	return (0);
586fc1f75e5SRui Paulo }
587fc1f75e5SRui Paulo 
588fc1f75e5SRui Paulo void
589454e82d7SRui Paulo amdtemp_intrhook(void *arg)
590fc1f75e5SRui Paulo {
591454e82d7SRui Paulo 	struct amdtemp_softc *sc;
592fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
593a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
594a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
595a4165bbaSJung-uk Kim 	amdsensor_t sensor;
596a4165bbaSJung-uk Kim 	int i;
597fc1f75e5SRui Paulo 
598fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
599fc1f75e5SRui Paulo 
600fc1f75e5SRui Paulo 	/*
601fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
602fc1f75e5SRui Paulo 	 */
603fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
604fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
605fc1f75e5SRui Paulo 
606a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
607a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
608a4165bbaSJung-uk Kim 			continue;
609fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
610a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
611a4165bbaSJung-uk Kim 		if (cpu != NULL) {
612fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
613fc1f75e5SRui Paulo 
614a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
615074d80acSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
616fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
617fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
6187029da5cSPawel Biernacki 			    OID_AUTO, "temperature",
6196c101ed7SAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
620a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
621a4165bbaSJung-uk Kim 			    "Current temparature");
622fc1f75e5SRui Paulo 		}
623fc1f75e5SRui Paulo 	}
624a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
625fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
626fc1f75e5SRui Paulo }
627fc1f75e5SRui Paulo 
628fc1f75e5SRui Paulo int
629454e82d7SRui Paulo amdtemp_detach(device_t dev)
630fc1f75e5SRui Paulo {
631454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
632a4165bbaSJung-uk Kim 	int i;
633fc1f75e5SRui Paulo 
634a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
635a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
636fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
637fc1f75e5SRui Paulo 
638454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
639fc1f75e5SRui Paulo 
6406c101ed7SAlexander Motin 	mtx_destroy(&sc->sc_lock);
641fc1f75e5SRui Paulo 	return (0);
642fc1f75e5SRui Paulo }
643fc1f75e5SRui Paulo 
644fc1f75e5SRui Paulo static int
645454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
646fc1f75e5SRui Paulo {
647fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
648454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
649a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
650a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
651fc1f75e5SRui Paulo 	int error;
652fc1f75e5SRui Paulo 
653a4165bbaSJung-uk Kim 	switch (sensor) {
654fc1f75e5SRui Paulo 	case CORE0:
655074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
656074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
657fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
658fc1f75e5SRui Paulo 		break;
659fc1f75e5SRui Paulo 	case CORE1:
660074d80acSJung-uk Kim 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
661074d80acSJung-uk Kim 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
662fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
663fc1f75e5SRui Paulo 		break;
664fc1f75e5SRui Paulo 	default:
665a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
666fc1f75e5SRui Paulo 		break;
667fc1f75e5SRui Paulo 	}
668fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
669fc1f75e5SRui Paulo 
670fc1f75e5SRui Paulo 	return (error);
671fc1f75e5SRui Paulo }
672fc1f75e5SRui Paulo 
6739d6672e1SLuiz Otavio O Souza #define	AMDTEMP_ZERO_C_TO_K	2731
674a4165bbaSJung-uk Kim 
675fc1f75e5SRui Paulo static int32_t
676454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
677fc1f75e5SRui Paulo {
678a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
679074d80acSJung-uk Kim 	uint32_t mask, offset, temp;
680fc1f75e5SRui Paulo 
6816c101ed7SAlexander Motin 	mtx_lock(&sc->sc_lock);
6826c101ed7SAlexander Motin 
683a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
684074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
685074d80acSJung-uk Kim 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
686fc1f75e5SRui Paulo 	switch (sensor) {
687074d80acSJung-uk Kim 	case CORE0_SENSOR1:
688074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
6897ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
690074d80acSJung-uk Kim 	case CORE0_SENSOR0:
691a4165bbaSJung-uk Kim 	case CORE0:
692fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
693074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
694fc1f75e5SRui Paulo 		break;
695074d80acSJung-uk Kim 	case CORE1_SENSOR1:
696074d80acSJung-uk Kim 		temp |= AMDTEMP_TTSR_SELSENSOR;
6977ca2d97bSJung-uk Kim 		/* FALLTHROUGH */
698074d80acSJung-uk Kim 	case CORE1_SENSOR0:
699a4165bbaSJung-uk Kim 	case CORE1:
700fdfa6079SJung-uk Kim 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
701074d80acSJung-uk Kim 			temp |= AMDTEMP_TTSR_SELCORE;
702fc1f75e5SRui Paulo 		break;
703c59b9a4fSConrad Meyer 	default:
704c79cee71SKyle Evans 		__assert_unreachable();
705fc1f75e5SRui Paulo 	}
706074d80acSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
707a4165bbaSJung-uk Kim 
708fdfa6079SJung-uk Kim 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
709074d80acSJung-uk Kim 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
710074d80acSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
711074d80acSJung-uk Kim 	temp = ((temp >> 14) & mask) * 5 / 2;
712074d80acSJung-uk Kim 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
713454e82d7SRui Paulo 
7146c101ed7SAlexander Motin 	mtx_unlock(&sc->sc_lock);
715454e82d7SRui Paulo 	return (temp);
716454e82d7SRui Paulo }
717454e82d7SRui Paulo 
718e49ec461SConrad Meyer static uint32_t
71902f70002SConrad Meyer amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49)
720e49ec461SConrad Meyer {
721e49ec461SConrad Meyer 	uint32_t temp;
722e49ec461SConrad Meyer 
723e49ec461SConrad Meyer 	/* Convert raw register subfield units (0.125C) to units of 0.1C. */
72402f70002SConrad Meyer 	temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4;
72502f70002SConrad Meyer 
72602f70002SConrad Meyer 	if (minus49)
72702f70002SConrad Meyer 		temp -= AMDTEMP_CURTMP_RANGE_ADJUST;
72802f70002SConrad Meyer 
72902f70002SConrad Meyer 	temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10;
73002f70002SConrad Meyer 	return (temp);
73102f70002SConrad Meyer }
73202f70002SConrad Meyer 
73302f70002SConrad Meyer static uint32_t
73402f70002SConrad Meyer amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val)
73502f70002SConrad Meyer {
73602f70002SConrad Meyer 	bool minus49;
737e49ec461SConrad Meyer 
738e49ec461SConrad Meyer 	/*
739e49ec461SConrad Meyer 	 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
740e49ec461SConrad Meyer 	 * adjusted down by 49.0 degrees Celsius.  (This adjustment is not
741e49ec461SConrad Meyer 	 * documented in BKDGs prior to family 15h model 00h.)
742e49ec461SConrad Meyer 	 */
74302f70002SConrad Meyer 	minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 &&
744e49ec461SConrad Meyer 	    ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) &
74502f70002SConrad Meyer 	    AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3);
746e49ec461SConrad Meyer 
74702f70002SConrad Meyer 	return (amdtemp_decode_fam10h_to_17h(sc_offset,
74802f70002SConrad Meyer 	    val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
74902f70002SConrad Meyer }
75002f70002SConrad Meyer 
75102f70002SConrad Meyer static uint32_t
75202f70002SConrad Meyer amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val)
75302f70002SConrad Meyer {
75402f70002SConrad Meyer 	bool minus49;
75502f70002SConrad Meyer 
756c1cbabe8SVal Packett 	minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0)
757c1cbabe8SVal Packett 	    || ((val & AMDTEMP_17H_CUR_TMP_TJ_SEL) == AMDTEMP_17H_CUR_TMP_TJ_SEL);
75802f70002SConrad Meyer 	return (amdtemp_decode_fam10h_to_17h(sc_offset,
75902f70002SConrad Meyer 	    val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
760e49ec461SConrad Meyer }
761e49ec461SConrad Meyer 
762454e82d7SRui Paulo static int32_t
763454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
764454e82d7SRui Paulo {
765074d80acSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
766454e82d7SRui Paulo 	uint32_t temp;
767a4165bbaSJung-uk Kim 
768a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
769e49ec461SConrad Meyer 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp));
770e49ec461SConrad Meyer }
771fc1f75e5SRui Paulo 
772e49ec461SConrad Meyer static int32_t
773e49ec461SConrad Meyer amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor)
774e49ec461SConrad Meyer {
775e49ec461SConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
776e49ec461SConrad Meyer 	uint32_t val;
777b9723c5bSMateusz Guzik 	int error __diagused;
778e49ec461SConrad Meyer 
779e49ec461SConrad Meyer 	error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val);
780e49ec461SConrad Meyer 	KASSERT(error == 0, ("amdsmn_read"));
781e49ec461SConrad Meyer 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val));
782fc1f75e5SRui Paulo }
783a03d621bSConrad Meyer 
784a03d621bSConrad Meyer static int32_t
785a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
786a03d621bSConrad Meyer {
787a03d621bSConrad Meyer 	struct amdtemp_softc *sc = device_get_softc(dev);
78802f70002SConrad Meyer 	uint32_t val;
789b9723c5bSMateusz Guzik 	int error __diagused;
790a03d621bSConrad Meyer 
791c59b9a4fSConrad Meyer 	switch (sensor) {
792c59b9a4fSConrad Meyer 	case CORE0_SENSOR0:
793c59b9a4fSConrad Meyer 		/* Tctl */
794fbd5d782SConrad Meyer 		error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val);
795a03d621bSConrad Meyer 		KASSERT(error == 0, ("amdsmn_read"));
79602f70002SConrad Meyer 		return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val));
797c59b9a4fSConrad Meyer 	case CCD_BASE ... CCD_MAX:
798c59b9a4fSConrad Meyer 		/* Tccd<N> */
799323a94afSAkio Morita 		error = amdsmn_read(sc->sc_smn, sc->sc_temp_base +
800c59b9a4fSConrad Meyer 		    (((int)sensor - CCD_BASE) * sizeof(val)), &val);
801c59b9a4fSConrad Meyer 		KASSERT(error == 0, ("amdsmn_read2"));
802c59b9a4fSConrad Meyer 		KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0,
803c59b9a4fSConrad Meyer 		    ("sensor %d: not valid", (int)sensor));
804c59b9a4fSConrad Meyer 		return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true));
805c59b9a4fSConrad Meyer 	default:
806c79cee71SKyle Evans 		__assert_unreachable();
807c59b9a4fSConrad Meyer 	}
808c59b9a4fSConrad Meyer }
809c59b9a4fSConrad Meyer 
810c59b9a4fSConrad Meyer static void
811ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(device_t dev, uint32_t maxreg)
812c59b9a4fSConrad Meyer {
813c59b9a4fSConrad Meyer 	char sensor_name[16], sensor_descr[32];
814c59b9a4fSConrad Meyer 	struct amdtemp_softc *sc;
815ea6189d3SConrad Meyer 	uint32_t i, val;
816c59b9a4fSConrad Meyer 	int error;
817c59b9a4fSConrad Meyer 
818c59b9a4fSConrad Meyer 	sc = device_get_softc(dev);
819c59b9a4fSConrad Meyer 	for (i = 0; i < maxreg; i++) {
820323a94afSAkio Morita 		error = amdsmn_read(sc->sc_smn, sc->sc_temp_base +
821c59b9a4fSConrad Meyer 		    (i * sizeof(val)), &val);
822c59b9a4fSConrad Meyer 		if (error != 0)
823c59b9a4fSConrad Meyer 			continue;
824c59b9a4fSConrad Meyer 		if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0)
825c59b9a4fSConrad Meyer 			continue;
826c59b9a4fSConrad Meyer 
827c59b9a4fSConrad Meyer 		snprintf(sensor_name, sizeof(sensor_name), "ccd%u", i);
828c59b9a4fSConrad Meyer 		snprintf(sensor_descr, sizeof(sensor_descr),
829c59b9a4fSConrad Meyer 		    "CCD %u temperature (Tccd%u)", i, i);
830c59b9a4fSConrad Meyer 
831c59b9a4fSConrad Meyer 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
832c59b9a4fSConrad Meyer 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
833c59b9a4fSConrad Meyer 		    sensor_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
834c59b9a4fSConrad Meyer 		    dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr);
835c59b9a4fSConrad Meyer 	}
836a03d621bSConrad Meyer }
837ea6189d3SConrad Meyer 
838ea6189d3SConrad Meyer static void
839ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model)
840ea6189d3SConrad Meyer {
841ea6189d3SConrad Meyer 	uint32_t maxreg;
842ea6189d3SConrad Meyer 
843ea6189d3SConrad Meyer 	switch (model) {
844b499ab87SConrad Meyer 	case 0x00 ... 0x2f: /* Zen1, Zen+ */
845ea6189d3SConrad Meyer 		maxreg = 4;
846ea6189d3SConrad Meyer 		break;
847b499ab87SConrad Meyer 	case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */
848b499ab87SConrad Meyer 	case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */
849b499ab87SConrad Meyer 	case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */
850ea6189d3SConrad Meyer 		maxreg = 8;
851ea6189d3SConrad Meyer 		_Static_assert((int)NUM_CCDS >= 8, "");
852ea6189d3SConrad Meyer 		break;
853ea6189d3SConrad Meyer 	default:
854ea6189d3SConrad Meyer 		device_printf(dev,
855ea6189d3SConrad Meyer 		    "Unrecognized Family 17h Model: %02xh\n", model);
856ea6189d3SConrad Meyer 		return;
857ea6189d3SConrad Meyer 	}
858ea6189d3SConrad Meyer 
859ea6189d3SConrad Meyer 	amdtemp_probe_ccd_sensors(dev, maxreg);
860ea6189d3SConrad Meyer }
861ea6189d3SConrad Meyer 
862ea6189d3SConrad Meyer static void
863ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model)
864ea6189d3SConrad Meyer {
865323a94afSAkio Morita 	struct amdtemp_softc *sc = device_get_softc(dev);
866ea6189d3SConrad Meyer 	uint32_t maxreg;
867ea6189d3SConrad Meyer 
868ea6189d3SConrad Meyer 	switch (model) {
869ea6189d3SConrad Meyer 	case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */
870ea6189d3SConrad Meyer 	case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */
871ea6189d3SConrad Meyer 		maxreg = 8;
872ea6189d3SConrad Meyer 		_Static_assert((int)NUM_CCDS >= 8, "");
873ea6189d3SConrad Meyer 		break;
87451c69c86SXin LI 	case 0x10 ... 0x1f:
87551c69c86SXin LI 		sc->sc_temp_base = AMDTEMP_ZEN4_10H_CCD_TMP_BASE;
87651c69c86SXin LI 		maxreg = 12;
87751c69c86SXin LI 		_Static_assert((int)NUM_CCDS >= 12, "");
87851c69c86SXin LI 		break;
879*ecbe99e1SMatthias Lanter 	case 0x40 ... 0x4f: /* Zen3+ Ryzen "Rembrandt" */
880323a94afSAkio Morita 	case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */
881ef3f8aa0SOliver Fromme 	case 0x70 ... 0x7f: /* Zen4 Ryzen "Phoenix" */
882323a94afSAkio Morita 		sc->sc_temp_base = AMDTEMP_ZEN4_CCD_TMP_BASE;
883323a94afSAkio Morita 		maxreg = 8;
884323a94afSAkio Morita 		_Static_assert((int)NUM_CCDS >= 8, "");
885323a94afSAkio Morita 		break;
886ea6189d3SConrad Meyer 	default:
887ea6189d3SConrad Meyer 		device_printf(dev,
888ea6189d3SConrad Meyer 		    "Unrecognized Family 19h Model: %02xh\n", model);
889ea6189d3SConrad Meyer 		return;
890ea6189d3SConrad Meyer 	}
891ea6189d3SConrad Meyer 
892ea6189d3SConrad Meyer 	amdtemp_probe_ccd_sensors(dev, maxreg);
893ea6189d3SConrad Meyer }
894