xref: /freebsd/sys/dev/amdtemp/amdtemp.c (revision a4165bba6f21e4e977c45105567d8f38e6f76f9e)
1fc1f75e5SRui Paulo /*-
2454e82d7SRui Paulo  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3454e82d7SRui Paulo  * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4a4165bbaSJung-uk Kim  * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org>
5fc1f75e5SRui Paulo  * All rights reserved.
6fc1f75e5SRui Paulo  *
7fc1f75e5SRui Paulo  * Redistribution and use in source and binary forms, with or without
8fc1f75e5SRui Paulo  * modification, are permitted provided that the following conditions
9fc1f75e5SRui Paulo  * are met:
10fc1f75e5SRui Paulo  * 1. Redistributions of source code must retain the above copyright
11fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer.
12fc1f75e5SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
13fc1f75e5SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
14fc1f75e5SRui Paulo  *    documentation and/or other materials provided with the distribution.
15fc1f75e5SRui Paulo  *
16fc1f75e5SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17fc1f75e5SRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18fc1f75e5SRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19fc1f75e5SRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20fc1f75e5SRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21fc1f75e5SRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22fc1f75e5SRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fc1f75e5SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24fc1f75e5SRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25fc1f75e5SRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26fc1f75e5SRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
27fc1f75e5SRui Paulo  */
28fc1f75e5SRui Paulo 
29fc1f75e5SRui Paulo /*
30a4165bbaSJung-uk Kim  * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs.
31a4165bbaSJung-uk Kim  * Initially based on the k8temp Linux driver.
32fc1f75e5SRui Paulo  */
33fc1f75e5SRui Paulo 
34fc1f75e5SRui Paulo #include <sys/cdefs.h>
35fc1f75e5SRui Paulo __FBSDID("$FreeBSD$");
36fc1f75e5SRui Paulo 
37fc1f75e5SRui Paulo #include <sys/param.h>
38fc1f75e5SRui Paulo #include <sys/bus.h>
39fc1f75e5SRui Paulo #include <sys/conf.h>
40fc1f75e5SRui Paulo #include <sys/kernel.h>
41a4165bbaSJung-uk Kim #include <sys/module.h>
42fc1f75e5SRui Paulo #include <sys/sysctl.h>
43a4165bbaSJung-uk Kim #include <sys/systm.h>
44fc1f75e5SRui Paulo 
45fc1f75e5SRui Paulo #include <machine/md_var.h>
46a4165bbaSJung-uk Kim #include <machine/specialreg.h>
47fc1f75e5SRui Paulo 
48fc1f75e5SRui Paulo #include <dev/pci/pcivar.h>
49fc1f75e5SRui Paulo 
50fc1f75e5SRui Paulo typedef enum {
51fc1f75e5SRui Paulo 	SENSOR0_CORE0,
52fc1f75e5SRui Paulo 	SENSOR0_CORE1,
53fc1f75e5SRui Paulo 	SENSOR1_CORE0,
54fc1f75e5SRui Paulo 	SENSOR1_CORE1,
55fc1f75e5SRui Paulo 	CORE0,
56fc1f75e5SRui Paulo 	CORE1
57454e82d7SRui Paulo } amdsensor_t;
58454e82d7SRui Paulo 
59454e82d7SRui Paulo struct amdtemp_softc {
60454e82d7SRui Paulo 	device_t	sc_dev;
61a4165bbaSJung-uk Kim 	uint32_t	sc_mask;
62a4165bbaSJung-uk Kim 	int		sc_ncores;
63454e82d7SRui Paulo 	int		sc_ntemps;
64a4165bbaSJung-uk Kim 	int		sc_swap;
65454e82d7SRui Paulo 	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
66a4165bbaSJung-uk Kim 	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
67a4165bbaSJung-uk Kim 	struct intr_config_hook sc_ich;
68454e82d7SRui Paulo };
69454e82d7SRui Paulo 
70454e82d7SRui Paulo #define	VENDORID_AMD		0x1022
71454e82d7SRui Paulo #define	DEVICEID_AMD_MISC0F	0x1103
72454e82d7SRui Paulo #define	DEVICEID_AMD_MISC10	0x1203
73454e82d7SRui Paulo #define	DEVICEID_AMD_MISC11	0x1303
74454e82d7SRui Paulo 
75454e82d7SRui Paulo static struct amdtemp_product {
76454e82d7SRui Paulo 	uint16_t	amdtemp_vendorid;
77454e82d7SRui Paulo 	uint16_t	amdtemp_deviceid;
78454e82d7SRui Paulo } amdtemp_products[] = {
79454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
80454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
81454e82d7SRui Paulo 	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
82454e82d7SRui Paulo 	{ 0, 0 }
83454e82d7SRui Paulo };
84454e82d7SRui Paulo 
85454e82d7SRui Paulo /*
86a4165bbaSJung-uk Kim  * Reported Temperature Control Register (Family 10h/11h only)
87454e82d7SRui Paulo  */
88a4165bbaSJung-uk Kim #define	AMDTEMP_REPTMP_CTRL	0xa4
89454e82d7SRui Paulo 
90454e82d7SRui Paulo /*
91a4165bbaSJung-uk Kim  * Thermaltrip Status Register
92454e82d7SRui Paulo  */
93a4165bbaSJung-uk Kim #define	AMDTEMP_THERMTP_STAT	0xe4
94a4165bbaSJung-uk Kim #define	AMDTEMP_TTSR_SELCORE	0x04	/* Family 0Fh only */
95a4165bbaSJung-uk Kim #define	AMDTEMP_TTSR_SELSENSOR	0x40	/* Family 0Fh only */
96454e82d7SRui Paulo 
97a4165bbaSJung-uk Kim /*
98a4165bbaSJung-uk Kim  * CPU Family/Model Register
99a4165bbaSJung-uk Kim  */
100a4165bbaSJung-uk Kim #define	AMDTEMP_CPUID		0xfc
101fc1f75e5SRui Paulo 
102fc1f75e5SRui Paulo /*
103fc1f75e5SRui Paulo  * Device methods.
104fc1f75e5SRui Paulo  */
105454e82d7SRui Paulo static void 	amdtemp_identify(driver_t *driver, device_t parent);
106454e82d7SRui Paulo static int	amdtemp_probe(device_t dev);
107454e82d7SRui Paulo static int	amdtemp_attach(device_t dev);
108454e82d7SRui Paulo static void	amdtemp_intrhook(void *arg);
109454e82d7SRui Paulo static int	amdtemp_detach(device_t dev);
110454e82d7SRui Paulo static int 	amdtemp_match(device_t dev);
111454e82d7SRui Paulo static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
112454e82d7SRui Paulo static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
113454e82d7SRui Paulo static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
114fc1f75e5SRui Paulo 
115454e82d7SRui Paulo static device_method_t amdtemp_methods[] = {
116fc1f75e5SRui Paulo 	/* Device interface */
117454e82d7SRui Paulo 	DEVMETHOD(device_identify,	amdtemp_identify),
118454e82d7SRui Paulo 	DEVMETHOD(device_probe,		amdtemp_probe),
119454e82d7SRui Paulo 	DEVMETHOD(device_attach,	amdtemp_attach),
120454e82d7SRui Paulo 	DEVMETHOD(device_detach,	amdtemp_detach),
121fc1f75e5SRui Paulo 
122fc1f75e5SRui Paulo 	{0, 0}
123fc1f75e5SRui Paulo };
124fc1f75e5SRui Paulo 
125454e82d7SRui Paulo static driver_t amdtemp_driver = {
126454e82d7SRui Paulo 	"amdtemp",
127454e82d7SRui Paulo 	amdtemp_methods,
128454e82d7SRui Paulo 	sizeof(struct amdtemp_softc),
129fc1f75e5SRui Paulo };
130fc1f75e5SRui Paulo 
131454e82d7SRui Paulo static devclass_t amdtemp_devclass;
132454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
133fc1f75e5SRui Paulo 
134fc1f75e5SRui Paulo static int
135454e82d7SRui Paulo amdtemp_match(device_t dev)
136fc1f75e5SRui Paulo {
137fc1f75e5SRui Paulo 	int i;
138fc1f75e5SRui Paulo 	uint16_t vendor, devid;
139fc1f75e5SRui Paulo 
140fc1f75e5SRui Paulo 	vendor = pci_get_vendor(dev);
141fc1f75e5SRui Paulo 	devid = pci_get_device(dev);
142fc1f75e5SRui Paulo 
143454e82d7SRui Paulo 	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
144454e82d7SRui Paulo 		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
145454e82d7SRui Paulo 		    devid == amdtemp_products[i].amdtemp_deviceid)
146fc1f75e5SRui Paulo 			return (1);
147fc1f75e5SRui Paulo 	}
148fc1f75e5SRui Paulo 
149fc1f75e5SRui Paulo 	return (0);
150fc1f75e5SRui Paulo }
151fc1f75e5SRui Paulo 
152fc1f75e5SRui Paulo static void
153454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent)
154fc1f75e5SRui Paulo {
155fc1f75e5SRui Paulo 	device_t child;
156fc1f75e5SRui Paulo 
157fc1f75e5SRui Paulo 	/* Make sure we're not being doubly invoked. */
158454e82d7SRui Paulo 	if (device_find_child(parent, "amdtemp", -1) != NULL)
159fc1f75e5SRui Paulo 		return;
160fc1f75e5SRui Paulo 
161454e82d7SRui Paulo 	if (amdtemp_match(parent)) {
162454e82d7SRui Paulo 		child = device_add_child(parent, "amdtemp", -1);
163fc1f75e5SRui Paulo 		if (child == NULL)
164454e82d7SRui Paulo 			device_printf(parent, "add amdtemp child failed\n");
165fc1f75e5SRui Paulo 	}
166fc1f75e5SRui Paulo }
167fc1f75e5SRui Paulo 
168fc1f75e5SRui Paulo static int
169454e82d7SRui Paulo amdtemp_probe(device_t dev)
170fc1f75e5SRui Paulo {
171a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model, temp;
172fc1f75e5SRui Paulo 
173454e82d7SRui Paulo 	if (resource_disabled("amdtemp", 0))
174fc1f75e5SRui Paulo 		return (ENXIO);
175fc1f75e5SRui Paulo 
176a4165bbaSJung-uk Kim 	cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
177a4165bbaSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
178a4165bbaSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
179a4165bbaSJung-uk Kim 
180a4165bbaSJung-uk Kim 	switch (family) {
181a4165bbaSJung-uk Kim 	case 0x0f:
182a4165bbaSJung-uk Kim 		if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) ||
183a4165bbaSJung-uk Kim 		    (model == 0x05 && (cpuid & CPUID_STEPPING) <= 1))
184a4165bbaSJung-uk Kim 			return (ENXIO);
185a4165bbaSJung-uk Kim 		break;
186a4165bbaSJung-uk Kim 	case 0x10:
187a4165bbaSJung-uk Kim 	case 0x11:
188a4165bbaSJung-uk Kim 		/*
189a4165bbaSJung-uk Kim 		 * DiodeOffset must be non-zero if thermal diode is supported.
190a4165bbaSJung-uk Kim 		 */
191a4165bbaSJung-uk Kim 		temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
192a4165bbaSJung-uk Kim 		temp = (temp >> 8) & 0x7f;
193a4165bbaSJung-uk Kim 		if (temp == 0)
194a4165bbaSJung-uk Kim 			return (ENXIO);
195a4165bbaSJung-uk Kim 		break;
196a4165bbaSJung-uk Kim 	default:
197fc1f75e5SRui Paulo 		return (ENXIO);
198fc1f75e5SRui Paulo 	}
199a4165bbaSJung-uk Kim 	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
200fc1f75e5SRui Paulo 
201fc1f75e5SRui Paulo 	return (BUS_PROBE_GENERIC);
202fc1f75e5SRui Paulo }
203fc1f75e5SRui Paulo 
204fc1f75e5SRui Paulo static int
205454e82d7SRui Paulo amdtemp_attach(device_t dev)
206fc1f75e5SRui Paulo {
207454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
208fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
209fc1f75e5SRui Paulo 	struct sysctl_oid *sysctlnode;
210a4165bbaSJung-uk Kim 	uint32_t cpuid, family, model;
211fc1f75e5SRui Paulo 
212a4165bbaSJung-uk Kim 	cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
213a4165bbaSJung-uk Kim 	family = CPUID_TO_FAMILY(cpuid);
214a4165bbaSJung-uk Kim 	model = CPUID_TO_MODEL(cpuid);
215a4165bbaSJung-uk Kim 
216a4165bbaSJung-uk Kim 	switch (family) {
217a4165bbaSJung-uk Kim 	case 0x0f:
218a4165bbaSJung-uk Kim 		/*
219a4165bbaSJung-uk Kim 		 * Thermaltrip Status Register - CurTmp
220a4165bbaSJung-uk Kim 		 *
221a4165bbaSJung-uk Kim 		 * Revision G:		bits 23-14
222a4165bbaSJung-uk Kim 		 * Earlier:		bits 23-16
223a4165bbaSJung-uk Kim 		 */
224a4165bbaSJung-uk Kim 		if (model >= 0x60 && model != 0xc1)
225a4165bbaSJung-uk Kim 			sc->sc_mask = 0x3ff << 14;
226a4165bbaSJung-uk Kim 		else
227a4165bbaSJung-uk Kim 			sc->sc_mask = 0xff << 16;
228fc1f75e5SRui Paulo 
229fc1f75e5SRui Paulo 		/*
230a4165bbaSJung-uk Kim 		 * Thermaltrip Status Register - ThermSenseCoreSel
231a4165bbaSJung-uk Kim 		 *
232a4165bbaSJung-uk Kim 		 * Revision F:		0 - Core1, 1 - Core0
233a4165bbaSJung-uk Kim 		 * Earlier:		0 - Core0, 1 - Core1
234fc1f75e5SRui Paulo 		 */
235a4165bbaSJung-uk Kim 		sc->sc_swap = (model >= 0x40);
236a4165bbaSJung-uk Kim 
237a4165bbaSJung-uk Kim 		/*
238a4165bbaSJung-uk Kim 		 * There are two sensors per core.
239a4165bbaSJung-uk Kim 		 */
240a4165bbaSJung-uk Kim 		sc->sc_ntemps = 2;
241a4165bbaSJung-uk Kim 
242a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp0f;
243a4165bbaSJung-uk Kim 		break;
244a4165bbaSJung-uk Kim 	case 0x10:
245a4165bbaSJung-uk Kim 	case 0x11:
246a4165bbaSJung-uk Kim 		/*
247a4165bbaSJung-uk Kim 		 * Reported Temperature Control Register - Curtmp
248a4165bbaSJung-uk Kim 		 */
249a4165bbaSJung-uk Kim 		sc->sc_mask = 0x3ff << 21;
250a4165bbaSJung-uk Kim 
251a4165bbaSJung-uk Kim 		/*
252a4165bbaSJung-uk Kim 		 * There is only one sensor per package.
253a4165bbaSJung-uk Kim 		 */
254a4165bbaSJung-uk Kim 		sc->sc_ntemps = 1;
255a4165bbaSJung-uk Kim 
256a4165bbaSJung-uk Kim 		sc->sc_gettemp = amdtemp_gettemp;
257a4165bbaSJung-uk Kim 		break;
258fc1f75e5SRui Paulo 	}
259fc1f75e5SRui Paulo 
260a4165bbaSJung-uk Kim 	/* Find number of cores per package. */
261a4165bbaSJung-uk Kim 	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
262a4165bbaSJung-uk Kim 	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
263a4165bbaSJung-uk Kim 	if (sc->sc_ncores > MAXCPU)
264a4165bbaSJung-uk Kim 		return (ENXIO);
265a4165bbaSJung-uk Kim 
266a4165bbaSJung-uk Kim 	if (bootverbose)
267a4165bbaSJung-uk Kim 		device_printf(dev, "Found %d cores and %d sensors.\n",
268a4165bbaSJung-uk Kim 		    sc->sc_ncores,
269a4165bbaSJung-uk Kim 		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
270454e82d7SRui Paulo 
271fc1f75e5SRui Paulo 	/*
272454e82d7SRui Paulo 	 * dev.amdtemp.N tree.
273fc1f75e5SRui Paulo 	 */
274fc1f75e5SRui Paulo 	sysctlctx = device_get_sysctl_ctx(dev);
275fc1f75e5SRui Paulo 	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
276a4165bbaSJung-uk Kim 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
277a4165bbaSJung-uk Kim 	    "sensor0", CTLFLAG_RD, 0, "Sensor 0");
278fc1f75e5SRui Paulo 
279fc1f75e5SRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
280fc1f75e5SRui Paulo 	    SYSCTL_CHILDREN(sysctlnode),
281fc1f75e5SRui Paulo 	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
282454e82d7SRui Paulo 	    dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
283fc1f75e5SRui Paulo 	    "Sensor 0 / Core 0 temperature");
284fc1f75e5SRui Paulo 
285a4165bbaSJung-uk Kim 	if (sc->sc_ntemps > 1) {
286a4165bbaSJung-uk Kim 		if (sc->sc_ncores > 1)
287fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
288fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
289fc1f75e5SRui Paulo 			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
290454e82d7SRui Paulo 			    dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
291fc1f75e5SRui Paulo 			    "Sensor 0 / Core 1 temperature");
292fc1f75e5SRui Paulo 
293fc1f75e5SRui Paulo 		sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
294a4165bbaSJung-uk Kim 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
295a4165bbaSJung-uk Kim 		    "sensor1", CTLFLAG_RD, 0, "Sensor 1");
296fc1f75e5SRui Paulo 
297fc1f75e5SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
298fc1f75e5SRui Paulo 		    SYSCTL_CHILDREN(sysctlnode),
299fc1f75e5SRui Paulo 		    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
300454e82d7SRui Paulo 		    dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
301fc1f75e5SRui Paulo 		    "Sensor 1 / Core 0 temperature");
302fc1f75e5SRui Paulo 
303a4165bbaSJung-uk Kim 		if (sc->sc_ncores > 1)
304fc1f75e5SRui Paulo 			SYSCTL_ADD_PROC(sysctlctx,
305fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(sysctlnode),
306fc1f75e5SRui Paulo 			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
307454e82d7SRui Paulo 			    dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
308fc1f75e5SRui Paulo 			    "Sensor 1 / Core 1 temperature");
309a4165bbaSJung-uk Kim 	}
310a4165bbaSJung-uk Kim 
311a4165bbaSJung-uk Kim 	/*
312a4165bbaSJung-uk Kim 	 * Try to create dev.cpu sysctl entries and setup intrhook function.
313a4165bbaSJung-uk Kim 	 * This is needed because the cpu driver may be loaded late on boot,
314a4165bbaSJung-uk Kim 	 * after us.
315a4165bbaSJung-uk Kim 	 */
316a4165bbaSJung-uk Kim 	amdtemp_intrhook(dev);
317a4165bbaSJung-uk Kim 	sc->sc_ich.ich_func = amdtemp_intrhook;
318a4165bbaSJung-uk Kim 	sc->sc_ich.ich_arg = dev;
319a4165bbaSJung-uk Kim 	if (config_intrhook_establish(&sc->sc_ich) != 0) {
320a4165bbaSJung-uk Kim 		device_printf(dev, "config_intrhook_establish failed!\n");
321a4165bbaSJung-uk Kim 		return (ENXIO);
322a4165bbaSJung-uk Kim 	}
323fc1f75e5SRui Paulo 
324fc1f75e5SRui Paulo 	return (0);
325fc1f75e5SRui Paulo }
326fc1f75e5SRui Paulo 
327fc1f75e5SRui Paulo void
328454e82d7SRui Paulo amdtemp_intrhook(void *arg)
329fc1f75e5SRui Paulo {
330454e82d7SRui Paulo 	struct amdtemp_softc *sc;
331fc1f75e5SRui Paulo 	struct sysctl_ctx_list *sysctlctx;
332a4165bbaSJung-uk Kim 	device_t dev = (device_t)arg;
333a4165bbaSJung-uk Kim 	device_t acpi, cpu, nexus;
334a4165bbaSJung-uk Kim 	amdsensor_t sensor;
335a4165bbaSJung-uk Kim 	int i;
336fc1f75e5SRui Paulo 
337fc1f75e5SRui Paulo 	sc = device_get_softc(dev);
338fc1f75e5SRui Paulo 
339fc1f75e5SRui Paulo 	/*
340fc1f75e5SRui Paulo 	 * dev.cpu.N.temperature.
341fc1f75e5SRui Paulo 	 */
342fc1f75e5SRui Paulo 	nexus = device_find_child(root_bus, "nexus", 0);
343fc1f75e5SRui Paulo 	acpi = device_find_child(nexus, "acpi", 0);
344fc1f75e5SRui Paulo 
345a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++) {
346a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
347a4165bbaSJung-uk Kim 			continue;
348fc1f75e5SRui Paulo 		cpu = device_find_child(acpi, "cpu",
349a4165bbaSJung-uk Kim 		    device_get_unit(dev) * sc->sc_ncores + i);
350a4165bbaSJung-uk Kim 		if (cpu != NULL) {
351fc1f75e5SRui Paulo 			sysctlctx = device_get_sysctl_ctx(cpu);
352fc1f75e5SRui Paulo 
353a4165bbaSJung-uk Kim 			sensor = sc->sc_ntemps > 1 ?
354a4165bbaSJung-uk Kim 			    (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0;
355fc1f75e5SRui Paulo 			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
356fc1f75e5SRui Paulo 			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
357fc1f75e5SRui Paulo 			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
358a4165bbaSJung-uk Kim 			    dev, sensor, amdtemp_sysctl, "IK",
359a4165bbaSJung-uk Kim 			    "Current temparature");
360fc1f75e5SRui Paulo 		}
361fc1f75e5SRui Paulo 	}
362a4165bbaSJung-uk Kim 	if (sc->sc_ich.ich_arg != NULL)
363fc1f75e5SRui Paulo 		config_intrhook_disestablish(&sc->sc_ich);
364fc1f75e5SRui Paulo }
365fc1f75e5SRui Paulo 
366fc1f75e5SRui Paulo int
367454e82d7SRui Paulo amdtemp_detach(device_t dev)
368fc1f75e5SRui Paulo {
369454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
370a4165bbaSJung-uk Kim 	int i;
371fc1f75e5SRui Paulo 
372a4165bbaSJung-uk Kim 	for (i = 0; i < sc->sc_ncores; i++)
373a4165bbaSJung-uk Kim 		if (sc->sc_sysctl_cpu[i] != NULL)
374fc1f75e5SRui Paulo 			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
375fc1f75e5SRui Paulo 
376454e82d7SRui Paulo 	/* NewBus removes the dev.amdtemp.N tree by itself. */
377fc1f75e5SRui Paulo 
378fc1f75e5SRui Paulo 	return (0);
379fc1f75e5SRui Paulo }
380fc1f75e5SRui Paulo 
381fc1f75e5SRui Paulo static int
382454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
383fc1f75e5SRui Paulo {
384fc1f75e5SRui Paulo 	device_t dev = (device_t)arg1;
385454e82d7SRui Paulo 	struct amdtemp_softc *sc = device_get_softc(dev);
386a4165bbaSJung-uk Kim 	amdsensor_t sensor = (amdsensor_t)arg2;
387a4165bbaSJung-uk Kim 	int32_t auxtemp[2], temp;
388fc1f75e5SRui Paulo 	int error;
389fc1f75e5SRui Paulo 
390a4165bbaSJung-uk Kim 	switch (sensor) {
391fc1f75e5SRui Paulo 	case CORE0:
392454e82d7SRui Paulo 		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
393454e82d7SRui Paulo 		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
394fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
395fc1f75e5SRui Paulo 		break;
396fc1f75e5SRui Paulo 	case CORE1:
397454e82d7SRui Paulo 		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1);
398454e82d7SRui Paulo 		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1);
399fc1f75e5SRui Paulo 		temp = imax(auxtemp[0], auxtemp[1]);
400fc1f75e5SRui Paulo 		break;
401fc1f75e5SRui Paulo 	default:
402a4165bbaSJung-uk Kim 		temp = sc->sc_gettemp(dev, sensor);
403fc1f75e5SRui Paulo 		break;
404fc1f75e5SRui Paulo 	}
405fc1f75e5SRui Paulo 	error = sysctl_handle_int(oidp, &temp, 0, req);
406fc1f75e5SRui Paulo 
407fc1f75e5SRui Paulo 	return (error);
408fc1f75e5SRui Paulo }
409fc1f75e5SRui Paulo 
410a4165bbaSJung-uk Kim #define	AMDTEMP_ZERO_C_TO_K	2732
411a4165bbaSJung-uk Kim 
412fc1f75e5SRui Paulo static int32_t
413454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
414fc1f75e5SRui Paulo {
415a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
416fc1f75e5SRui Paulo 	uint32_t temp;
417a4165bbaSJung-uk Kim 	int32_t diode_offset, offset;
418a4165bbaSJung-uk Kim 	uint8_t cfg, sel;
419fc1f75e5SRui Paulo 
420a4165bbaSJung-uk Kim 	/* Set Sensor/Core selector. */
421a4165bbaSJung-uk Kim 	sel = 0;
422fc1f75e5SRui Paulo 	switch (sensor) {
423fc1f75e5SRui Paulo 	case SENSOR1_CORE0:
424a4165bbaSJung-uk Kim 		sel |= AMDTEMP_TTSR_SELSENSOR;
425a4165bbaSJung-uk Kim 		/* FALLTROUGH */
426a4165bbaSJung-uk Kim 	case SENSOR0_CORE0:
427a4165bbaSJung-uk Kim 	case CORE0:
428a4165bbaSJung-uk Kim 		if (sc->sc_swap)
429a4165bbaSJung-uk Kim 			sel |= AMDTEMP_TTSR_SELCORE;
430fc1f75e5SRui Paulo 		break;
431fc1f75e5SRui Paulo 	case SENSOR1_CORE1:
432a4165bbaSJung-uk Kim 		sel |= AMDTEMP_TTSR_SELSENSOR;
433a4165bbaSJung-uk Kim 		/* FALLTROUGH */
434a4165bbaSJung-uk Kim 	case SENSOR0_CORE1:
435a4165bbaSJung-uk Kim 	case CORE1:
436a4165bbaSJung-uk Kim 		if (!sc->sc_swap)
437a4165bbaSJung-uk Kim 			sel |= AMDTEMP_TTSR_SELCORE;
438fc1f75e5SRui Paulo 		break;
439fc1f75e5SRui Paulo 	}
440a4165bbaSJung-uk Kim 	cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
441a4165bbaSJung-uk Kim 	cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE);
442a4165bbaSJung-uk Kim 	pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1);
443a4165bbaSJung-uk Kim 
444a4165bbaSJung-uk Kim 	/* CurTmp starts from -49C. */
445a4165bbaSJung-uk Kim 	offset = AMDTEMP_ZERO_C_TO_K - 490;
446a4165bbaSJung-uk Kim 
447a4165bbaSJung-uk Kim 	/* Adjust offset if DiodeOffset is set and valid. */
448a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
449a4165bbaSJung-uk Kim 	diode_offset = (temp >> 8) & 0x3f;
450a4165bbaSJung-uk Kim 	if (diode_offset != 0)
451a4165bbaSJung-uk Kim 		offset += (diode_offset - 11) * 10;
452a4165bbaSJung-uk Kim 
453a4165bbaSJung-uk Kim 	temp = ((temp & sc->sc_mask) >> 14) * 5 / 2 + offset;
454454e82d7SRui Paulo 
455454e82d7SRui Paulo 	return (temp);
456454e82d7SRui Paulo }
457454e82d7SRui Paulo 
458454e82d7SRui Paulo static int32_t
459454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor)
460454e82d7SRui Paulo {
461a4165bbaSJung-uk Kim 	struct amdtemp_softc *sc = device_get_softc(dev);
462454e82d7SRui Paulo 	uint32_t temp;
463a4165bbaSJung-uk Kim 	int32_t diode_offset, offset;
464454e82d7SRui Paulo 
465a4165bbaSJung-uk Kim 	/* CurTmp starts from 0C. */
466a4165bbaSJung-uk Kim 	offset = AMDTEMP_ZERO_C_TO_K;
467a4165bbaSJung-uk Kim 
468a4165bbaSJung-uk Kim 	/* Adjust offset if DiodeOffset is set and valid. */
469a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
470a4165bbaSJung-uk Kim 	diode_offset = (temp >> 8) & 0x7f;
471a4165bbaSJung-uk Kim 	if (diode_offset > 0 && diode_offset < 0x40)
472a4165bbaSJung-uk Kim 		offset += (diode_offset - 11) * 10;
473a4165bbaSJung-uk Kim 
474a4165bbaSJung-uk Kim 	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
475a4165bbaSJung-uk Kim 	temp = ((temp & sc->sc_mask) >> 21) * 5 / 4 + offset;
476fc1f75e5SRui Paulo 
477fc1f75e5SRui Paulo 	return (temp);
478fc1f75e5SRui Paulo }
479