xref: /dragonfly/sys/dev/powermng/amdtemp/amdtemp.c (revision 4970ada5)
1114b2e18SPierre-Alain TORET /*-
2114b2e18SPierre-Alain TORET  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3114b2e18SPierre-Alain TORET  *
4114b2e18SPierre-Alain TORET  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
5114b2e18SPierre-Alain TORET  * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
6114b2e18SPierre-Alain TORET  * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
7114b2e18SPierre-Alain TORET  * All rights reserved.
8114b2e18SPierre-Alain TORET  * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
9114b2e18SPierre-Alain TORET  *
10114b2e18SPierre-Alain TORET  * Redistribution and use in source and binary forms, with or without
11114b2e18SPierre-Alain TORET  * modification, are permitted provided that the following conditions
12114b2e18SPierre-Alain TORET  * are met:
13114b2e18SPierre-Alain TORET  * 1. Redistributions of source code must retain the above copyright
14114b2e18SPierre-Alain TORET  *    notice, this list of conditions and the following disclaimer.
15114b2e18SPierre-Alain TORET  * 2. Redistributions in binary form must reproduce the above copyright
16114b2e18SPierre-Alain TORET  *    notice, this list of conditions and the following disclaimer in the
17114b2e18SPierre-Alain TORET  *    documentation and/or other materials provided with the distribution.
18114b2e18SPierre-Alain TORET  *
19114b2e18SPierre-Alain TORET  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20114b2e18SPierre-Alain TORET  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21114b2e18SPierre-Alain TORET  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22114b2e18SPierre-Alain TORET  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23114b2e18SPierre-Alain TORET  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24114b2e18SPierre-Alain TORET  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25114b2e18SPierre-Alain TORET  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26114b2e18SPierre-Alain TORET  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27114b2e18SPierre-Alain TORET  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28114b2e18SPierre-Alain TORET  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29114b2e18SPierre-Alain TORET  * POSSIBILITY OF SUCH DAMAGE.
3023be8282SSascha Wildner  *
312a07801cSSascha Wildner  * $FreeBSD: head/sys/dev/amdtemp/amdtemp.c 366136 2020-09-25 04:16:28Z cem $
32114b2e18SPierre-Alain TORET  */
33114b2e18SPierre-Alain TORET 
34114b2e18SPierre-Alain TORET /*
35114b2e18SPierre-Alain TORET  * Driver for the AMD CPU on-die thermal sensors.
36114b2e18SPierre-Alain TORET  * Initially based on the k8temp Linux driver.
37114b2e18SPierre-Alain TORET  */
38114b2e18SPierre-Alain TORET 
39114b2e18SPierre-Alain TORET #include <sys/param.h>
40114b2e18SPierre-Alain TORET #include <sys/bus.h>
41114b2e18SPierre-Alain TORET #include <sys/conf.h>
42114b2e18SPierre-Alain TORET #include <sys/kernel.h>
43114b2e18SPierre-Alain TORET #include <sys/module.h>
44114b2e18SPierre-Alain TORET #include <sys/sysctl.h>
45114b2e18SPierre-Alain TORET #include <sys/systm.h>
46d834e1dcSMatthew Dillon #include <sys/malloc.h>
47d834e1dcSMatthew Dillon #include <sys/sensors.h>
48114b2e18SPierre-Alain TORET 
49114b2e18SPierre-Alain TORET #include <machine/cpufunc.h>
50114b2e18SPierre-Alain TORET #include <machine/md_var.h>
51114b2e18SPierre-Alain TORET #include <machine/specialreg.h>
52114b2e18SPierre-Alain TORET 
53114b2e18SPierre-Alain TORET #include <bus/pci/pcivar.h>
54114b2e18SPierre-Alain TORET #include <bus/pci/pci_cfgreg.h>
55114b2e18SPierre-Alain TORET 
56114b2e18SPierre-Alain TORET #include <dev/powermng/amdsmn/amdsmn.h>
57114b2e18SPierre-Alain TORET 
58114b2e18SPierre-Alain TORET typedef enum {
59114b2e18SPierre-Alain TORET 	CORE0_SENSOR0,
60114b2e18SPierre-Alain TORET 	CORE0_SENSOR1,
61114b2e18SPierre-Alain TORET 	CORE1_SENSOR0,
62114b2e18SPierre-Alain TORET 	CORE1_SENSOR1,
63114b2e18SPierre-Alain TORET 	CORE0,
64114b2e18SPierre-Alain TORET 	CORE1,
65114b2e18SPierre-Alain TORET 	CCD1,
66114b2e18SPierre-Alain TORET 	CCD_BASE = CCD1,
67114b2e18SPierre-Alain TORET 	CCD2,
68114b2e18SPierre-Alain TORET 	CCD3,
69114b2e18SPierre-Alain TORET 	CCD4,
70114b2e18SPierre-Alain TORET 	CCD5,
71114b2e18SPierre-Alain TORET 	CCD6,
72114b2e18SPierre-Alain TORET 	CCD7,
73114b2e18SPierre-Alain TORET 	CCD8,
74*4970ada5SMatthew Dillon 	CCD9,
75*4970ada5SMatthew Dillon 	CCD10,
76*4970ada5SMatthew Dillon 	CCD11,
77*4970ada5SMatthew Dillon 	CCD12,
78d834e1dcSMatthew Dillon 	MAXSENSORS,
79d834e1dcSMatthew Dillon 
80*4970ada5SMatthew Dillon 	CCD_MAX = CCD12,
81114b2e18SPierre-Alain TORET 	NUM_CCDS = CCD_MAX - CCD_BASE + 1,
82114b2e18SPierre-Alain TORET } amdsensor_t;
83114b2e18SPierre-Alain TORET 
84114b2e18SPierre-Alain TORET struct amdtemp_softc {
85114b2e18SPierre-Alain TORET 	int		sc_ncores;
86114b2e18SPierre-Alain TORET 	int		sc_ntemps;
87114b2e18SPierre-Alain TORET 	int		sc_flags;
88d834e1dcSMatthew Dillon 	int		sc_ccd_display;
89114b2e18SPierre-Alain TORET #define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
90114b2e18SPierre-Alain TORET #define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
91114b2e18SPierre-Alain TORET #define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
92114b2e18SPierre-Alain TORET 	int32_t		sc_offset;
93*4970ada5SMatthew Dillon 	int32_t		sc_ccd_offset;
94114b2e18SPierre-Alain TORET 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
95114b2e18SPierre-Alain TORET 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
96114b2e18SPierre-Alain TORET 	struct intr_config_hook sc_ich;
97114b2e18SPierre-Alain TORET 	device_t	sc_smn;
98d834e1dcSMatthew Dillon 	uint32_t	sc_probed_regmask;
99d834e1dcSMatthew Dillon 
100d834e1dcSMatthew Dillon 	/*
101d834e1dcSMatthew Dillon 	 * NOTE: We put common sensors like the CCDs on cpu0.  Remaining
102d834e1dcSMatthew Dillon 	 *	 cores are only applicable if ntemps == 2 (with no CCDs).
103d834e1dcSMatthew Dillon 	 *	 When ntemps == 1 the temp sensors are CCD-based and shared.
104d834e1dcSMatthew Dillon 	 */
105d834e1dcSMatthew Dillon 	struct sensorcpu {
106d834e1dcSMatthew Dillon 		device_t dev;
107d834e1dcSMatthew Dillon 		struct amdtemp_softc *sc;
108d834e1dcSMatthew Dillon 		struct ksensordev    sensordev;
109d834e1dcSMatthew Dillon 		struct ksensor	     *sensors;
110d834e1dcSMatthew Dillon 		struct sensor_task   *senstask;
111d834e1dcSMatthew Dillon 		uint32_t regmask;
112d834e1dcSMatthew Dillon 	} *sc_sensorcpus;
113114b2e18SPierre-Alain TORET };
114114b2e18SPierre-Alain TORET 
115114b2e18SPierre-Alain TORET /*
116114b2e18SPierre-Alain TORET  * N.B. The numbers in macro names below are significant and represent CPU
117114b2e18SPierre-Alain TORET  * family and model numbers.  Do not make up fictitious family or model numbers
118114b2e18SPierre-Alain TORET  * when adding support for new devices.
119114b2e18SPierre-Alain TORET  */
120114b2e18SPierre-Alain TORET #define	VENDORID_AMD			0x1022
121*4970ada5SMatthew Dillon 
122114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC0F		0x1103
123114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC10		0x1203
124114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC11		0x1303
125114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC14		0x1703
126114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC15		0x1603
127114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC15_M10H	0x1403
128114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC15_M30H	0x141d
129114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC15_M60H_ROOT	0x1576
130114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC16		0x1533
131114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_MISC16_M30H	0x1583
132114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_HOSTB17H_ROOT	0x1450
133114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_HOSTB17H_M10H_ROOT	0x15d0
134114b2e18SPierre-Alain TORET #define	DEVICEID_AMD_HOSTB17H_M30H_ROOT	0x1480	/* Also M70h. */
1352a07801cSSascha Wildner #define	DEVICEID_AMD_HOSTB17H_M60H_ROOT	0x1630
136*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB17H_M70H_ROOT 0x1443
137*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB17H_MA0H_ROOT 0x1727
138*4970ada5SMatthew Dillon 
139*4970ada5SMatthew Dillon #if 0
140*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M10H_ROOT 0x14b0
141*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M40H_ROOT 0x167c
142*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M50H_ROOT 0x166d
143*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M60H_ROOT 0x14e3
144*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M70H_ROOT 0x14f3
145*4970ada5SMatthew Dillon #endif
146*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M10H_ROOT 0x14a4
147*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M60H_ROOT 0x14d8
148*4970ada5SMatthew Dillon #define DEVICEID_AMD_HOSTB19H_M70H_ROOT 0x14e8
149*4970ada5SMatthew Dillon 
150114b2e18SPierre-Alain TORET 
151114b2e18SPierre-Alain TORET static const struct amdtemp_product {
152114b2e18SPierre-Alain TORET 	uint16_t	amdtemp_vendorid;
153114b2e18SPierre-Alain TORET 	uint16_t	amdtemp_deviceid;
154114b2e18SPierre-Alain TORET 	/*
155114b2e18SPierre-Alain TORET 	 * 0xFC register is only valid on the D18F3 PCI device; SMN temp
156114b2e18SPierre-Alain TORET 	 * drivers do not attach to that device.
157114b2e18SPierre-Alain TORET 	 */
158114b2e18SPierre-Alain TORET 	bool		amdtemp_has_cpuid;
159114b2e18SPierre-Alain TORET } amdtemp_products[] = {
160114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F, true },
161114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10, true },
162114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11, true },
163114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC14, true },
164114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15, true },
165114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M10H, true },
166114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M30H, true },
167114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC15_M60H_ROOT, false },
168114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16, true },
169114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H, true },
170*4970ada5SMatthew Dillon 
171114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_ROOT, false },
172114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M10H_ROOT, false },
173114b2e18SPierre-Alain TORET 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M30H_ROOT, false },
1742a07801cSSascha Wildner 	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H_M60H_ROOT, false },
175*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M70H_ROOT, false },
176*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB17H_MA0H_ROOT, false },
177*4970ada5SMatthew Dillon 
178*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M10H_ROOT, false },
179*4970ada5SMatthew Dillon #if 0
180*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M40H_ROOT, false },
181*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M50H_ROOT, false },
182*4970ada5SMatthew Dillon #endif
183*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M60H_ROOT, false },
184*4970ada5SMatthew Dillon 	{ VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M70H_ROOT, false },
185114b2e18SPierre-Alain TORET };
186114b2e18SPierre-Alain TORET 
187114b2e18SPierre-Alain TORET /*
188114b2e18SPierre-Alain TORET  * Reported Temperature Control Register, family 0Fh-15h (some models), 16h.
189114b2e18SPierre-Alain TORET  */
190114b2e18SPierre-Alain TORET #define	AMDTEMP_REPTMP_CTRL	0xa4
191114b2e18SPierre-Alain TORET 
192114b2e18SPierre-Alain TORET #define	AMDTEMP_REPTMP10H_CURTMP_MASK	0x7ff
193114b2e18SPierre-Alain TORET #define	AMDTEMP_REPTMP10H_CURTMP_SHIFT	21
194114b2e18SPierre-Alain TORET #define	AMDTEMP_REPTMP10H_TJSEL_MASK	0x3
195114b2e18SPierre-Alain TORET #define	AMDTEMP_REPTMP10H_TJSEL_SHIFT	16
196114b2e18SPierre-Alain TORET 
197114b2e18SPierre-Alain TORET /*
198114b2e18SPierre-Alain TORET  * Reported Temperature, Family 15h, M60+
199114b2e18SPierre-Alain TORET  *
200114b2e18SPierre-Alain TORET  * Same register bit definitions as other Family 15h CPUs, but access is
201114b2e18SPierre-Alain TORET  * indirect via SMN, like Family 17h.
202114b2e18SPierre-Alain TORET  */
203114b2e18SPierre-Alain TORET #define	AMDTEMP_15H_M60H_REPTMP_CTRL	0xd8200ca4
204114b2e18SPierre-Alain TORET 
205114b2e18SPierre-Alain TORET /*
206114b2e18SPierre-Alain TORET  * Reported Temperature, Family 17h
207114b2e18SPierre-Alain TORET  *
208114b2e18SPierre-Alain TORET  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
209114b2e18SPierre-Alain TORET  * provide the current temp.  bit 19, when clear, means the temp is reported in
210114b2e18SPierre-Alain TORET  * a range 0.."225C" (probable typo for 255C), and when set changes the range
211114b2e18SPierre-Alain TORET  * to -49..206C.
212*4970ada5SMatthew Dillon  *
213*4970ada5SMatthew Dillon  * Family 17H and 19H
214114b2e18SPierre-Alain TORET  */
215114b2e18SPierre-Alain TORET #define	AMDTEMP_17H_CUR_TMP		0x59800
216114b2e18SPierre-Alain TORET #define	AMDTEMP_17H_CUR_TMP_RANGE_SEL	(1u << 19)
217114b2e18SPierre-Alain TORET /*
218*4970ada5SMatthew Dillon  *
219114b2e18SPierre-Alain TORET  */
220114b2e18SPierre-Alain TORET #define	AMDTEMP_17H_CCD_TMP_VALID	(1u << 11)
221114b2e18SPierre-Alain TORET 
222114b2e18SPierre-Alain TORET /*
223114b2e18SPierre-Alain TORET  * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius).
224114b2e18SPierre-Alain TORET  */
225114b2e18SPierre-Alain TORET #define	AMDTEMP_CURTMP_RANGE_ADJUST	490
226114b2e18SPierre-Alain TORET 
227114b2e18SPierre-Alain TORET /*
228114b2e18SPierre-Alain TORET  * Thermaltrip Status Register (Family 0Fh only)
229114b2e18SPierre-Alain TORET  */
230114b2e18SPierre-Alain TORET #define	AMDTEMP_THERMTP_STAT	0xe4
231114b2e18SPierre-Alain TORET #define	AMDTEMP_TTSR_SELCORE	0x04
232114b2e18SPierre-Alain TORET #define	AMDTEMP_TTSR_SELSENSOR	0x40
233114b2e18SPierre-Alain TORET 
234114b2e18SPierre-Alain TORET /*
235114b2e18SPierre-Alain TORET  * DRAM Configuration High Register
236114b2e18SPierre-Alain TORET  */
237114b2e18SPierre-Alain TORET #define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
238114b2e18SPierre-Alain TORET #define	AMDTEMP_DRAM_MODE_DDR3	0x0100
239114b2e18SPierre-Alain TORET 
240114b2e18SPierre-Alain TORET /*
241114b2e18SPierre-Alain TORET  * CPU Family/Model Register
242114b2e18SPierre-Alain TORET  */
243114b2e18SPierre-Alain TORET #define	AMDTEMP_CPUID		0xfc
244114b2e18SPierre-Alain TORET 
245114b2e18SPierre-Alain TORET /*
246114b2e18SPierre-Alain TORET  * Device methods.
247114b2e18SPierre-Alain TORET  */
248114b2e18SPierre-Alain TORET static void 	amdtemp_identify(driver_t *driver, device_t parent);
249114b2e18SPierre-Alain TORET static int	amdtemp_probe(device_t dev);
250114b2e18SPierre-Alain TORET static int	amdtemp_attach(device_t dev);
251114b2e18SPierre-Alain TORET static void	amdtemp_intrhook(void *arg);
252114b2e18SPierre-Alain TORET static int	amdtemp_detach(device_t dev);
253114b2e18SPierre-Alain TORET static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
254114b2e18SPierre-Alain TORET static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
255114b2e18SPierre-Alain TORET static int32_t	amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor);
256d834e1dcSMatthew Dillon static int32_t	amdtemp_gettemp17to19h(device_t dev, amdsensor_t sensor);
257114b2e18SPierre-Alain TORET static void	amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model);
258d834e1dcSMatthew Dillon static void	amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model);
259114b2e18SPierre-Alain TORET static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
260d834e1dcSMatthew Dillon static void	amdtemp_sensor_task(void *);
261114b2e18SPierre-Alain TORET 
262114b2e18SPierre-Alain TORET static device_method_t amdtemp_methods[] = {
263114b2e18SPierre-Alain TORET 	/* Device interface */
264114b2e18SPierre-Alain TORET 	DEVMETHOD(device_identify,	amdtemp_identify),
265114b2e18SPierre-Alain TORET 	DEVMETHOD(device_probe,		amdtemp_probe),
266114b2e18SPierre-Alain TORET 	DEVMETHOD(device_attach,	amdtemp_attach),
267114b2e18SPierre-Alain TORET 	DEVMETHOD(device_detach,	amdtemp_detach),
268114b2e18SPierre-Alain TORET 
269114b2e18SPierre-Alain TORET 	DEVMETHOD_END
270114b2e18SPierre-Alain TORET };
271114b2e18SPierre-Alain TORET 
272114b2e18SPierre-Alain TORET static driver_t amdtemp_driver = {
273114b2e18SPierre-Alain TORET 	"amdtemp",
274114b2e18SPierre-Alain TORET 	amdtemp_methods,
275114b2e18SPierre-Alain TORET 	sizeof(struct amdtemp_softc),
276114b2e18SPierre-Alain TORET };
277114b2e18SPierre-Alain TORET 
278114b2e18SPierre-Alain TORET static devclass_t amdtemp_devclass;
279*4970ada5SMatthew Dillon DRIVER_MODULE_ORDERED(amdtemp, hostb, amdtemp_driver,
280*4970ada5SMatthew Dillon 		      &amdtemp_devclass, NULL, NULL, SI_ORDER_LATER);
281114b2e18SPierre-Alain TORET MODULE_VERSION(amdtemp, 1);
282114b2e18SPierre-Alain TORET MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
283114b2e18SPierre-Alain TORET #if !defined(__DragonFly__)
284114b2e18SPierre-Alain TORET MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products,
285114b2e18SPierre-Alain TORET     nitems(amdtemp_products));
286114b2e18SPierre-Alain TORET #endif
287114b2e18SPierre-Alain TORET 
288114b2e18SPierre-Alain TORET static bool
amdtemp_match(device_t dev,const struct amdtemp_product ** product_out)289114b2e18SPierre-Alain TORET amdtemp_match(device_t dev, const struct amdtemp_product **product_out)
290114b2e18SPierre-Alain TORET {
291114b2e18SPierre-Alain TORET 	int i;
292114b2e18SPierre-Alain TORET 	uint16_t vendor, devid;
293114b2e18SPierre-Alain TORET 
294114b2e18SPierre-Alain TORET 	vendor = pci_get_vendor(dev);
295114b2e18SPierre-Alain TORET 	devid = pci_get_device(dev);
296114b2e18SPierre-Alain TORET 
297114b2e18SPierre-Alain TORET 	for (i = 0; i < nitems(amdtemp_products); i++) {
298114b2e18SPierre-Alain TORET 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
299114b2e18SPierre-Alain TORET 		    devid == amdtemp_products[i].amdtemp_deviceid) {
300114b2e18SPierre-Alain TORET 			if (product_out != NULL)
301114b2e18SPierre-Alain TORET 				*product_out = &amdtemp_products[i];
302114b2e18SPierre-Alain TORET 			return (true);
303114b2e18SPierre-Alain TORET 		}
304114b2e18SPierre-Alain TORET 	}
305114b2e18SPierre-Alain TORET 	return (false);
306114b2e18SPierre-Alain TORET }
307114b2e18SPierre-Alain TORET 
308114b2e18SPierre-Alain TORET static void
amdtemp_identify(driver_t * driver,device_t parent)309114b2e18SPierre-Alain TORET amdtemp_identify(driver_t *driver, device_t parent)
310114b2e18SPierre-Alain TORET {
311114b2e18SPierre-Alain TORET 	device_t child;
312114b2e18SPierre-Alain TORET 
313114b2e18SPierre-Alain TORET 	/* Make sure we're not being doubly invoked. */
314114b2e18SPierre-Alain TORET 	if (device_find_child(parent, "amdtemp", -1) != NULL)
315114b2e18SPierre-Alain TORET 		return;
316114b2e18SPierre-Alain TORET 
317114b2e18SPierre-Alain TORET 	if (amdtemp_match(parent, NULL)) {
318114b2e18SPierre-Alain TORET 		child = device_add_child(parent, "amdtemp", -1);
319114b2e18SPierre-Alain TORET 		if (child == NULL)
320114b2e18SPierre-Alain TORET 			device_printf(parent, "add amdtemp child failed\n");
321114b2e18SPierre-Alain TORET 	}
322114b2e18SPierre-Alain TORET }
323114b2e18SPierre-Alain TORET 
324114b2e18SPierre-Alain TORET static int
amdtemp_probe(device_t dev)325114b2e18SPierre-Alain TORET amdtemp_probe(device_t dev)
326114b2e18SPierre-Alain TORET {
327114b2e18SPierre-Alain TORET 	uint32_t family, model;
328114b2e18SPierre-Alain TORET 
329114b2e18SPierre-Alain TORET 	if (resource_disabled("amdtemp", 0))
330114b2e18SPierre-Alain TORET 		return (ENXIO);
331114b2e18SPierre-Alain TORET 	if (!amdtemp_match(device_get_parent(dev), NULL))
332114b2e18SPierre-Alain TORET 		return (ENXIO);
333114b2e18SPierre-Alain TORET 
334114b2e18SPierre-Alain TORET 	family = CPUID_TO_FAMILY(cpu_id);
335114b2e18SPierre-Alain TORET 	model = CPUID_TO_MODEL(cpu_id);
336114b2e18SPierre-Alain TORET 
337114b2e18SPierre-Alain TORET 	switch (family) {
338114b2e18SPierre-Alain TORET 	case 0x0f:
339114b2e18SPierre-Alain TORET 		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
340114b2e18SPierre-Alain TORET 		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
341114b2e18SPierre-Alain TORET 			return (ENXIO);
342114b2e18SPierre-Alain TORET 		break;
343114b2e18SPierre-Alain TORET 	case 0x10:
344114b2e18SPierre-Alain TORET 	case 0x11:
345114b2e18SPierre-Alain TORET 	case 0x12:
346114b2e18SPierre-Alain TORET 	case 0x14:
347114b2e18SPierre-Alain TORET 	case 0x15:
348114b2e18SPierre-Alain TORET 	case 0x16:
349114b2e18SPierre-Alain TORET 	case 0x17:
350d834e1dcSMatthew Dillon 	case 0x19:
351114b2e18SPierre-Alain TORET 		break;
352114b2e18SPierre-Alain TORET 	default:
353114b2e18SPierre-Alain TORET 		return (ENXIO);
354114b2e18SPierre-Alain TORET 	}
355114b2e18SPierre-Alain TORET 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
356114b2e18SPierre-Alain TORET 
357114b2e18SPierre-Alain TORET 	return (BUS_PROBE_GENERIC);
358114b2e18SPierre-Alain TORET }
359114b2e18SPierre-Alain TORET 
360114b2e18SPierre-Alain TORET static int
amdtemp_attach(device_t dev)361114b2e18SPierre-Alain TORET amdtemp_attach(device_t dev)
362114b2e18SPierre-Alain TORET {
363114b2e18SPierre-Alain TORET 	char tn[32];
364114b2e18SPierre-Alain TORET 	u_int regs[4];
365114b2e18SPierre-Alain TORET 	const struct amdtemp_product *product;
366114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc;
367114b2e18SPierre-Alain TORET 	struct sysctl_ctx_list *sysctlctx;
368114b2e18SPierre-Alain TORET 	struct sysctl_oid *sysctlnode;
369114b2e18SPierre-Alain TORET 	uint32_t cpuid, family, model;
370114b2e18SPierre-Alain TORET 	u_int bid;
371114b2e18SPierre-Alain TORET 	int erratum319, unit;
372114b2e18SPierre-Alain TORET 	bool needsmn;
373114b2e18SPierre-Alain TORET 
374114b2e18SPierre-Alain TORET 	sc = device_get_softc(dev);
375114b2e18SPierre-Alain TORET 	erratum319 = 0;
376114b2e18SPierre-Alain TORET 	needsmn = false;
377114b2e18SPierre-Alain TORET 
378114b2e18SPierre-Alain TORET 	if (!amdtemp_match(device_get_parent(dev), &product))
379114b2e18SPierre-Alain TORET 		return (ENXIO);
380114b2e18SPierre-Alain TORET 
381114b2e18SPierre-Alain TORET 	cpuid = cpu_id;
382114b2e18SPierre-Alain TORET 	family = CPUID_TO_FAMILY(cpuid);
383114b2e18SPierre-Alain TORET 	model = CPUID_TO_MODEL(cpuid);
384114b2e18SPierre-Alain TORET 
385114b2e18SPierre-Alain TORET 	/*
386114b2e18SPierre-Alain TORET 	 * This checks for the byzantine condition of running a heterogenous
387114b2e18SPierre-Alain TORET 	 * revision multi-socket system where the attach thread is potentially
388114b2e18SPierre-Alain TORET 	 * probing a remote socket's PCI device.
389114b2e18SPierre-Alain TORET 	 *
390114b2e18SPierre-Alain TORET 	 * Currently, such scenarios are unsupported on models using the SMN
391114b2e18SPierre-Alain TORET 	 * (because on those models, amdtemp(4) attaches to a different PCI
392114b2e18SPierre-Alain TORET 	 * device than the one that contains AMDTEMP_CPUID).
393114b2e18SPierre-Alain TORET 	 *
394114b2e18SPierre-Alain TORET 	 * The ancient 0x0F family of devices only supports this register from
395114b2e18SPierre-Alain TORET 	 * models 40h+.
396114b2e18SPierre-Alain TORET 	 */
397114b2e18SPierre-Alain TORET 	if (product->amdtemp_has_cpuid && (family > 0x0f ||
398114b2e18SPierre-Alain TORET 	    (family == 0x0f && model >= 0x40))) {
399114b2e18SPierre-Alain TORET 		cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID,
400114b2e18SPierre-Alain TORET 		    4);
401114b2e18SPierre-Alain TORET 		family = CPUID_TO_FAMILY(cpuid);
402114b2e18SPierre-Alain TORET 		model = CPUID_TO_MODEL(cpuid);
403114b2e18SPierre-Alain TORET 	}
404114b2e18SPierre-Alain TORET 
405114b2e18SPierre-Alain TORET 	switch (family) {
406114b2e18SPierre-Alain TORET 	case 0x0f:
407114b2e18SPierre-Alain TORET 		/*
408114b2e18SPierre-Alain TORET 		 * Thermaltrip Status Register
409114b2e18SPierre-Alain TORET 		 *
410114b2e18SPierre-Alain TORET 		 * - ThermSenseCoreSel
411114b2e18SPierre-Alain TORET 		 *
412114b2e18SPierre-Alain TORET 		 * Revision F & G:	0 - Core1, 1 - Core0
413114b2e18SPierre-Alain TORET 		 * Other:		0 - Core0, 1 - Core1
414114b2e18SPierre-Alain TORET 		 *
415114b2e18SPierre-Alain TORET 		 * - CurTmp
416114b2e18SPierre-Alain TORET 		 *
417114b2e18SPierre-Alain TORET 		 * Revision G:		bits 23-14
418114b2e18SPierre-Alain TORET 		 * Other:		bits 23-16
419114b2e18SPierre-Alain TORET 		 *
420114b2e18SPierre-Alain TORET 		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
421114b2e18SPierre-Alain TORET 		 * ThermSenseCoreSel bits were introduced in Revision F
422114b2e18SPierre-Alain TORET 		 * but CurTmp seems working fine as early as Revision C.
423114b2e18SPierre-Alain TORET 		 * However, it is not clear whether ThermSenseSel and/or
424114b2e18SPierre-Alain TORET 		 * ThermSenseCoreSel work in undocumented cases as well.
425114b2e18SPierre-Alain TORET 		 * In fact, the Linux driver suggests it may not work but
426114b2e18SPierre-Alain TORET 		 * we just assume it does until we find otherwise.
427114b2e18SPierre-Alain TORET 		 *
428114b2e18SPierre-Alain TORET 		 * XXX According to Linux, CurTmp starts at -28C on
429114b2e18SPierre-Alain TORET 		 * Socket AM2 Revision G processors, which is not
430114b2e18SPierre-Alain TORET 		 * documented anywhere.
431114b2e18SPierre-Alain TORET 		 */
432114b2e18SPierre-Alain TORET 		if (model >= 0x40)
433114b2e18SPierre-Alain TORET 			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
434114b2e18SPierre-Alain TORET 		if (model >= 0x60 && model != 0xc1) {
435114b2e18SPierre-Alain TORET 			do_cpuid(0x80000001, regs);
436114b2e18SPierre-Alain TORET 			bid = (regs[1] >> 9) & 0x1f;
437114b2e18SPierre-Alain TORET 			switch (model) {
438114b2e18SPierre-Alain TORET 			case 0x68: /* Socket S1g1 */
439114b2e18SPierre-Alain TORET 			case 0x6c:
440114b2e18SPierre-Alain TORET 			case 0x7c:
441114b2e18SPierre-Alain TORET 				break;
442114b2e18SPierre-Alain TORET 			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
443114b2e18SPierre-Alain TORET 				if (bid != 0x0b && bid != 0x0c)
444114b2e18SPierre-Alain TORET 					sc->sc_flags |=
445114b2e18SPierre-Alain TORET 					    AMDTEMP_FLAG_ALT_OFFSET;
446114b2e18SPierre-Alain TORET 				break;
447114b2e18SPierre-Alain TORET 			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
448114b2e18SPierre-Alain TORET 			case 0x7f:
449114b2e18SPierre-Alain TORET 				if (bid != 0x07 && bid != 0x09 &&
450114b2e18SPierre-Alain TORET 				    bid != 0x0c)
451114b2e18SPierre-Alain TORET 					sc->sc_flags |=
452114b2e18SPierre-Alain TORET 					    AMDTEMP_FLAG_ALT_OFFSET;
453114b2e18SPierre-Alain TORET 				break;
454114b2e18SPierre-Alain TORET 			default:
455114b2e18SPierre-Alain TORET 				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
456114b2e18SPierre-Alain TORET 			}
457114b2e18SPierre-Alain TORET 			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
458114b2e18SPierre-Alain TORET 		}
459114b2e18SPierre-Alain TORET 
460114b2e18SPierre-Alain TORET 		/*
461114b2e18SPierre-Alain TORET 		 * There are two sensors per core.
462114b2e18SPierre-Alain TORET 		 */
463114b2e18SPierre-Alain TORET 		sc->sc_ntemps = 2;
464d834e1dcSMatthew Dillon 		sc->sc_ccd_display = 0;
465114b2e18SPierre-Alain TORET 
466114b2e18SPierre-Alain TORET 		sc->sc_gettemp = amdtemp_gettemp0f;
467114b2e18SPierre-Alain TORET 		break;
468114b2e18SPierre-Alain TORET 	case 0x10:
469114b2e18SPierre-Alain TORET 		/*
470114b2e18SPierre-Alain TORET 		 * Erratum 319 Inaccurate Temperature Measurement
471114b2e18SPierre-Alain TORET 		 *
472114b2e18SPierre-Alain TORET 		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
473114b2e18SPierre-Alain TORET 		 */
474114b2e18SPierre-Alain TORET 		do_cpuid(0x80000001, regs);
475114b2e18SPierre-Alain TORET 		switch ((regs[1] >> 28) & 0xf) {
476114b2e18SPierre-Alain TORET 		case 0:	/* Socket F */
477114b2e18SPierre-Alain TORET 			erratum319 = 1;
478114b2e18SPierre-Alain TORET 			break;
479114b2e18SPierre-Alain TORET 		case 1:	/* Socket AM2+ or AM3 */
480114b2e18SPierre-Alain TORET 			if ((pci_cfgregread(pci_get_bus(dev),
481114b2e18SPierre-Alain TORET 			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
482114b2e18SPierre-Alain TORET 			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
483114b2e18SPierre-Alain TORET 			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
484114b2e18SPierre-Alain TORET 				break;
485114b2e18SPierre-Alain TORET 			/* XXX 00100F42h (RB-C2) exists in both formats. */
486114b2e18SPierre-Alain TORET 			erratum319 = 1;
487114b2e18SPierre-Alain TORET 			break;
488114b2e18SPierre-Alain TORET 		}
489114b2e18SPierre-Alain TORET 		/* FALLTHROUGH */
490114b2e18SPierre-Alain TORET 	case 0x11:
491114b2e18SPierre-Alain TORET 	case 0x12:
492114b2e18SPierre-Alain TORET 	case 0x14:
493114b2e18SPierre-Alain TORET 	case 0x15:
494114b2e18SPierre-Alain TORET 	case 0x16:
495114b2e18SPierre-Alain TORET 		sc->sc_ntemps = 1;
496d834e1dcSMatthew Dillon 		sc->sc_ccd_display = 1;
497114b2e18SPierre-Alain TORET 		/*
498114b2e18SPierre-Alain TORET 		 * Some later (60h+) models of family 15h use a similar SMN
499114b2e18SPierre-Alain TORET 		 * network as family 17h.  (However, the register index differs
500114b2e18SPierre-Alain TORET 		 * from 17h and the decoding matches other 10h-15h models,
501114b2e18SPierre-Alain TORET 		 * which differ from 17h.)
502114b2e18SPierre-Alain TORET 		 */
503114b2e18SPierre-Alain TORET 		if (family == 0x15 && model >= 0x60) {
504114b2e18SPierre-Alain TORET 			sc->sc_gettemp = amdtemp_gettemp15hm60h;
505114b2e18SPierre-Alain TORET 			needsmn = true;
506114b2e18SPierre-Alain TORET 		} else
507114b2e18SPierre-Alain TORET 			sc->sc_gettemp = amdtemp_gettemp;
508114b2e18SPierre-Alain TORET 		break;
509114b2e18SPierre-Alain TORET 	case 0x17:
510d834e1dcSMatthew Dillon 	case 0x19:
511114b2e18SPierre-Alain TORET 		sc->sc_ntemps = 1;
512d834e1dcSMatthew Dillon 		sc->sc_ccd_display = 1;
513d834e1dcSMatthew Dillon 		sc->sc_gettemp = amdtemp_gettemp17to19h;
514*4970ada5SMatthew Dillon 		switch(model) {
515*4970ada5SMatthew Dillon 		case 0x10 ... 0x1f:
516*4970ada5SMatthew Dillon 		case 0xa0 ... 0xaf:
517*4970ada5SMatthew Dillon 		case 0x40 ... 0x4f:
518*4970ada5SMatthew Dillon 			sc->sc_ccd_offset = 0x300;
519*4970ada5SMatthew Dillon 			break;
520*4970ada5SMatthew Dillon 		case 0x60 ... 0x6f:
521*4970ada5SMatthew Dillon 		case 0x70 ... 0x7f:
522*4970ada5SMatthew Dillon 			sc->sc_ccd_offset = 0x308;
523*4970ada5SMatthew Dillon 			break;
524*4970ada5SMatthew Dillon 		default:
525*4970ada5SMatthew Dillon 			sc->sc_ccd_offset = 0x154;
526*4970ada5SMatthew Dillon 			break;
527*4970ada5SMatthew Dillon 		}
528114b2e18SPierre-Alain TORET 		needsmn = true;
529*4970ada5SMatthew Dillon 		device_printf(dev, "sc_ccd_offset = %08x\n", sc->sc_ccd_offset);
530114b2e18SPierre-Alain TORET 		break;
531114b2e18SPierre-Alain TORET 	default:
532114b2e18SPierre-Alain TORET 		device_printf(dev, "Bogus family 0x%x\n", family);
533114b2e18SPierre-Alain TORET 		return (ENXIO);
534114b2e18SPierre-Alain TORET 	}
535114b2e18SPierre-Alain TORET 
536114b2e18SPierre-Alain TORET 	if (needsmn) {
537114b2e18SPierre-Alain TORET 		sc->sc_smn = device_find_child(
538114b2e18SPierre-Alain TORET 		    device_get_parent(dev), "amdsmn", -1);
539114b2e18SPierre-Alain TORET 		if (sc->sc_smn == NULL) {
540114b2e18SPierre-Alain TORET 			device_printf(dev, "No SMN device found\n");
541114b2e18SPierre-Alain TORET 			return (ENXIO);
542114b2e18SPierre-Alain TORET 		}
543114b2e18SPierre-Alain TORET 	}
544114b2e18SPierre-Alain TORET 
545d834e1dcSMatthew Dillon 	/*
546d834e1dcSMatthew Dillon 	 * Find number of cores per package.  XXX this does not work
547d834e1dcSMatthew Dillon 	 * properly, it appears to be calculating the total number of cores.
548d834e1dcSMatthew Dillon 	 */
549d834e1dcSMatthew Dillon 
550114b2e18SPierre-Alain TORET 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
551114b2e18SPierre-Alain TORET 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
552114b2e18SPierre-Alain TORET 	if (sc->sc_ncores > MAXCPU)
553114b2e18SPierre-Alain TORET 		return (ENXIO);
554114b2e18SPierre-Alain TORET 
555114b2e18SPierre-Alain TORET 	if (erratum319)
556114b2e18SPierre-Alain TORET 		device_printf(dev,
557114b2e18SPierre-Alain TORET 		    "Erratum 319: temperature measurement may be inaccurate\n");
558114b2e18SPierre-Alain TORET 	if (bootverbose)
559114b2e18SPierre-Alain TORET 		device_printf(dev, "Found %d cores and %d sensors.\n",
560114b2e18SPierre-Alain TORET 		    sc->sc_ncores,
561114b2e18SPierre-Alain TORET 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
562114b2e18SPierre-Alain TORET 
563114b2e18SPierre-Alain TORET 	/*
564114b2e18SPierre-Alain TORET 	 * dev.amdtemp.N tree.
565114b2e18SPierre-Alain TORET 	 */
566114b2e18SPierre-Alain TORET 	unit = device_get_unit(dev);
567114b2e18SPierre-Alain TORET 	ksnprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
568114b2e18SPierre-Alain TORET 	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
569114b2e18SPierre-Alain TORET 
570114b2e18SPierre-Alain TORET 	sysctlctx = device_get_sysctl_ctx(dev);
571114b2e18SPierre-Alain TORET 	SYSCTL_ADD_INT(sysctlctx,
572114b2e18SPierre-Alain TORET 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
573114b2e18SPierre-Alain TORET 	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
574114b2e18SPierre-Alain TORET 	    "Temperature sensor offset");
575114b2e18SPierre-Alain TORET 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
576114b2e18SPierre-Alain TORET 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
577114b2e18SPierre-Alain TORET 	    "core0", CTLFLAG_RD, 0, "Core 0");
578114b2e18SPierre-Alain TORET 
579114b2e18SPierre-Alain TORET 	SYSCTL_ADD_PROC(sysctlctx,
580114b2e18SPierre-Alain TORET 	    SYSCTL_CHILDREN(sysctlnode),
581114b2e18SPierre-Alain TORET 	    OID_AUTO, "sensor0",
582114b2e18SPierre-Alain TORET 	    CTLTYPE_INT | CTLFLAG_RD,
583114b2e18SPierre-Alain TORET 	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
584114b2e18SPierre-Alain TORET 	    "Core 0 / Sensor 0 temperature");
585114b2e18SPierre-Alain TORET 
586d834e1dcSMatthew Dillon 	sc->sc_probed_regmask |= 1U << CORE0_SENSOR0;
587d834e1dcSMatthew Dillon 
588114b2e18SPierre-Alain TORET 	if (family == 0x17)
589114b2e18SPierre-Alain TORET 		amdtemp_probe_ccd_sensors17h(dev, model);
590d834e1dcSMatthew Dillon 	else if (family == 0x19)
591d834e1dcSMatthew Dillon 		amdtemp_probe_ccd_sensors19h(dev, model);
592114b2e18SPierre-Alain TORET 	else if (sc->sc_ntemps > 1) {
593114b2e18SPierre-Alain TORET 		SYSCTL_ADD_PROC(sysctlctx,
594114b2e18SPierre-Alain TORET 		    SYSCTL_CHILDREN(sysctlnode),
595114b2e18SPierre-Alain TORET 		    OID_AUTO, "sensor1",
596114b2e18SPierre-Alain TORET 		    CTLTYPE_INT | CTLFLAG_RD,
597114b2e18SPierre-Alain TORET 		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
598114b2e18SPierre-Alain TORET 		    "Core 0 / Sensor 1 temperature");
599114b2e18SPierre-Alain TORET 
600d834e1dcSMatthew Dillon 		sc->sc_probed_regmask |= 1U << CORE0_SENSOR1;
601d834e1dcSMatthew Dillon 
602114b2e18SPierre-Alain TORET 		if (sc->sc_ncores > 1) {
603114b2e18SPierre-Alain TORET 			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
604114b2e18SPierre-Alain TORET 			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
605114b2e18SPierre-Alain TORET 			    OID_AUTO, "core1", CTLFLAG_RD,
606114b2e18SPierre-Alain TORET 			    0, "Core 1");
607114b2e18SPierre-Alain TORET 
608114b2e18SPierre-Alain TORET 			SYSCTL_ADD_PROC(sysctlctx,
609114b2e18SPierre-Alain TORET 			    SYSCTL_CHILDREN(sysctlnode),
610114b2e18SPierre-Alain TORET 			    OID_AUTO, "sensor0",
611114b2e18SPierre-Alain TORET 			    CTLTYPE_INT | CTLFLAG_RD,
612114b2e18SPierre-Alain TORET 			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
613114b2e18SPierre-Alain TORET 			    "Core 1 / Sensor 0 temperature");
614114b2e18SPierre-Alain TORET 
615114b2e18SPierre-Alain TORET 			SYSCTL_ADD_PROC(sysctlctx,
616114b2e18SPierre-Alain TORET 			    SYSCTL_CHILDREN(sysctlnode),
617114b2e18SPierre-Alain TORET 			    OID_AUTO, "sensor1",
618114b2e18SPierre-Alain TORET 			    CTLTYPE_INT | CTLFLAG_RD,
619114b2e18SPierre-Alain TORET 			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
620114b2e18SPierre-Alain TORET 			    "Core 1 / Sensor 1 temperature");
621d834e1dcSMatthew Dillon 
622d834e1dcSMatthew Dillon 			sc->sc_probed_regmask |= 1U << CORE1_SENSOR0;
623d834e1dcSMatthew Dillon 			sc->sc_probed_regmask |= 1U << CORE1_SENSOR1;
624114b2e18SPierre-Alain TORET 		}
625114b2e18SPierre-Alain TORET 	}
626114b2e18SPierre-Alain TORET 
627114b2e18SPierre-Alain TORET 	/*
628114b2e18SPierre-Alain TORET 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
629114b2e18SPierre-Alain TORET 	 * This is needed because the cpu driver may be loaded late on boot,
630114b2e18SPierre-Alain TORET 	 * after us.
631114b2e18SPierre-Alain TORET 	 */
632114b2e18SPierre-Alain TORET 	amdtemp_intrhook(dev);
633114b2e18SPierre-Alain TORET 	sc->sc_ich.ich_func = amdtemp_intrhook;
634114b2e18SPierre-Alain TORET 	sc->sc_ich.ich_arg = dev;
635114b2e18SPierre-Alain TORET 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
636114b2e18SPierre-Alain TORET 		device_printf(dev, "config_intrhook_establish failed!\n");
637114b2e18SPierre-Alain TORET 		return (ENXIO);
638114b2e18SPierre-Alain TORET 	}
639114b2e18SPierre-Alain TORET 
640114b2e18SPierre-Alain TORET 	return (0);
641114b2e18SPierre-Alain TORET }
642114b2e18SPierre-Alain TORET 
643114b2e18SPierre-Alain TORET void
amdtemp_intrhook(void * arg)644114b2e18SPierre-Alain TORET amdtemp_intrhook(void *arg)
645114b2e18SPierre-Alain TORET {
646114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc;
647114b2e18SPierre-Alain TORET 	struct sysctl_ctx_list *sysctlctx;
648114b2e18SPierre-Alain TORET 	device_t dev = (device_t)arg;
649114b2e18SPierre-Alain TORET 	device_t acpi, cpu, nexus;
650114b2e18SPierre-Alain TORET 	amdsensor_t sensor;
651114b2e18SPierre-Alain TORET 	int i;
652d834e1dcSMatthew Dillon 	int j;
653114b2e18SPierre-Alain TORET 
654114b2e18SPierre-Alain TORET 	sc = device_get_softc(dev);
655d834e1dcSMatthew Dillon 	if (sc->sc_ich.ich_arg == NULL)
656d834e1dcSMatthew Dillon 		return;
657114b2e18SPierre-Alain TORET 
658114b2e18SPierre-Alain TORET 	/*
659114b2e18SPierre-Alain TORET 	 * dev.cpu.N.temperature.
660114b2e18SPierre-Alain TORET 	 */
661114b2e18SPierre-Alain TORET 	nexus = device_find_child(root_bus, "nexus", 0);
662114b2e18SPierre-Alain TORET 	acpi = device_find_child(nexus, "acpi", 0);
663114b2e18SPierre-Alain TORET 
664114b2e18SPierre-Alain TORET 	for (i = 0; i < sc->sc_ncores; i++) {
665114b2e18SPierre-Alain TORET 		if (sc->sc_sysctl_cpu[i] != NULL)
666114b2e18SPierre-Alain TORET 			continue;
667114b2e18SPierre-Alain TORET 		cpu = device_find_child(acpi, "cpu",
668114b2e18SPierre-Alain TORET 		    device_get_unit(dev) * sc->sc_ncores + i);
669114b2e18SPierre-Alain TORET 		if (cpu != NULL) {
670114b2e18SPierre-Alain TORET 			sysctlctx = device_get_sysctl_ctx(cpu);
671114b2e18SPierre-Alain TORET 
672114b2e18SPierre-Alain TORET 			sensor = sc->sc_ntemps > 1 ?
673114b2e18SPierre-Alain TORET 			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
674114b2e18SPierre-Alain TORET 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
675114b2e18SPierre-Alain TORET 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
676114b2e18SPierre-Alain TORET 			    OID_AUTO, "temperature",
677114b2e18SPierre-Alain TORET 			    CTLTYPE_INT | CTLFLAG_RD,
678114b2e18SPierre-Alain TORET 			    dev, sensor, amdtemp_sysctl, "IK",
679114b2e18SPierre-Alain TORET 			    "Current temparature");
680114b2e18SPierre-Alain TORET 		}
681114b2e18SPierre-Alain TORET 	}
682114b2e18SPierre-Alain TORET 	config_intrhook_disestablish(&sc->sc_ich);
683d834e1dcSMatthew Dillon 
684d834e1dcSMatthew Dillon 	/*
685d834e1dcSMatthew Dillon 	 * sensor infrastructure.  Use [ncpus] for globally shared sensors
686d834e1dcSMatthew Dillon 	 */
687d834e1dcSMatthew Dillon 	sc->sc_sensorcpus = kmalloc(sizeof(*sc->sc_sensorcpus) *
688d834e1dcSMatthew Dillon 				   (sc->sc_ncores + 1),
689d834e1dcSMatthew Dillon 				   M_DEVBUF, M_WAITOK | M_ZERO);
690d834e1dcSMatthew Dillon 
691d834e1dcSMatthew Dillon 	for (i = 0; i <= sc->sc_ncores; i++) {
692d834e1dcSMatthew Dillon 		struct sensorcpu *scpu = &sc->sc_sensorcpus[i];
693d834e1dcSMatthew Dillon 
694d834e1dcSMatthew Dillon 		if (i == 0)
695d834e1dcSMatthew Dillon 			scpu->regmask = sc->sc_probed_regmask & 0x0003U;
696d834e1dcSMatthew Dillon 		else if (i == 1)
697d834e1dcSMatthew Dillon 			scpu->regmask = sc->sc_probed_regmask & 0x000CU;
698d834e1dcSMatthew Dillon 		else if (i != sc->sc_ncores)
699d834e1dcSMatthew Dillon 			scpu->regmask = 0;
700d834e1dcSMatthew Dillon 		else
701d834e1dcSMatthew Dillon 			scpu->regmask = sc->sc_probed_regmask & ~0xFU;
702d834e1dcSMatthew Dillon 
703d834e1dcSMatthew Dillon 		if (scpu->regmask == 0)
704d834e1dcSMatthew Dillon 			continue;
705d834e1dcSMatthew Dillon 
706d834e1dcSMatthew Dillon 		if (sc->sc_ccd_display) {
707d834e1dcSMatthew Dillon 			ksnprintf(scpu->sensordev.xname,
708d834e1dcSMatthew Dillon 				  sizeof(scpu->sensordev.xname),
709d834e1dcSMatthew Dillon 				  "die%d", device_get_unit(dev));
710d834e1dcSMatthew Dillon 		} else {
711d834e1dcSMatthew Dillon 			ksnprintf(scpu->sensordev.xname,
712d834e1dcSMatthew Dillon 				  sizeof(scpu->sensordev.xname),
713d834e1dcSMatthew Dillon 				  "cpu%d", i);
714d834e1dcSMatthew Dillon 		}
715d834e1dcSMatthew Dillon 
716d834e1dcSMatthew Dillon 		scpu->dev = dev;
717d834e1dcSMatthew Dillon 		scpu->sc = sc;
718d834e1dcSMatthew Dillon 		scpu->sensors = kmalloc(sizeof(*scpu->sensors) * MAXSENSORS,
719d834e1dcSMatthew Dillon 					M_DEVBUF, M_WAITOK | M_ZERO);
720d834e1dcSMatthew Dillon 		for (j = 0; j < MAXSENSORS; ++j) {
721d834e1dcSMatthew Dillon 			if ((scpu->regmask & (1U << j)) == 0)
722d834e1dcSMatthew Dillon 				continue;
723d834e1dcSMatthew Dillon 
724d834e1dcSMatthew Dillon 			switch(j) {
725d834e1dcSMatthew Dillon 			case CORE0_SENSOR0:
726d834e1dcSMatthew Dillon 			case CORE0_SENSOR1:
727d834e1dcSMatthew Dillon 			case CORE1_SENSOR0:
728d834e1dcSMatthew Dillon 			case CORE1_SENSOR1:
729d834e1dcSMatthew Dillon 				if (sc->sc_ccd_display) {
730d834e1dcSMatthew Dillon 					ksnprintf(scpu->sensors[j].desc,
731d834e1dcSMatthew Dillon 						  sizeof(scpu->sensors[0].desc),
732d834e1dcSMatthew Dillon 						  "high temp");
733d834e1dcSMatthew Dillon 				} else {
734d834e1dcSMatthew Dillon 					ksnprintf(scpu->sensors[j].desc,
735d834e1dcSMatthew Dillon 						  sizeof(scpu->sensors[0].desc),
736d834e1dcSMatthew Dillon 						  "temp%d", j & 1);
737d834e1dcSMatthew Dillon 				}
738d834e1dcSMatthew Dillon 				break;
739d834e1dcSMatthew Dillon 			case CORE0:
740d834e1dcSMatthew Dillon 				ksnprintf(scpu->sensors[j].desc,
741d834e1dcSMatthew Dillon 					  sizeof(scpu->sensors[0].desc),
742d834e1dcSMatthew Dillon 					  "core0 rollup temp");
743d834e1dcSMatthew Dillon 				break;
744d834e1dcSMatthew Dillon 			case CORE1:
745d834e1dcSMatthew Dillon 				ksnprintf(scpu->sensors[j].desc,
746d834e1dcSMatthew Dillon 					  sizeof(scpu->sensors[0].desc),
747d834e1dcSMatthew Dillon 					  "core1 rollup temp");
748d834e1dcSMatthew Dillon 				break;
749d834e1dcSMatthew Dillon 			case CCD_BASE ... CCD_MAX:
750d834e1dcSMatthew Dillon 				ksnprintf(scpu->sensors[j].desc,
751d834e1dcSMatthew Dillon 					  sizeof(scpu->sensors[0].desc),
752d834e1dcSMatthew Dillon 					  "ccd%u temp", j - CCD_BASE);
753d834e1dcSMatthew Dillon 				break;
754d834e1dcSMatthew Dillon 			}
755d834e1dcSMatthew Dillon 			scpu->sensors[j].type = SENSOR_TEMP;
756d834e1dcSMatthew Dillon 			sensor_set_unknown(&scpu->sensors[j]);
757d834e1dcSMatthew Dillon 			sensor_attach(&scpu->sensordev, &scpu->sensors[j]);
758d834e1dcSMatthew Dillon 		}
759d834e1dcSMatthew Dillon 		scpu->senstask = sensor_task_register2(scpu,
760d834e1dcSMatthew Dillon 						       amdtemp_sensor_task,
761d834e1dcSMatthew Dillon 						       2,
762d834e1dcSMatthew Dillon 						       ((i < sc->sc_ncores) ?
763d834e1dcSMatthew Dillon 							i : -1));
764d834e1dcSMatthew Dillon 		sensordev_install(&scpu->sensordev);
765d834e1dcSMatthew Dillon 	}
766114b2e18SPierre-Alain TORET }
767114b2e18SPierre-Alain TORET 
768114b2e18SPierre-Alain TORET int
amdtemp_detach(device_t dev)769114b2e18SPierre-Alain TORET amdtemp_detach(device_t dev)
770114b2e18SPierre-Alain TORET {
771114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
772114b2e18SPierre-Alain TORET 	int i;
773d834e1dcSMatthew Dillon 	int j;
774114b2e18SPierre-Alain TORET 
775d834e1dcSMatthew Dillon 	for (i = 0; i < sc->sc_ncores; i++) {
776114b2e18SPierre-Alain TORET 		if (sc->sc_sysctl_cpu[i] != NULL)
777114b2e18SPierre-Alain TORET 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
778d834e1dcSMatthew Dillon 	}
779d834e1dcSMatthew Dillon 
780d834e1dcSMatthew Dillon 	if (sc->sc_sensorcpus) {
781d834e1dcSMatthew Dillon 		for (i = 0; i <= sc->sc_ncores; i++) {
782d834e1dcSMatthew Dillon 			struct sensorcpu *scpu = &sc->sc_sensorcpus[i];
783d834e1dcSMatthew Dillon 
784d834e1dcSMatthew Dillon 			if (scpu->sensors) {
785d834e1dcSMatthew Dillon 				for (j = 0; j < MAXSENSORS; ++j) {
786d834e1dcSMatthew Dillon 					if ((scpu->regmask & (1U << j)) == 0)
787d834e1dcSMatthew Dillon 						continue;
788d834e1dcSMatthew Dillon 					sensor_detach(&scpu->sensordev,
789d834e1dcSMatthew Dillon 						      &scpu->sensors[j]);
790d834e1dcSMatthew Dillon 				}
791d834e1dcSMatthew Dillon 				if (scpu->senstask) {
792d834e1dcSMatthew Dillon 					sensor_task_unregister2(scpu->senstask);
793d834e1dcSMatthew Dillon 					scpu->senstask = NULL;
794d834e1dcSMatthew Dillon 				}
795d834e1dcSMatthew Dillon 				sensordev_deinstall(&scpu->sensordev);
796d834e1dcSMatthew Dillon 				kfree(scpu->sensors, M_DEVBUF);
797d834e1dcSMatthew Dillon 				scpu->sensors = NULL;
798d834e1dcSMatthew Dillon 			}
799d834e1dcSMatthew Dillon 		}
800d834e1dcSMatthew Dillon 		kfree(sc->sc_sensorcpus, M_DEVBUF);
801d834e1dcSMatthew Dillon 		sc->sc_sensorcpus = NULL;
802d834e1dcSMatthew Dillon 	}
803114b2e18SPierre-Alain TORET 
804114b2e18SPierre-Alain TORET 	/* NewBus removes the dev.amdtemp.N tree by itself. */
805114b2e18SPierre-Alain TORET 
806114b2e18SPierre-Alain TORET 	return (0);
807114b2e18SPierre-Alain TORET }
808114b2e18SPierre-Alain TORET 
809114b2e18SPierre-Alain TORET static int
amdtemp_sysctl(SYSCTL_HANDLER_ARGS)810114b2e18SPierre-Alain TORET amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
811114b2e18SPierre-Alain TORET {
812114b2e18SPierre-Alain TORET 	device_t dev = (device_t)arg1;
813114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
814114b2e18SPierre-Alain TORET 	amdsensor_t sensor = (amdsensor_t)arg2;
815114b2e18SPierre-Alain TORET 	int32_t auxtemp[2], temp;
816114b2e18SPierre-Alain TORET 	int error;
817114b2e18SPierre-Alain TORET 
818114b2e18SPierre-Alain TORET 	switch (sensor) {
819114b2e18SPierre-Alain TORET 	case CORE0:
820114b2e18SPierre-Alain TORET 		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
821114b2e18SPierre-Alain TORET 		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
822114b2e18SPierre-Alain TORET 		temp = imax(auxtemp[0], auxtemp[1]);
823114b2e18SPierre-Alain TORET 		break;
824114b2e18SPierre-Alain TORET 	case CORE1:
825114b2e18SPierre-Alain TORET 		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
826114b2e18SPierre-Alain TORET 		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
827114b2e18SPierre-Alain TORET 		temp = imax(auxtemp[0], auxtemp[1]);
828114b2e18SPierre-Alain TORET 		break;
829114b2e18SPierre-Alain TORET 	default:
830114b2e18SPierre-Alain TORET 		temp = sc->sc_gettemp(dev, sensor);
831114b2e18SPierre-Alain TORET 		break;
832114b2e18SPierre-Alain TORET 	}
833114b2e18SPierre-Alain TORET 	error = sysctl_handle_int(oidp, &temp, 0, req);
834114b2e18SPierre-Alain TORET 
835114b2e18SPierre-Alain TORET 	return (error);
836114b2e18SPierre-Alain TORET }
837114b2e18SPierre-Alain TORET 
838114b2e18SPierre-Alain TORET #define	AMDTEMP_ZERO_C_TO_K	2731
839114b2e18SPierre-Alain TORET 
840114b2e18SPierre-Alain TORET static int32_t
amdtemp_gettemp0f(device_t dev,amdsensor_t sensor)841114b2e18SPierre-Alain TORET amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
842114b2e18SPierre-Alain TORET {
843114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
844114b2e18SPierre-Alain TORET 	uint32_t mask, offset, temp;
845114b2e18SPierre-Alain TORET 
846114b2e18SPierre-Alain TORET 	/* Set Sensor/Core selector. */
847114b2e18SPierre-Alain TORET 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
848114b2e18SPierre-Alain TORET 	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
849114b2e18SPierre-Alain TORET 	switch (sensor) {
850114b2e18SPierre-Alain TORET 	case CORE0_SENSOR1:
851114b2e18SPierre-Alain TORET 		temp |= AMDTEMP_TTSR_SELSENSOR;
852114b2e18SPierre-Alain TORET 		/* FALLTHROUGH */
853114b2e18SPierre-Alain TORET 	case CORE0_SENSOR0:
854114b2e18SPierre-Alain TORET 	case CORE0:
855114b2e18SPierre-Alain TORET 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
856114b2e18SPierre-Alain TORET 			temp |= AMDTEMP_TTSR_SELCORE;
857114b2e18SPierre-Alain TORET 		break;
858114b2e18SPierre-Alain TORET 	case CORE1_SENSOR1:
859114b2e18SPierre-Alain TORET 		temp |= AMDTEMP_TTSR_SELSENSOR;
860114b2e18SPierre-Alain TORET 		/* FALLTHROUGH */
861114b2e18SPierre-Alain TORET 	case CORE1_SENSOR0:
862114b2e18SPierre-Alain TORET 	case CORE1:
863114b2e18SPierre-Alain TORET 		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
864114b2e18SPierre-Alain TORET 			temp |= AMDTEMP_TTSR_SELCORE;
865114b2e18SPierre-Alain TORET 		break;
866114b2e18SPierre-Alain TORET 	default:
86723be8282SSascha Wildner 		__assert_unreachable();
868114b2e18SPierre-Alain TORET 	}
869114b2e18SPierre-Alain TORET 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
870114b2e18SPierre-Alain TORET 
871114b2e18SPierre-Alain TORET 	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
872114b2e18SPierre-Alain TORET 	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
873114b2e18SPierre-Alain TORET 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
874114b2e18SPierre-Alain TORET 	temp = ((temp >> 14) & mask) * 5 / 2;
875114b2e18SPierre-Alain TORET 	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
876114b2e18SPierre-Alain TORET 
877114b2e18SPierre-Alain TORET 	return (temp);
878114b2e18SPierre-Alain TORET }
879114b2e18SPierre-Alain TORET 
880114b2e18SPierre-Alain TORET static uint32_t
amdtemp_decode_fam10h_to_17h(int32_t sc_offset,uint32_t val,bool minus49)881114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49)
882114b2e18SPierre-Alain TORET {
883114b2e18SPierre-Alain TORET 	uint32_t temp;
884114b2e18SPierre-Alain TORET 
885114b2e18SPierre-Alain TORET 	/* Convert raw register subfield units (0.125C) to units of 0.1C. */
886114b2e18SPierre-Alain TORET 	temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4;
887114b2e18SPierre-Alain TORET 
888114b2e18SPierre-Alain TORET 	if (minus49)
889114b2e18SPierre-Alain TORET 		temp -= AMDTEMP_CURTMP_RANGE_ADJUST;
890114b2e18SPierre-Alain TORET 
891114b2e18SPierre-Alain TORET 	temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10;
892114b2e18SPierre-Alain TORET 	return (temp);
893114b2e18SPierre-Alain TORET }
894114b2e18SPierre-Alain TORET 
895114b2e18SPierre-Alain TORET static uint32_t
amdtemp_decode_fam10h_to_16h(int32_t sc_offset,uint32_t val)896114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val)
897114b2e18SPierre-Alain TORET {
898114b2e18SPierre-Alain TORET 	bool minus49;
899114b2e18SPierre-Alain TORET 
900114b2e18SPierre-Alain TORET 	/*
901114b2e18SPierre-Alain TORET 	 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
902114b2e18SPierre-Alain TORET 	 * adjusted down by 49.0 degrees Celsius.  (This adjustment is not
903114b2e18SPierre-Alain TORET 	 * documented in BKDGs prior to family 15h model 00h.)
904114b2e18SPierre-Alain TORET 	 */
905114b2e18SPierre-Alain TORET 	minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 &&
906114b2e18SPierre-Alain TORET 	    ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) &
907114b2e18SPierre-Alain TORET 	    AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3);
908114b2e18SPierre-Alain TORET 
909114b2e18SPierre-Alain TORET 	return (amdtemp_decode_fam10h_to_17h(sc_offset,
910114b2e18SPierre-Alain TORET 	    val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
911114b2e18SPierre-Alain TORET }
912114b2e18SPierre-Alain TORET 
913114b2e18SPierre-Alain TORET static uint32_t
amdtemp_decode_fam17h_tctl(int32_t sc_offset,uint32_t val)914114b2e18SPierre-Alain TORET amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val)
915114b2e18SPierre-Alain TORET {
916114b2e18SPierre-Alain TORET 	bool minus49;
917114b2e18SPierre-Alain TORET 
918114b2e18SPierre-Alain TORET 	minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0);
919114b2e18SPierre-Alain TORET 	return (amdtemp_decode_fam10h_to_17h(sc_offset,
920114b2e18SPierre-Alain TORET 	    val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49));
921114b2e18SPierre-Alain TORET }
922114b2e18SPierre-Alain TORET 
923114b2e18SPierre-Alain TORET static int32_t
amdtemp_gettemp(device_t dev,amdsensor_t sensor)924114b2e18SPierre-Alain TORET amdtemp_gettemp(device_t dev, amdsensor_t sensor)
925114b2e18SPierre-Alain TORET {
926114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
927114b2e18SPierre-Alain TORET 	uint32_t temp;
928114b2e18SPierre-Alain TORET 
929114b2e18SPierre-Alain TORET 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
930114b2e18SPierre-Alain TORET 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp));
931114b2e18SPierre-Alain TORET }
932114b2e18SPierre-Alain TORET 
933114b2e18SPierre-Alain TORET static int32_t
amdtemp_gettemp15hm60h(device_t dev,amdsensor_t sensor)934114b2e18SPierre-Alain TORET amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor)
935114b2e18SPierre-Alain TORET {
936114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
937114b2e18SPierre-Alain TORET 	uint32_t val;
938114b2e18SPierre-Alain TORET 	int error;
939114b2e18SPierre-Alain TORET 
940114b2e18SPierre-Alain TORET 	error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val);
941114b2e18SPierre-Alain TORET 	KASSERT(error == 0, ("amdsmn_read"));
942114b2e18SPierre-Alain TORET 	return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val));
943114b2e18SPierre-Alain TORET }
944114b2e18SPierre-Alain TORET 
945114b2e18SPierre-Alain TORET static int32_t
amdtemp_gettemp17to19h(device_t dev,amdsensor_t sensor)946d834e1dcSMatthew Dillon amdtemp_gettemp17to19h(device_t dev, amdsensor_t sensor)
947114b2e18SPierre-Alain TORET {
948114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc = device_get_softc(dev);
949114b2e18SPierre-Alain TORET 	uint32_t val;
950114b2e18SPierre-Alain TORET 	int error;
951114b2e18SPierre-Alain TORET 
952114b2e18SPierre-Alain TORET 	switch (sensor) {
953114b2e18SPierre-Alain TORET 	case CORE0_SENSOR0:
954114b2e18SPierre-Alain TORET 		/* Tctl */
955114b2e18SPierre-Alain TORET 		error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val);
956114b2e18SPierre-Alain TORET 		KASSERT(error == 0, ("amdsmn_read"));
957114b2e18SPierre-Alain TORET 		return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val));
958114b2e18SPierre-Alain TORET 	case CCD_BASE ... CCD_MAX:
959114b2e18SPierre-Alain TORET 		/* Tccd<N> */
960*4970ada5SMatthew Dillon 		error = amdsmn_read(sc->sc_smn,
961*4970ada5SMatthew Dillon 				    AMDTEMP_17H_CUR_TMP +
962*4970ada5SMatthew Dillon 				    sc->sc_ccd_offset +
963*4970ada5SMatthew Dillon 				    (((int)sensor - CCD_BASE) * sizeof(val)),
964*4970ada5SMatthew Dillon 				    &val);
965114b2e18SPierre-Alain TORET 		KASSERT(error == 0, ("amdsmn_read2"));
966114b2e18SPierre-Alain TORET 		KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0,
967114b2e18SPierre-Alain TORET 		    ("sensor %d: not valid", (int)sensor));
968114b2e18SPierre-Alain TORET 		return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true));
969114b2e18SPierre-Alain TORET 	default:
97023be8282SSascha Wildner 		__assert_unreachable();
971114b2e18SPierre-Alain TORET 	}
972114b2e18SPierre-Alain TORET }
973114b2e18SPierre-Alain TORET 
974114b2e18SPierre-Alain TORET static void
amdtemp_probe_ccd_sensors17h(device_t dev,uint32_t model)975114b2e18SPierre-Alain TORET amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model)
976114b2e18SPierre-Alain TORET {
977114b2e18SPierre-Alain TORET 	char sensor_name[16], sensor_descr[32];
978114b2e18SPierre-Alain TORET 	struct amdtemp_softc *sc;
979114b2e18SPierre-Alain TORET 	uint32_t maxreg, i, val;
980114b2e18SPierre-Alain TORET 	int error;
981114b2e18SPierre-Alain TORET 
982114b2e18SPierre-Alain TORET 	switch (model) {
983114b2e18SPierre-Alain TORET 	case 0x00 ... 0x1f: /* Zen1, Zen+ */
984114b2e18SPierre-Alain TORET 		maxreg = 4;
985114b2e18SPierre-Alain TORET 		break;
986114b2e18SPierre-Alain TORET 	case 0x30 ... 0x3f: /* Zen2 TR/Epyc */
987*4970ada5SMatthew Dillon 	case 0x60:	    /* Renoir */
988*4970ada5SMatthew Dillon 	case 0x68:	    /* Lucienne */
989114b2e18SPierre-Alain TORET 	case 0x70 ... 0x7f: /* Zen2 Ryzen */
990114b2e18SPierre-Alain TORET 		maxreg = 8;
991114b2e18SPierre-Alain TORET 		_Static_assert((int)NUM_CCDS >= 8, "");
992114b2e18SPierre-Alain TORET 		break;
993*4970ada5SMatthew Dillon 	case 0xa0 ... 0xaf: /* Zen3 ? */
994*4970ada5SMatthew Dillon 		maxreg = 8;
995*4970ada5SMatthew Dillon 		_Static_assert((int)NUM_CCDS >= 8, "");
996*4970ada5SMatthew Dillon 		break;
997114b2e18SPierre-Alain TORET 	default:
998114b2e18SPierre-Alain TORET 		device_printf(dev,
999114b2e18SPierre-Alain TORET 		    "Unrecognized Family 17h Model: %02xh\n", model);
1000114b2e18SPierre-Alain TORET 		return;
1001114b2e18SPierre-Alain TORET 	}
1002114b2e18SPierre-Alain TORET 
1003114b2e18SPierre-Alain TORET 	sc = device_get_softc(dev);
1004114b2e18SPierre-Alain TORET 	for (i = 0; i < maxreg; i++) {
1005*4970ada5SMatthew Dillon 		error = amdsmn_read(sc->sc_smn,
1006*4970ada5SMatthew Dillon 				    AMDTEMP_17H_CUR_TMP +
1007*4970ada5SMatthew Dillon 				    sc->sc_ccd_offset +
1008*4970ada5SMatthew Dillon 				    (i * sizeof(val)),
1009*4970ada5SMatthew Dillon 				    &val);
1010114b2e18SPierre-Alain TORET 		if (error != 0)
1011114b2e18SPierre-Alain TORET 			continue;
1012114b2e18SPierre-Alain TORET 		if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0)
1013114b2e18SPierre-Alain TORET 			continue;
1014114b2e18SPierre-Alain TORET 
1015114b2e18SPierre-Alain TORET 		ksnprintf(sensor_name, sizeof(sensor_name), "ccd%u", i);
1016114b2e18SPierre-Alain TORET 		ksnprintf(sensor_descr, sizeof(sensor_descr),
1017114b2e18SPierre-Alain TORET 		    "CCD %u temperature (Tccd%u)", i, i);
1018114b2e18SPierre-Alain TORET 
1019114b2e18SPierre-Alain TORET 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1020114b2e18SPierre-Alain TORET 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1021114b2e18SPierre-Alain TORET 		    sensor_name, CTLTYPE_INT | CTLFLAG_RD,
1022114b2e18SPierre-Alain TORET 		    dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr);
1023d834e1dcSMatthew Dillon 
1024d834e1dcSMatthew Dillon 		sc->sc_probed_regmask |= 1U << (CCD_BASE + i);
1025d834e1dcSMatthew Dillon 	}
1026d834e1dcSMatthew Dillon }
1027d834e1dcSMatthew Dillon 
1028d834e1dcSMatthew Dillon static void
amdtemp_probe_ccd_sensors19h(device_t dev,uint32_t model)1029d834e1dcSMatthew Dillon amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model)
1030d834e1dcSMatthew Dillon {
1031d834e1dcSMatthew Dillon 	char sensor_name[16], sensor_descr[32];
1032d834e1dcSMatthew Dillon 	struct amdtemp_softc *sc;
1033d834e1dcSMatthew Dillon 	uint32_t maxreg, i, val;
1034d834e1dcSMatthew Dillon 	int error;
1035d834e1dcSMatthew Dillon 
1036*4970ada5SMatthew Dillon 	device_printf(dev, "probe ccd sensors 19h %02x\n", model);
1037*4970ada5SMatthew Dillon 
1038d834e1dcSMatthew Dillon         switch (model) {
1039d834e1dcSMatthew Dillon         case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */
1040d834e1dcSMatthew Dillon         case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */
1041*4970ada5SMatthew Dillon         case 0x40 ... 0x4f:
1042*4970ada5SMatthew Dillon         case 0x50 ... 0x5f:
1043*4970ada5SMatthew Dillon         case 0x60 ... 0x6f:
1044*4970ada5SMatthew Dillon         case 0x70 ... 0x7f:
1045d834e1dcSMatthew Dillon                 maxreg = 8;
1046d834e1dcSMatthew Dillon                 _Static_assert((int)NUM_CCDS >= 8, "");
1047d834e1dcSMatthew Dillon                 break;
1048*4970ada5SMatthew Dillon         case 0x10 ... 0x1f:
1049*4970ada5SMatthew Dillon         case 0xa0 ... 0xaf:
1050*4970ada5SMatthew Dillon                 maxreg = 12;
1051*4970ada5SMatthew Dillon                 _Static_assert((int)NUM_CCDS >= 12, "");
1052*4970ada5SMatthew Dillon 		break;
1053d834e1dcSMatthew Dillon         default:
1054d834e1dcSMatthew Dillon                 device_printf(dev,
1055d834e1dcSMatthew Dillon                     "Unrecognized Family 19h Model: %02xh\n", model);
1056d834e1dcSMatthew Dillon                 return;
1057d834e1dcSMatthew Dillon         }
1058d834e1dcSMatthew Dillon 
1059d834e1dcSMatthew Dillon 	sc = device_get_softc(dev);
1060d834e1dcSMatthew Dillon 	for (i = 0; i < maxreg; i++) {
1061*4970ada5SMatthew Dillon 		error = amdsmn_read(sc->sc_smn,
1062*4970ada5SMatthew Dillon 				    AMDTEMP_17H_CUR_TMP +
1063*4970ada5SMatthew Dillon 				    sc->sc_ccd_offset +
1064*4970ada5SMatthew Dillon 				    (i * sizeof(val)),
1065*4970ada5SMatthew Dillon 				    &val);
1066*4970ada5SMatthew Dillon 		device_printf(dev, "probe ccd%d error %d val=%08x\n",
1067*4970ada5SMatthew Dillon 			      i, error, val);
1068d834e1dcSMatthew Dillon 		if (error != 0)
1069d834e1dcSMatthew Dillon 			continue;
1070d834e1dcSMatthew Dillon 		if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0)
1071d834e1dcSMatthew Dillon 			continue;
1072d834e1dcSMatthew Dillon 
1073d834e1dcSMatthew Dillon 		ksnprintf(sensor_name, sizeof(sensor_name), "ccd%u", i);
1074d834e1dcSMatthew Dillon 		ksnprintf(sensor_descr, sizeof(sensor_descr),
1075d834e1dcSMatthew Dillon 		    "CCD %u temperature (Tccd%u)", i, i);
1076d834e1dcSMatthew Dillon 
1077d834e1dcSMatthew Dillon 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1078d834e1dcSMatthew Dillon 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1079d834e1dcSMatthew Dillon 		    sensor_name, CTLTYPE_INT | CTLFLAG_RD,
1080d834e1dcSMatthew Dillon 		    dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr);
1081d834e1dcSMatthew Dillon 
1082d834e1dcSMatthew Dillon 		sc->sc_probed_regmask |= 1U << (CCD_BASE + i);
1083d834e1dcSMatthew Dillon 	}
1084d834e1dcSMatthew Dillon }
1085d834e1dcSMatthew Dillon 
1086d834e1dcSMatthew Dillon static void
amdtemp_sensor_task(void * sc_arg)1087d834e1dcSMatthew Dillon amdtemp_sensor_task(void *sc_arg)
1088d834e1dcSMatthew Dillon {
1089d834e1dcSMatthew Dillon 	struct sensorcpu *scpu = sc_arg;
1090d834e1dcSMatthew Dillon 	struct amdtemp_softc *sc;
1091d834e1dcSMatthew Dillon 	uint32_t mask;
1092d834e1dcSMatthew Dillon 	int32_t temp;
1093d834e1dcSMatthew Dillon 	int j;
1094d834e1dcSMatthew Dillon 
1095d834e1dcSMatthew Dillon 	sc = scpu->sc;
1096d834e1dcSMatthew Dillon 	if (sc->sc_ich.ich_arg == NULL)
1097d834e1dcSMatthew Dillon 		return;
1098d834e1dcSMatthew Dillon 	mask = scpu->regmask;
1099d834e1dcSMatthew Dillon 
1100d834e1dcSMatthew Dillon 	for (j = 0; mask; ++j) {
1101d834e1dcSMatthew Dillon 		if ((mask & (1U << j)) == 0)
1102d834e1dcSMatthew Dillon 			continue;
1103d834e1dcSMatthew Dillon 		temp = sc->sc_gettemp(scpu->dev, j);
1104d834e1dcSMatthew Dillon 		sensor_set(&scpu->sensors[j], temp * 100000L, 0);
1105d834e1dcSMatthew Dillon 		mask &= ~(1U << j);
1106114b2e18SPierre-Alain TORET 	}
1107114b2e18SPierre-Alain TORET }
1108