1178151Srpaulo/*-
2189769Srpaulo * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3189769Srpaulo * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4232090Sjkim * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
5178151Srpaulo * All rights reserved.
6178151Srpaulo *
7178151Srpaulo * Redistribution and use in source and binary forms, with or without
8178151Srpaulo * modification, are permitted provided that the following conditions
9178151Srpaulo * are met:
10178151Srpaulo * 1. Redistributions of source code must retain the above copyright
11178151Srpaulo *    notice, this list of conditions and the following disclaimer.
12178151Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
13178151Srpaulo *    notice, this list of conditions and the following disclaimer in the
14178151Srpaulo *    documentation and/or other materials provided with the distribution.
15178151Srpaulo *
16178151Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17178151Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18178151Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19178151Srpaulo * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20178151Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21178151Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22178151Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23178151Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24178151Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25178151Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26178151Srpaulo * POSSIBILITY OF SUCH DAMAGE.
27178151Srpaulo */
28178151Srpaulo
29178151Srpaulo/*
30232090Sjkim * Driver for the AMD CPU on-die thermal sensors.
31197102Sjkim * Initially based on the k8temp Linux driver.
32178151Srpaulo */
33178151Srpaulo
34178151Srpaulo#include <sys/cdefs.h>
35178151Srpaulo__FBSDID("$FreeBSD$");
36178151Srpaulo
37178151Srpaulo#include <sys/param.h>
38178151Srpaulo#include <sys/bus.h>
39178151Srpaulo#include <sys/conf.h>
40178151Srpaulo#include <sys/kernel.h>
41197102Sjkim#include <sys/module.h>
42178151Srpaulo#include <sys/sysctl.h>
43197102Sjkim#include <sys/systm.h>
44178151Srpaulo
45197205Sjkim#include <machine/cpufunc.h>
46197102Sjkim#include <machine/md_var.h>
47178151Srpaulo#include <machine/specialreg.h>
48178151Srpaulo
49178151Srpaulo#include <dev/pci/pcivar.h>
50232090Sjkim#include <x86/pci_cfgreg.h>
51178151Srpaulo
52189769Srpaulotypedef enum {
53232090Sjkim	CORE0_SENSOR0,
54232090Sjkim	CORE0_SENSOR1,
55232090Sjkim	CORE1_SENSOR0,
56232090Sjkim	CORE1_SENSOR1,
57189769Srpaulo	CORE0,
58189769Srpaulo	CORE1
59189769Srpaulo} amdsensor_t;
60189769Srpaulo
61189769Srpaulostruct amdtemp_softc {
62178151Srpaulo	device_t	sc_dev;
63197102Sjkim	int		sc_ncores;
64178151Srpaulo	int		sc_ntemps;
65197205Sjkim	int		sc_flags;
66232090Sjkim#define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
67232090Sjkim#define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
68232090Sjkim#define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
69232090Sjkim	int32_t		sc_offset;
70197102Sjkim	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
71197102Sjkim	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
72180312Srpaulo	struct intr_config_hook sc_ich;
73178151Srpaulo};
74178151Srpaulo
75197102Sjkim#define	VENDORID_AMD		0x1022
76197102Sjkim#define	DEVICEID_AMD_MISC0F	0x1103
77197102Sjkim#define	DEVICEID_AMD_MISC10	0x1203
78197102Sjkim#define	DEVICEID_AMD_MISC11	0x1303
79254924Sjmg#define	DEVICEID_AMD_MISC12	0x1403
80232090Sjkim#define	DEVICEID_AMD_MISC14	0x1703
81232090Sjkim#define	DEVICEID_AMD_MISC15	0x1603
82263869Sbrueffer#define	DEVICEID_AMD_MISC16	0x1533
83300520Sloos#define	DEVICEID_AMD_MISC16_M30H	0x1583
84273383Sbrueffer#define	DEVICEID_AMD_MISC17	0x141d
85178151Srpaulo
86189769Srpaulostatic struct amdtemp_product {
87189769Srpaulo	uint16_t	amdtemp_vendorid;
88189769Srpaulo	uint16_t	amdtemp_deviceid;
89189769Srpaulo} amdtemp_products[] = {
90189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
91189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
92189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
93254924Sjmg	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
94232090Sjkim	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
95232090Sjkim	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
96263869Sbrueffer	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
97300520Sloos	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H },
98273383Sbrueffer	{ VENDORID_AMD,	DEVICEID_AMD_MISC17 },
99178151Srpaulo	{ 0, 0 }
100178151Srpaulo};
101178151Srpaulo
102178151Srpaulo/*
103232090Sjkim * Reported Temperature Control Register
104178151Srpaulo */
105197102Sjkim#define	AMDTEMP_REPTMP_CTRL	0xa4
106178151Srpaulo
107189769Srpaulo/*
108232090Sjkim * Thermaltrip Status Register (Family 0Fh only)
109189769Srpaulo */
110197102Sjkim#define	AMDTEMP_THERMTP_STAT	0xe4
111232090Sjkim#define	AMDTEMP_TTSR_SELCORE	0x04
112232090Sjkim#define	AMDTEMP_TTSR_SELSENSOR	0x40
113178151Srpaulo
114197102Sjkim/*
115232090Sjkim * DRAM Configuration High Register
116232090Sjkim */
117232090Sjkim#define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
118232090Sjkim#define	AMDTEMP_DRAM_MODE_DDR3	0x0100
119232090Sjkim
120232090Sjkim/*
121197102Sjkim * CPU Family/Model Register
122197102Sjkim */
123197102Sjkim#define	AMDTEMP_CPUID		0xfc
124178151Srpaulo
125178151Srpaulo/*
126178151Srpaulo * Device methods.
127178151Srpaulo */
128189769Srpaulostatic void 	amdtemp_identify(driver_t *driver, device_t parent);
129189769Srpaulostatic int	amdtemp_probe(device_t dev);
130189769Srpaulostatic int	amdtemp_attach(device_t dev);
131189769Srpaulostatic void	amdtemp_intrhook(void *arg);
132189769Srpaulostatic int	amdtemp_detach(device_t dev);
133189769Srpaulostatic int 	amdtemp_match(device_t dev);
134189769Srpaulostatic int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
135189769Srpaulostatic int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
136189769Srpaulostatic int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
137178151Srpaulo
138189769Srpaulostatic device_method_t amdtemp_methods[] = {
139178151Srpaulo	/* Device interface */
140189769Srpaulo	DEVMETHOD(device_identify,	amdtemp_identify),
141189769Srpaulo	DEVMETHOD(device_probe,		amdtemp_probe),
142189769Srpaulo	DEVMETHOD(device_attach,	amdtemp_attach),
143189769Srpaulo	DEVMETHOD(device_detach,	amdtemp_detach),
144178151Srpaulo
145246128Ssbz	DEVMETHOD_END
146178151Srpaulo};
147178151Srpaulo
148189769Srpaulostatic driver_t amdtemp_driver = {
149189769Srpaulo	"amdtemp",
150189769Srpaulo	amdtemp_methods,
151189769Srpaulo	sizeof(struct amdtemp_softc),
152178151Srpaulo};
153178151Srpaulo
154189769Srpaulostatic devclass_t amdtemp_devclass;
155189769SrpauloDRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
156178151Srpaulo
157178151Srpaulostatic int
158189769Srpauloamdtemp_match(device_t dev)
159178151Srpaulo{
160178151Srpaulo	int i;
161178151Srpaulo	uint16_t vendor, devid;
162197102Sjkim
163197102Sjkim	vendor = pci_get_vendor(dev);
164178151Srpaulo	devid = pci_get_device(dev);
165178151Srpaulo
166189769Srpaulo	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
167189769Srpaulo		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
168189769Srpaulo		    devid == amdtemp_products[i].amdtemp_deviceid)
169178151Srpaulo			return (1);
170178151Srpaulo	}
171178151Srpaulo
172178151Srpaulo	return (0);
173178151Srpaulo}
174178151Srpaulo
175178151Srpaulostatic void
176189769Srpauloamdtemp_identify(driver_t *driver, device_t parent)
177178151Srpaulo{
178178151Srpaulo	device_t child;
179178151Srpaulo
180178151Srpaulo	/* Make sure we're not being doubly invoked. */
181189769Srpaulo	if (device_find_child(parent, "amdtemp", -1) != NULL)
182178151Srpaulo		return;
183197102Sjkim
184189769Srpaulo	if (amdtemp_match(parent)) {
185189769Srpaulo		child = device_add_child(parent, "amdtemp", -1);
186178151Srpaulo		if (child == NULL)
187189769Srpaulo			device_printf(parent, "add amdtemp child failed\n");
188178151Srpaulo	}
189178151Srpaulo}
190178151Srpaulo
191178151Srpaulostatic int
192189769Srpauloamdtemp_probe(device_t dev)
193178151Srpaulo{
194197205Sjkim	uint32_t family, model;
195197102Sjkim
196241885Seadler	if (resource_disabled("amdtemp", 0))
197241885Seadler		return (ENXIO);
198241885Seadler
199197205Sjkim	family = CPUID_TO_FAMILY(cpu_id);
200197205Sjkim	model = CPUID_TO_MODEL(cpu_id);
201197102Sjkim
202197102Sjkim	switch (family) {
203197102Sjkim	case 0x0f:
204197205Sjkim		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
205197205Sjkim		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
206197102Sjkim			return (ENXIO);
207197102Sjkim		break;
208197102Sjkim	case 0x10:
209197102Sjkim	case 0x11:
210232090Sjkim	case 0x12:
211232090Sjkim	case 0x14:
212232090Sjkim	case 0x15:
213263869Sbrueffer	case 0x16:
214197102Sjkim		break;
215197102Sjkim	default:
216178151Srpaulo		return (ENXIO);
217178151Srpaulo	}
218197102Sjkim	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
219197102Sjkim
220178151Srpaulo	return (BUS_PROBE_GENERIC);
221178151Srpaulo}
222178151Srpaulo
223178151Srpaulostatic int
224189769Srpauloamdtemp_attach(device_t dev)
225178151Srpaulo{
226232090Sjkim	char tn[32];
227232090Sjkim	u_int regs[4];
228189769Srpaulo	struct amdtemp_softc *sc = device_get_softc(dev);
229178151Srpaulo	struct sysctl_ctx_list *sysctlctx;
230178151Srpaulo	struct sysctl_oid *sysctlnode;
231197102Sjkim	uint32_t cpuid, family, model;
232232090Sjkim	u_int bid;
233232090Sjkim	int erratum319, unit;
234178151Srpaulo
235232090Sjkim	erratum319 = 0;
236180312Srpaulo
237197205Sjkim	/*
238197205Sjkim	 * CPUID Register is available from Revision F.
239197205Sjkim	 */
240232090Sjkim	cpuid = cpu_id;
241232090Sjkim	family = CPUID_TO_FAMILY(cpuid);
242232090Sjkim	model = CPUID_TO_MODEL(cpuid);
243197205Sjkim	if (family != 0x0f || model >= 0x40) {
244197205Sjkim		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
245197205Sjkim		family = CPUID_TO_FAMILY(cpuid);
246197205Sjkim		model = CPUID_TO_MODEL(cpuid);
247197205Sjkim	}
248197205Sjkim
249197102Sjkim	switch (family) {
250197102Sjkim	case 0x0f:
251197102Sjkim		/*
252197205Sjkim		 * Thermaltrip Status Register
253197102Sjkim		 *
254197205Sjkim		 * - ThermSenseCoreSel
255197205Sjkim		 *
256197205Sjkim		 * Revision F & G:	0 - Core1, 1 - Core0
257197205Sjkim		 * Other:		0 - Core0, 1 - Core1
258197205Sjkim		 *
259197205Sjkim		 * - CurTmp
260197205Sjkim		 *
261197102Sjkim		 * Revision G:		bits 23-14
262197205Sjkim		 * Other:		bits 23-16
263197102Sjkim		 *
264197205Sjkim		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
265197205Sjkim		 * ThermSenseCoreSel bits were introduced in Revision F
266197205Sjkim		 * but CurTmp seems working fine as early as Revision C.
267197205Sjkim		 * However, it is not clear whether ThermSenseSel and/or
268197205Sjkim		 * ThermSenseCoreSel work in undocumented cases as well.
269197205Sjkim		 * In fact, the Linux driver suggests it may not work but
270197205Sjkim		 * we just assume it does until we find otherwise.
271232090Sjkim		 *
272232090Sjkim		 * XXX According to Linux, CurTmp starts at -28C on
273232090Sjkim		 * Socket AM2 Revision G processors, which is not
274232090Sjkim		 * documented anywhere.
275197102Sjkim		 */
276232090Sjkim		if (model >= 0x40)
277197205Sjkim			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
278232090Sjkim		if (model >= 0x60 && model != 0xc1) {
279232090Sjkim			do_cpuid(0x80000001, regs);
280232090Sjkim			bid = (regs[1] >> 9) & 0x1f;
281232090Sjkim			switch (model) {
282232090Sjkim			case 0x68: /* Socket S1g1 */
283232090Sjkim			case 0x6c:
284232090Sjkim			case 0x7c:
285232090Sjkim				break;
286232090Sjkim			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
287232090Sjkim				if (bid != 0x0b && bid != 0x0c)
288232090Sjkim					sc->sc_flags |=
289232090Sjkim					    AMDTEMP_FLAG_ALT_OFFSET;
290232090Sjkim				break;
291232090Sjkim			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
292232090Sjkim			case 0x7f:
293232090Sjkim				if (bid != 0x07 && bid != 0x09 &&
294232090Sjkim				    bid != 0x0c)
295232090Sjkim					sc->sc_flags |=
296232090Sjkim					    AMDTEMP_FLAG_ALT_OFFSET;
297232090Sjkim				break;
298232090Sjkim			default:
299232090Sjkim				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
300232090Sjkim			}
301232090Sjkim			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
302197205Sjkim		}
303197102Sjkim
304197102Sjkim		/*
305197102Sjkim		 * There are two sensors per core.
306197102Sjkim		 */
307197102Sjkim		sc->sc_ntemps = 2;
308197102Sjkim
309189769Srpaulo		sc->sc_gettemp = amdtemp_gettemp0f;
310197102Sjkim		break;
311197102Sjkim	case 0x10:
312232090Sjkim		/*
313232090Sjkim		 * Erratum 319 Inaccurate Temperature Measurement
314232090Sjkim		 *
315232090Sjkim		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
316232090Sjkim		 */
317232090Sjkim		do_cpuid(0x80000001, regs);
318232090Sjkim		switch ((regs[1] >> 28) & 0xf) {
319232090Sjkim		case 0:	/* Socket F */
320232090Sjkim			erratum319 = 1;
321232090Sjkim			break;
322232090Sjkim		case 1:	/* Socket AM2+ or AM3 */
323232090Sjkim			if ((pci_cfgregread(pci_get_bus(dev),
324232090Sjkim			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
325232090Sjkim			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
326232090Sjkim			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
327232090Sjkim				break;
328232090Sjkim			/* XXX 00100F42h (RB-C2) exists in both formats. */
329232090Sjkim			erratum319 = 1;
330232090Sjkim			break;
331232090Sjkim		}
332232090Sjkim		/* FALLTHROUGH */
333197102Sjkim	case 0x11:
334232090Sjkim	case 0x12:
335232090Sjkim	case 0x14:
336232090Sjkim	case 0x15:
337263869Sbrueffer	case 0x16:
338197102Sjkim		/*
339197102Sjkim		 * There is only one sensor per package.
340197102Sjkim		 */
341197102Sjkim		sc->sc_ntemps = 1;
342197102Sjkim
343189769Srpaulo		sc->sc_gettemp = amdtemp_gettemp;
344197102Sjkim		break;
345189769Srpaulo	}
346189769Srpaulo
347197102Sjkim	/* Find number of cores per package. */
348197102Sjkim	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
349197102Sjkim	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
350197102Sjkim	if (sc->sc_ncores > MAXCPU)
351197102Sjkim		return (ENXIO);
352197102Sjkim
353232090Sjkim	if (erratum319)
354232090Sjkim		device_printf(dev,
355232090Sjkim		    "Erratum 319: temperature measurement may be inaccurate\n");
356197102Sjkim	if (bootverbose)
357197102Sjkim		device_printf(dev, "Found %d cores and %d sensors.\n",
358197102Sjkim		    sc->sc_ncores,
359197102Sjkim		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
360197102Sjkim
361178151Srpaulo	/*
362189769Srpaulo	 * dev.amdtemp.N tree.
363178151Srpaulo	 */
364232090Sjkim	unit = device_get_unit(dev);
365232090Sjkim	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
366232090Sjkim	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
367232090Sjkim
368178151Srpaulo	sysctlctx = device_get_sysctl_ctx(dev);
369232090Sjkim	SYSCTL_ADD_INT(sysctlctx,
370232090Sjkim	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
371232090Sjkim	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
372232090Sjkim	    "Temperature sensor offset");
373178151Srpaulo	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
374197102Sjkim	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
375232090Sjkim	    "core0", CTLFLAG_RD, 0, "Core 0");
376197102Sjkim
377178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
378178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
379232090Sjkim	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
380232090Sjkim	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
381232090Sjkim	    "Core 0 / Sensor 0 temperature");
382178151Srpaulo
383197102Sjkim	if (sc->sc_ntemps > 1) {
384197102Sjkim		SYSCTL_ADD_PROC(sysctlctx,
385197102Sjkim		    SYSCTL_CHILDREN(sysctlnode),
386232090Sjkim		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
387232090Sjkim		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
388232090Sjkim		    "Core 0 / Sensor 1 temperature");
389197102Sjkim
390232090Sjkim		if (sc->sc_ncores > 1) {
391232090Sjkim			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
392232090Sjkim			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
393232090Sjkim			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
394232090Sjkim
395197102Sjkim			SYSCTL_ADD_PROC(sysctlctx,
396197102Sjkim			    SYSCTL_CHILDREN(sysctlnode),
397232090Sjkim			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
398232090Sjkim			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
399232090Sjkim			    "Core 1 / Sensor 0 temperature");
400232090Sjkim
401232090Sjkim			SYSCTL_ADD_PROC(sysctlctx,
402232090Sjkim			    SYSCTL_CHILDREN(sysctlnode),
403232090Sjkim			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
404232090Sjkim			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
405232090Sjkim			    "Core 1 / Sensor 1 temperature");
406232090Sjkim		}
407197102Sjkim	}
408197102Sjkim
409197102Sjkim	/*
410197102Sjkim	 * Try to create dev.cpu sysctl entries and setup intrhook function.
411197102Sjkim	 * This is needed because the cpu driver may be loaded late on boot,
412197102Sjkim	 * after us.
413197102Sjkim	 */
414197102Sjkim	amdtemp_intrhook(dev);
415197102Sjkim	sc->sc_ich.ich_func = amdtemp_intrhook;
416197102Sjkim	sc->sc_ich.ich_arg = dev;
417197102Sjkim	if (config_intrhook_establish(&sc->sc_ich) != 0) {
418197102Sjkim		device_printf(dev, "config_intrhook_establish failed!\n");
419197102Sjkim		return (ENXIO);
420197102Sjkim	}
421197102Sjkim
422178151Srpaulo	return (0);
423178151Srpaulo}
424178151Srpaulo
425180312Srpaulovoid
426189769Srpauloamdtemp_intrhook(void *arg)
427180312Srpaulo{
428189769Srpaulo	struct amdtemp_softc *sc;
429180312Srpaulo	struct sysctl_ctx_list *sysctlctx;
430197102Sjkim	device_t dev = (device_t)arg;
431197102Sjkim	device_t acpi, cpu, nexus;
432197102Sjkim	amdsensor_t sensor;
433197102Sjkim	int i;
434180312Srpaulo
435180312Srpaulo	sc = device_get_softc(dev);
436197102Sjkim
437180312Srpaulo	/*
438180312Srpaulo	 * dev.cpu.N.temperature.
439180312Srpaulo	 */
440180312Srpaulo	nexus = device_find_child(root_bus, "nexus", 0);
441180312Srpaulo	acpi = device_find_child(nexus, "acpi", 0);
442180312Srpaulo
443197102Sjkim	for (i = 0; i < sc->sc_ncores; i++) {
444197102Sjkim		if (sc->sc_sysctl_cpu[i] != NULL)
445197102Sjkim			continue;
446180312Srpaulo		cpu = device_find_child(acpi, "cpu",
447197102Sjkim		    device_get_unit(dev) * sc->sc_ncores + i);
448197102Sjkim		if (cpu != NULL) {
449180312Srpaulo			sysctlctx = device_get_sysctl_ctx(cpu);
450180312Srpaulo
451197102Sjkim			sensor = sc->sc_ntemps > 1 ?
452232090Sjkim			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
453180312Srpaulo			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
454180312Srpaulo			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
455180312Srpaulo			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
456197102Sjkim			    dev, sensor, amdtemp_sysctl, "IK",
457197102Sjkim			    "Current temparature");
458180312Srpaulo		}
459180312Srpaulo	}
460197102Sjkim	if (sc->sc_ich.ich_arg != NULL)
461197102Sjkim		config_intrhook_disestablish(&sc->sc_ich);
462180312Srpaulo}
463180312Srpaulo
464178151Srpauloint
465189769Srpauloamdtemp_detach(device_t dev)
466178151Srpaulo{
467197102Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
468178151Srpaulo	int i;
469197102Sjkim
470197102Sjkim	for (i = 0; i < sc->sc_ncores; i++)
471197102Sjkim		if (sc->sc_sysctl_cpu[i] != NULL)
472178151Srpaulo			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
473178151Srpaulo
474189769Srpaulo	/* NewBus removes the dev.amdtemp.N tree by itself. */
475197102Sjkim
476178151Srpaulo	return (0);
477178151Srpaulo}
478178151Srpaulo
479178151Srpaulostatic int
480189769Srpauloamdtemp_sysctl(SYSCTL_HANDLER_ARGS)
481178151Srpaulo{
482197102Sjkim	device_t dev = (device_t)arg1;
483189769Srpaulo	struct amdtemp_softc *sc = device_get_softc(dev);
484197102Sjkim	amdsensor_t sensor = (amdsensor_t)arg2;
485197102Sjkim	int32_t auxtemp[2], temp;
486178151Srpaulo	int error;
487178151Srpaulo
488197102Sjkim	switch (sensor) {
489178151Srpaulo	case CORE0:
490232090Sjkim		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
491232090Sjkim		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
492178988Srpaulo		temp = imax(auxtemp[0], auxtemp[1]);
493178151Srpaulo		break;
494178151Srpaulo	case CORE1:
495232090Sjkim		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
496232090Sjkim		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
497178988Srpaulo		temp = imax(auxtemp[0], auxtemp[1]);
498178151Srpaulo		break;
499178151Srpaulo	default:
500197102Sjkim		temp = sc->sc_gettemp(dev, sensor);
501178151Srpaulo		break;
502178151Srpaulo	}
503178151Srpaulo	error = sysctl_handle_int(oidp, &temp, 0, req);
504197102Sjkim
505178151Srpaulo	return (error);
506178151Srpaulo}
507178151Srpaulo
508197102Sjkim#define	AMDTEMP_ZERO_C_TO_K	2732
509197102Sjkim
510178151Srpaulostatic int32_t
511189769Srpauloamdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
512178151Srpaulo{
513197102Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
514232090Sjkim	uint32_t mask, offset, temp;
515197102Sjkim
516197102Sjkim	/* Set Sensor/Core selector. */
517232090Sjkim	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
518232090Sjkim	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
519178151Srpaulo	switch (sensor) {
520232090Sjkim	case CORE0_SENSOR1:
521232090Sjkim		temp |= AMDTEMP_TTSR_SELSENSOR;
522197103Sjkim		/* FALLTHROUGH */
523232090Sjkim	case CORE0_SENSOR0:
524197102Sjkim	case CORE0:
525197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
526232090Sjkim			temp |= AMDTEMP_TTSR_SELCORE;
527178151Srpaulo		break;
528232090Sjkim	case CORE1_SENSOR1:
529232090Sjkim		temp |= AMDTEMP_TTSR_SELSENSOR;
530197103Sjkim		/* FALLTHROUGH */
531232090Sjkim	case CORE1_SENSOR0:
532197102Sjkim	case CORE1:
533197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
534232090Sjkim			temp |= AMDTEMP_TTSR_SELCORE;
535178151Srpaulo		break;
536178151Srpaulo	}
537232090Sjkim	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
538197102Sjkim
539232090Sjkim	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
540232090Sjkim	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
541197102Sjkim	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
542232090Sjkim	temp = ((temp >> 14) & mask) * 5 / 2;
543232090Sjkim	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
544197102Sjkim
545178151Srpaulo	return (temp);
546178151Srpaulo}
547189769Srpaulo
548189769Srpaulostatic int32_t
549189769Srpauloamdtemp_gettemp(device_t dev, amdsensor_t sensor)
550189769Srpaulo{
551232090Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
552189769Srpaulo	uint32_t temp;
553189769Srpaulo
554197102Sjkim	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
555232090Sjkim	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
556232090Sjkim	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
557197102Sjkim
558189769Srpaulo	return (temp);
559189769Srpaulo}
560