amdtemp.c revision 263169
133965Sjdp/*-
278828Sobrien * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
389857Sobrien * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
438889Sjdp * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
533965Sjdp * All rights reserved.
633965Sjdp *
733965Sjdp * Redistribution and use in source and binary forms, with or without
833965Sjdp * modification, are permitted provided that the following conditions
933965Sjdp * are met:
1033965Sjdp * 1. Redistributions of source code must retain the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer.
1233965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1333965Sjdp *    notice, this list of conditions and the following disclaimer in the
1433965Sjdp *    documentation and/or other materials provided with the distribution.
1533965Sjdp *
1633965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1733965Sjdp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1833965Sjdp * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1933965Sjdp * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2033965Sjdp * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2133965Sjdp * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2233965Sjdp * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2333965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2433965Sjdp * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2533965Sjdp * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2633965Sjdp * POSSIBILITY OF SUCH DAMAGE.
2733965Sjdp */
2833965Sjdp
2977298Sobrien/*
3033965Sjdp * Driver for the AMD CPU on-die thermal sensors.
3133965Sjdp * Initially based on the k8temp Linux driver.
3233965Sjdp */
3333965Sjdp
3433965Sjdp#include <sys/cdefs.h>
3533965Sjdp__FBSDID("$FreeBSD: head/sys/dev/amdtemp/amdtemp.c 263169 2014-03-14 12:15:28Z brueffer $");
3633965Sjdp
3733965Sjdp#include <sys/param.h>
3877298Sobrien#include <sys/bus.h>
3933965Sjdp#include <sys/conf.h>
4033965Sjdp#include <sys/kernel.h>
4133965Sjdp#include <sys/module.h>
4233965Sjdp#include <sys/sysctl.h>
4333965Sjdp#include <sys/systm.h>
4433965Sjdp
4533965Sjdp#include <machine/cpufunc.h>
4677298Sobrien#include <machine/md_var.h>
4733965Sjdp#include <machine/specialreg.h>
4833965Sjdp
4933965Sjdp#include <dev/pci/pcivar.h>
5077298Sobrien#include <x86/pci_cfgreg.h>
5133965Sjdp
5233965Sjdptypedef enum {
5333965Sjdp	CORE0_SENSOR0,
5433965Sjdp	CORE0_SENSOR1,
5533965Sjdp	CORE1_SENSOR0,
5633965Sjdp	CORE1_SENSOR1,
5733965Sjdp	CORE0,
5833965Sjdp	CORE1
5933965Sjdp} amdsensor_t;
6077298Sobrien
6133965Sjdpstruct amdtemp_softc {
6233965Sjdp	device_t	sc_dev;
6333965Sjdp	int		sc_ncores;
6433965Sjdp	int		sc_ntemps;
6533965Sjdp	int		sc_flags;
6633965Sjdp#define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
6733965Sjdp#define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
6833965Sjdp#define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
6933965Sjdp	int32_t		sc_offset;
7077298Sobrien	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
7133965Sjdp	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
7233965Sjdp	struct intr_config_hook sc_ich;
7333965Sjdp};
7433965Sjdp
7533965Sjdp#define	VENDORID_AMD		0x1022
7633965Sjdp#define	DEVICEID_AMD_MISC0F	0x1103
7733965Sjdp#define	DEVICEID_AMD_MISC10	0x1203
7833965Sjdp#define	DEVICEID_AMD_MISC11	0x1303
7933965Sjdp#define	DEVICEID_AMD_MISC12	0x1403
8033965Sjdp#define	DEVICEID_AMD_MISC14	0x1703
8133965Sjdp#define	DEVICEID_AMD_MISC15	0x1603
8233965Sjdp#define	DEVICEID_AMD_MISC16	0x1533
8333965Sjdp
8433965Sjdpstatic struct amdtemp_product {
8533965Sjdp	uint16_t	amdtemp_vendorid;
8633965Sjdp	uint16_t	amdtemp_deviceid;
8733965Sjdp} amdtemp_products[] = {
8833965Sjdp	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
8933965Sjdp	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
9077298Sobrien	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
9133965Sjdp	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
9233965Sjdp	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
9377298Sobrien	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
9433965Sjdp	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
9533965Sjdp	{ 0, 0 }
9633965Sjdp};
9733965Sjdp
9833965Sjdp/*
9933965Sjdp * Reported Temperature Control Register
10077298Sobrien */
10133965Sjdp#define	AMDTEMP_REPTMP_CTRL	0xa4
10233965Sjdp
10333965Sjdp/*
10433965Sjdp * Thermaltrip Status Register (Family 0Fh only)
10533965Sjdp */
10633965Sjdp#define	AMDTEMP_THERMTP_STAT	0xe4
10733965Sjdp#define	AMDTEMP_TTSR_SELCORE	0x04
10833965Sjdp#define	AMDTEMP_TTSR_SELSENSOR	0x40
10933965Sjdp
11033965Sjdp/*
11133965Sjdp * DRAM Configuration High Register
11233965Sjdp */
11333965Sjdp#define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
11433965Sjdp#define	AMDTEMP_DRAM_MODE_DDR3	0x0100
11533965Sjdp
11633965Sjdp/*
11733965Sjdp * CPU Family/Model Register
11833965Sjdp */
11977298Sobrien#define	AMDTEMP_CPUID		0xfc
12033965Sjdp
12133965Sjdp/*
12233965Sjdp * Device methods.
12333965Sjdp */
12433965Sjdpstatic void 	amdtemp_identify(driver_t *driver, device_t parent);
12577298Sobrienstatic int	amdtemp_probe(device_t dev);
12633965Sjdpstatic int	amdtemp_attach(device_t dev);
12733965Sjdpstatic void	amdtemp_intrhook(void *arg);
12891041Sobrienstatic int	amdtemp_detach(device_t dev);
12933965Sjdpstatic int 	amdtemp_match(device_t dev);
13033965Sjdpstatic int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
13133965Sjdpstatic int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
13233965Sjdpstatic int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
13333965Sjdp
13433965Sjdpstatic device_method_t amdtemp_methods[] = {
13533965Sjdp	/* Device interface */
13677298Sobrien	DEVMETHOD(device_identify,	amdtemp_identify),
13733965Sjdp	DEVMETHOD(device_probe,		amdtemp_probe),
13891041Sobrien	DEVMETHOD(device_attach,	amdtemp_attach),
13991041Sobrien	DEVMETHOD(device_detach,	amdtemp_detach),
14033965Sjdp
14133965Sjdp	DEVMETHOD_END
14233965Sjdp};
14333965Sjdp
14477298Sobrienstatic driver_t amdtemp_driver = {
14533965Sjdp	"amdtemp",
14633965Sjdp	amdtemp_methods,
14733965Sjdp	sizeof(struct amdtemp_softc),
14833965Sjdp};
14933965Sjdp
15033965Sjdpstatic devclass_t amdtemp_devclass;
15133965SjdpDRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
15233965Sjdp
15333965Sjdpstatic int
15433965Sjdpamdtemp_match(device_t dev)
15533965Sjdp{
15660484Sobrien	int i;
15789857Sobrien	uint16_t vendor, devid;
15889857Sobrien
15933965Sjdp	vendor = pci_get_vendor(dev);
16033965Sjdp	devid = pci_get_device(dev);
16133965Sjdp
16233965Sjdp	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
16333965Sjdp		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
16433965Sjdp		    devid == amdtemp_products[i].amdtemp_deviceid)
16533965Sjdp			return (1);
16633965Sjdp	}
16733965Sjdp
16891041Sobrien	return (0);
16933965Sjdp}
17091041Sobrien
17191041Sobrienstatic void
17291041Sobrienamdtemp_identify(driver_t *driver, device_t parent)
17333965Sjdp{
17491041Sobrien	device_t child;
17591041Sobrien
17633965Sjdp	/* Make sure we're not being doubly invoked. */
17791041Sobrien	if (device_find_child(parent, "amdtemp", -1) != NULL)
17891041Sobrien		return;
17933965Sjdp
18091041Sobrien	if (amdtemp_match(parent)) {
18191041Sobrien		child = device_add_child(parent, "amdtemp", -1);
18291041Sobrien		if (child == NULL)
18377298Sobrien			device_printf(parent, "add amdtemp child failed\n");
18491041Sobrien	}
18591041Sobrien}
18691041Sobrien
18733965Sjdpstatic int
18891041Sobrienamdtemp_probe(device_t dev)
18991041Sobrien{
19091041Sobrien	uint32_t family, model;
19133965Sjdp
19291041Sobrien	if (resource_disabled("amdtemp", 0))
19391041Sobrien		return (ENXIO);
19477298Sobrien
19591041Sobrien	family = CPUID_TO_FAMILY(cpu_id);
19691041Sobrien	model = CPUID_TO_MODEL(cpu_id);
19733965Sjdp
19891041Sobrien	switch (family) {
19991041Sobrien	case 0x0f:
20091041Sobrien		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
20191041Sobrien		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
20289857Sobrien			return (ENXIO);
20333965Sjdp		break;
20489857Sobrien	case 0x10:
20589857Sobrien	case 0x11:
20633965Sjdp	case 0x12:
20789857Sobrien	case 0x14:
20889857Sobrien	case 0x15:
20933965Sjdp	case 0x16:
21089857Sobrien		break;
21191041Sobrien	default:
21291041Sobrien		return (ENXIO);
21389857Sobrien	}
21433965Sjdp	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
21589857Sobrien
21689857Sobrien	return (BUS_PROBE_GENERIC);
21733965Sjdp}
21889857Sobrien
21989857Sobrienstatic int
22033965Sjdpamdtemp_attach(device_t dev)
22189857Sobrien{
22291041Sobrien	char tn[32];
22391041Sobrien	u_int regs[4];
22491041Sobrien	struct amdtemp_softc *sc = device_get_softc(dev);
22591041Sobrien	struct sysctl_ctx_list *sysctlctx;
22691041Sobrien	struct sysctl_oid *sysctlnode;
22733965Sjdp	uint32_t cpuid, family, model;
22891041Sobrien	u_int bid;
22991041Sobrien	int erratum319, unit;
23089857Sobrien
23191041Sobrien	erratum319 = 0;
23291041Sobrien
23389857Sobrien	/*
23491041Sobrien	 * CPUID Register is available from Revision F.
23560484Sobrien	 */
23660484Sobrien	cpuid = cpu_id;
23733965Sjdp	family = CPUID_TO_FAMILY(cpuid);
23833965Sjdp	model = CPUID_TO_MODEL(cpuid);
23989857Sobrien	if (family != 0x0f || model >= 0x40) {
24089857Sobrien		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
24189857Sobrien		family = CPUID_TO_FAMILY(cpuid);
24289857Sobrien		model = CPUID_TO_MODEL(cpuid);
24389857Sobrien	}
24489857Sobrien
24589857Sobrien	switch (family) {
24689857Sobrien	case 0x0f:
24789857Sobrien		/*
24833965Sjdp		 * Thermaltrip Status Register
24933965Sjdp		 *
25089857Sobrien		 * - ThermSenseCoreSel
25133965Sjdp		 *
25289857Sobrien		 * Revision F & G:	0 - Core1, 1 - Core0
25333965Sjdp		 * Other:		0 - Core0, 1 - Core1
25489857Sobrien		 *
25533965Sjdp		 * - CurTmp
25689857Sobrien		 *
25789857Sobrien		 * Revision G:		bits 23-14
25889857Sobrien		 * Other:		bits 23-16
25989857Sobrien		 *
26033965Sjdp		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
26133965Sjdp		 * ThermSenseCoreSel bits were introduced in Revision F
26289857Sobrien		 * but CurTmp seems working fine as early as Revision C.
26389857Sobrien		 * However, it is not clear whether ThermSenseSel and/or
26489857Sobrien		 * ThermSenseCoreSel work in undocumented cases as well.
26589857Sobrien		 * In fact, the Linux driver suggests it may not work but
26689857Sobrien		 * we just assume it does until we find otherwise.
26789857Sobrien		 *
26889857Sobrien		 * XXX According to Linux, CurTmp starts at -28C on
26933965Sjdp		 * Socket AM2 Revision G processors, which is not
27033965Sjdp		 * documented anywhere.
27189857Sobrien		 */
27233965Sjdp		if (model >= 0x40)
27333965Sjdp			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
27489857Sobrien		if (model >= 0x60 && model != 0xc1) {
27533965Sjdp			do_cpuid(0x80000001, regs);
27633965Sjdp			bid = (regs[1] >> 9) & 0x1f;
27789857Sobrien			switch (model) {
27889857Sobrien			case 0x68: /* Socket S1g1 */
27977298Sobrien			case 0x6c:
28033965Sjdp			case 0x7c:
28189857Sobrien				break;
28289857Sobrien			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
28391041Sobrien				if (bid != 0x0b && bid != 0x0c)
28489857Sobrien					sc->sc_flags |=
28533965Sjdp					    AMDTEMP_FLAG_ALT_OFFSET;
28691041Sobrien				break;
28789857Sobrien			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
28833965Sjdp			case 0x7f:
28933965Sjdp				if (bid != 0x07 && bid != 0x09 &&
29089857Sobrien				    bid != 0x0c)
29189857Sobrien					sc->sc_flags |=
29289857Sobrien					    AMDTEMP_FLAG_ALT_OFFSET;
29389857Sobrien				break;
29433965Sjdp			default:
29533965Sjdp				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
29633965Sjdp			}
29733965Sjdp			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
29833965Sjdp		}
29989857Sobrien
30089857Sobrien		/*
30189857Sobrien		 * There are two sensors per core.
30289857Sobrien		 */
30389857Sobrien		sc->sc_ntemps = 2;
30489857Sobrien
30589857Sobrien		sc->sc_gettemp = amdtemp_gettemp0f;
30689857Sobrien		break;
30789857Sobrien	case 0x10:
30889857Sobrien		/*
30989857Sobrien		 * Erratum 319 Inaccurate Temperature Measurement
31033965Sjdp		 *
31133965Sjdp		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
31233965Sjdp		 */
31389857Sobrien		do_cpuid(0x80000001, regs);
31489857Sobrien		switch ((regs[1] >> 28) & 0xf) {
31589857Sobrien		case 0:	/* Socket F */
31689857Sobrien			erratum319 = 1;
31789857Sobrien			break;
31889857Sobrien		case 1:	/* Socket AM2+ or AM3 */
31933965Sjdp			if ((pci_cfgregread(pci_get_bus(dev),
32033965Sjdp			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
32133965Sjdp			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
32233965Sjdp			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
32333965Sjdp				break;
32433965Sjdp			/* XXX 00100F42h (RB-C2) exists in both formats. */
32589857Sobrien			erratum319 = 1;
32689857Sobrien			break;
32789857Sobrien		}
32889857Sobrien		/* FALLTHROUGH */
32989857Sobrien	case 0x11:
33089857Sobrien	case 0x12:
33189857Sobrien	case 0x14:
33289857Sobrien	case 0x15:
33389857Sobrien	case 0x16:
33489857Sobrien		/*
33589857Sobrien		 * There is only one sensor per package.
33689857Sobrien		 */
33789857Sobrien		sc->sc_ntemps = 1;
33889857Sobrien
33989857Sobrien		sc->sc_gettemp = amdtemp_gettemp;
34089857Sobrien		break;
34189857Sobrien	}
34289857Sobrien
34389857Sobrien	/* Find number of cores per package. */
34489857Sobrien	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
34533965Sjdp	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
34689857Sobrien	if (sc->sc_ncores > MAXCPU)
34789857Sobrien		return (ENXIO);
34889857Sobrien
34933965Sjdp	if (erratum319)
35089857Sobrien		device_printf(dev,
35133965Sjdp		    "Erratum 319: temperature measurement may be inaccurate\n");
35289857Sobrien	if (bootverbose)
35389857Sobrien		device_printf(dev, "Found %d cores and %d sensors.\n",
35489857Sobrien		    sc->sc_ncores,
35589857Sobrien		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
35633965Sjdp
35733965Sjdp	/*
35833965Sjdp	 * dev.amdtemp.N tree.
35989857Sobrien	 */
36089857Sobrien	unit = device_get_unit(dev);
36133965Sjdp	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
36233965Sjdp	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
36389857Sobrien
36489857Sobrien	sysctlctx = device_get_sysctl_ctx(dev);
36533965Sjdp	SYSCTL_ADD_INT(sysctlctx,
36633965Sjdp	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
36733965Sjdp	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
36833965Sjdp	    "Temperature sensor offset");
36933965Sjdp	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
37033965Sjdp	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
37189857Sobrien	    "core0", CTLFLAG_RD, 0, "Core 0");
37289857Sobrien
37389857Sobrien	SYSCTL_ADD_PROC(sysctlctx,
37489857Sobrien	    SYSCTL_CHILDREN(sysctlnode),
37589857Sobrien	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
37689857Sobrien	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
37789857Sobrien	    "Core 0 / Sensor 0 temperature");
37833965Sjdp
37933965Sjdp	if (sc->sc_ntemps > 1) {
38089857Sobrien		SYSCTL_ADD_PROC(sysctlctx,
38133965Sjdp		    SYSCTL_CHILDREN(sysctlnode),
38233965Sjdp		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
38389857Sobrien		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
38489857Sobrien		    "Core 0 / Sensor 1 temperature");
38589857Sobrien
38689857Sobrien		if (sc->sc_ncores > 1) {
38789857Sobrien			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
38889857Sobrien			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
38989857Sobrien			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
39033965Sjdp
39133965Sjdp			SYSCTL_ADD_PROC(sysctlctx,
39289857Sobrien			    SYSCTL_CHILDREN(sysctlnode),
39389857Sobrien			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
39489857Sobrien			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
39589857Sobrien			    "Core 1 / Sensor 0 temperature");
39689857Sobrien
39789857Sobrien			SYSCTL_ADD_PROC(sysctlctx,
39889857Sobrien			    SYSCTL_CHILDREN(sysctlnode),
39989857Sobrien			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
40089857Sobrien			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
40189857Sobrien			    "Core 1 / Sensor 1 temperature");
40289857Sobrien		}
40389857Sobrien	}
40489857Sobrien
40589857Sobrien	/*
40633965Sjdp	 * Try to create dev.cpu sysctl entries and setup intrhook function.
40789857Sobrien	 * This is needed because the cpu driver may be loaded late on boot,
40889857Sobrien	 * after us.
40933965Sjdp	 */
41033965Sjdp	amdtemp_intrhook(dev);
41133965Sjdp	sc->sc_ich.ich_func = amdtemp_intrhook;
41233965Sjdp	sc->sc_ich.ich_arg = dev;
41333965Sjdp	if (config_intrhook_establish(&sc->sc_ich) != 0) {
41433965Sjdp		device_printf(dev, "config_intrhook_establish failed!\n");
41589857Sobrien		return (ENXIO);
41633965Sjdp	}
41733965Sjdp
41833965Sjdp	return (0);
41989857Sobrien}
42033965Sjdp
42133965Sjdpvoid
42289857Sobrienamdtemp_intrhook(void *arg)
42333965Sjdp{
42460484Sobrien	struct amdtemp_softc *sc;
42589857Sobrien	struct sysctl_ctx_list *sysctlctx;
42660484Sobrien	device_t dev = (device_t)arg;
42789857Sobrien	device_t acpi, cpu, nexus;
42889857Sobrien	amdsensor_t sensor;
42989857Sobrien	int i;
43060484Sobrien
43189857Sobrien	sc = device_get_softc(dev);
43289857Sobrien
43389857Sobrien	/*
43489857Sobrien	 * dev.cpu.N.temperature.
43589857Sobrien	 */
43691041Sobrien	nexus = device_find_child(root_bus, "nexus", 0);
43789857Sobrien	acpi = device_find_child(nexus, "acpi", 0);
43833965Sjdp
43989857Sobrien	for (i = 0; i < sc->sc_ncores; i++) {
44033965Sjdp		if (sc->sc_sysctl_cpu[i] != NULL)
44133965Sjdp			continue;
44289857Sobrien		cpu = device_find_child(acpi, "cpu",
44333965Sjdp		    device_get_unit(dev) * sc->sc_ncores + i);
44489857Sobrien		if (cpu != NULL) {
44533965Sjdp			sysctlctx = device_get_sysctl_ctx(cpu);
44633965Sjdp
44733965Sjdp			sensor = sc->sc_ntemps > 1 ?
44860484Sobrien			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
44960484Sobrien			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
45060484Sobrien			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
45160484Sobrien			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
45260484Sobrien			    dev, sensor, amdtemp_sysctl, "IK",
45360484Sobrien			    "Current temparature");
45491041Sobrien		}
45591041Sobrien	}
45677298Sobrien	if (sc->sc_ich.ich_arg != NULL)
45760484Sobrien		config_intrhook_disestablish(&sc->sc_ich);
45891041Sobrien}
45991041Sobrien
46091041Sobrienint
46177298Sobrienamdtemp_detach(device_t dev)
46233965Sjdp{
46391041Sobrien	struct amdtemp_softc *sc = device_get_softc(dev);
46433965Sjdp	int i;
46533965Sjdp
46633965Sjdp	for (i = 0; i < sc->sc_ncores; i++)
46733965Sjdp		if (sc->sc_sysctl_cpu[i] != NULL)
46833965Sjdp			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
46933965Sjdp
47033965Sjdp	/* NewBus removes the dev.amdtemp.N tree by itself. */
47133965Sjdp
47277298Sobrien	return (0);
47333965Sjdp}
47433965Sjdp
47533965Sjdpstatic int
47633965Sjdpamdtemp_sysctl(SYSCTL_HANDLER_ARGS)
47733965Sjdp{
47877298Sobrien	device_t dev = (device_t)arg1;
47977298Sobrien	struct amdtemp_softc *sc = device_get_softc(dev);
48077298Sobrien	amdsensor_t sensor = (amdsensor_t)arg2;
48177298Sobrien	int32_t auxtemp[2], temp;
48277298Sobrien	int error;
48333965Sjdp
48460484Sobrien	switch (sensor) {
48577298Sobrien	case CORE0:
48633965Sjdp		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
48777298Sobrien		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
48833965Sjdp		temp = imax(auxtemp[0], auxtemp[1]);
48933965Sjdp		break;
49033965Sjdp	case CORE1:
49177298Sobrien		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
49277298Sobrien		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
49360484Sobrien		temp = imax(auxtemp[0], auxtemp[1]);
49477298Sobrien		break;
49538889Sjdp	default:
49677298Sobrien		temp = sc->sc_gettemp(dev, sensor);
49760484Sobrien		break;
49833965Sjdp	}
49977298Sobrien	error = sysctl_handle_int(oidp, &temp, 0, req);
50033965Sjdp
50160484Sobrien	return (error);
50277298Sobrien}
50389857Sobrien
50477298Sobrien#define	AMDTEMP_ZERO_C_TO_K	2732
50533965Sjdp
50660484Sobrienstatic int32_t
50733965Sjdpamdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
50877298Sobrien{
50933965Sjdp	struct amdtemp_softc *sc = device_get_softc(dev);
51060484Sobrien	uint32_t mask, offset, temp;
51177298Sobrien
51289857Sobrien	/* Set Sensor/Core selector. */
51333965Sjdp	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
51477298Sobrien	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
51577298Sobrien	switch (sensor) {
51677298Sobrien	case CORE0_SENSOR1:
51733965Sjdp		temp |= AMDTEMP_TTSR_SELSENSOR;
51833965Sjdp		/* FALLTHROUGH */
51977298Sobrien	case CORE0_SENSOR0:
52077298Sobrien	case CORE0:
52133965Sjdp		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
52233965Sjdp			temp |= AMDTEMP_TTSR_SELCORE;
52377298Sobrien		break;
52477298Sobrien	case CORE1_SENSOR1:
52533965Sjdp		temp |= AMDTEMP_TTSR_SELSENSOR;
52633965Sjdp		/* FALLTHROUGH */
52789857Sobrien	case CORE1_SENSOR0:
52891041Sobrien	case CORE1:
52960484Sobrien		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
53060484Sobrien			temp |= AMDTEMP_TTSR_SELCORE;
53133965Sjdp		break;
53233965Sjdp	}
53389857Sobrien	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
53433965Sjdp
53577298Sobrien	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
53633965Sjdp	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
53777298Sobrien	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
53889857Sobrien	temp = ((temp >> 14) & mask) * 5 / 2;
53989857Sobrien	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
54033965Sjdp
54177298Sobrien	return (temp);
54277298Sobrien}
54377298Sobrien
54438889Sjdpstatic int32_t
54589857Sobrienamdtemp_gettemp(device_t dev, amdsensor_t sensor)
54677298Sobrien{
54733965Sjdp	struct amdtemp_softc *sc = device_get_softc(dev);
54877298Sobrien	uint32_t temp;
54977298Sobrien
55077298Sobrien	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
55189857Sobrien	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
55289857Sobrien	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
55377298Sobrien
55489857Sobrien	return (temp);
55577298Sobrien}
55633965Sjdp