1178151Srpaulo/*-
2189769Srpaulo * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3189769Srpaulo * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4197102Sjkim * Copyright (c) 2009 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/*
30197102Sjkim * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs.
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>
50178151Srpaulo
51189769Srpaulotypedef enum {
52189769Srpaulo	SENSOR0_CORE0,
53189769Srpaulo	SENSOR0_CORE1,
54189769Srpaulo	SENSOR1_CORE0,
55189769Srpaulo	SENSOR1_CORE1,
56189769Srpaulo	CORE0,
57189769Srpaulo	CORE1
58189769Srpaulo} amdsensor_t;
59189769Srpaulo
60189769Srpaulostruct amdtemp_softc {
61178151Srpaulo	device_t	sc_dev;
62197102Sjkim	int		sc_ncores;
63178151Srpaulo	int		sc_ntemps;
64197205Sjkim	int		sc_flags;
65197205Sjkim#define	AMDTEMP_FLAG_DO_QUIRK	0x01	/* DiodeOffset may be incorrect. */
66197205Sjkim#define	AMDTEMP_FLAG_DO_ZERO	0x02	/* DiodeOffset starts from 0C. */
67197205Sjkim#define	AMDTEMP_FLAG_DO_SIGN	0x04	/* DiodeOffsetSignBit is present. */
68197205Sjkim#define	AMDTEMP_FLAG_CS_SWAP	0x08	/* ThermSenseCoreSel is inverted. */
69197205Sjkim#define	AMDTEMP_FLAG_CT_10BIT	0x10	/* CurTmp is 10-bit wide. */
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
79263870Sbrueffer#define	DEVICEID_AMD_MISC16	0x1533
80178151Srpaulo
81189769Srpaulostatic struct amdtemp_product {
82189769Srpaulo	uint16_t	amdtemp_vendorid;
83189769Srpaulo	uint16_t	amdtemp_deviceid;
84189769Srpaulo} amdtemp_products[] = {
85189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
86189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
87189769Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
88263870Sbrueffer	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
89178151Srpaulo	{ 0, 0 }
90178151Srpaulo};
91178151Srpaulo
92178151Srpaulo/*
93197102Sjkim * Reported Temperature Control Register (Family 10h/11h only)
94178151Srpaulo */
95197102Sjkim#define	AMDTEMP_REPTMP_CTRL	0xa4
96178151Srpaulo
97189769Srpaulo/*
98197102Sjkim * Thermaltrip Status Register
99189769Srpaulo */
100197102Sjkim#define	AMDTEMP_THERMTP_STAT	0xe4
101197102Sjkim#define	AMDTEMP_TTSR_SELCORE	0x04	/* Family 0Fh only */
102197102Sjkim#define	AMDTEMP_TTSR_SELSENSOR	0x40	/* Family 0Fh only */
103178151Srpaulo
104197102Sjkim/*
105197102Sjkim * CPU Family/Model Register
106197102Sjkim */
107197102Sjkim#define	AMDTEMP_CPUID		0xfc
108178151Srpaulo
109178151Srpaulo/*
110178151Srpaulo * Device methods.
111178151Srpaulo */
112189769Srpaulostatic void 	amdtemp_identify(driver_t *driver, device_t parent);
113189769Srpaulostatic int	amdtemp_probe(device_t dev);
114189769Srpaulostatic int	amdtemp_attach(device_t dev);
115189769Srpaulostatic void	amdtemp_intrhook(void *arg);
116189769Srpaulostatic int	amdtemp_detach(device_t dev);
117189769Srpaulostatic int 	amdtemp_match(device_t dev);
118189769Srpaulostatic int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
119189769Srpaulostatic int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
120189769Srpaulostatic int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
121178151Srpaulo
122189769Srpaulostatic device_method_t amdtemp_methods[] = {
123178151Srpaulo	/* Device interface */
124189769Srpaulo	DEVMETHOD(device_identify,	amdtemp_identify),
125189769Srpaulo	DEVMETHOD(device_probe,		amdtemp_probe),
126189769Srpaulo	DEVMETHOD(device_attach,	amdtemp_attach),
127189769Srpaulo	DEVMETHOD(device_detach,	amdtemp_detach),
128178151Srpaulo
129178151Srpaulo	{0, 0}
130178151Srpaulo};
131178151Srpaulo
132189769Srpaulostatic driver_t amdtemp_driver = {
133189769Srpaulo	"amdtemp",
134189769Srpaulo	amdtemp_methods,
135189769Srpaulo	sizeof(struct amdtemp_softc),
136178151Srpaulo};
137178151Srpaulo
138189769Srpaulostatic devclass_t amdtemp_devclass;
139189769SrpauloDRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
140178151Srpaulo
141178151Srpaulostatic int
142189769Srpauloamdtemp_match(device_t dev)
143178151Srpaulo{
144178151Srpaulo	int i;
145178151Srpaulo	uint16_t vendor, devid;
146197102Sjkim
147197102Sjkim	vendor = pci_get_vendor(dev);
148178151Srpaulo	devid = pci_get_device(dev);
149178151Srpaulo
150189769Srpaulo	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
151189769Srpaulo		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
152189769Srpaulo		    devid == amdtemp_products[i].amdtemp_deviceid)
153178151Srpaulo			return (1);
154178151Srpaulo	}
155178151Srpaulo
156178151Srpaulo	return (0);
157178151Srpaulo}
158178151Srpaulo
159178151Srpaulostatic void
160189769Srpauloamdtemp_identify(driver_t *driver, device_t parent)
161178151Srpaulo{
162178151Srpaulo	device_t child;
163178151Srpaulo
164178151Srpaulo	/* Make sure we're not being doubly invoked. */
165189769Srpaulo	if (device_find_child(parent, "amdtemp", -1) != NULL)
166178151Srpaulo		return;
167197102Sjkim
168189769Srpaulo	if (amdtemp_match(parent)) {
169189769Srpaulo		child = device_add_child(parent, "amdtemp", -1);
170178151Srpaulo		if (child == NULL)
171189769Srpaulo			device_printf(parent, "add amdtemp child failed\n");
172178151Srpaulo	}
173178151Srpaulo}
174178151Srpaulo
175178151Srpaulostatic int
176189769Srpauloamdtemp_probe(device_t dev)
177178151Srpaulo{
178197205Sjkim	uint32_t family, model;
179197102Sjkim
180189769Srpaulo	if (resource_disabled("amdtemp", 0))
181178151Srpaulo		return (ENXIO);
182178151Srpaulo
183197205Sjkim	family = CPUID_TO_FAMILY(cpu_id);
184197205Sjkim	model = CPUID_TO_MODEL(cpu_id);
185197102Sjkim
186197102Sjkim	switch (family) {
187197102Sjkim	case 0x0f:
188197205Sjkim		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
189197205Sjkim		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
190197102Sjkim			return (ENXIO);
191197102Sjkim		break;
192197102Sjkim	case 0x10:
193197102Sjkim	case 0x11:
194263870Sbrueffer	case 0x16:
195197102Sjkim		break;
196197102Sjkim	default:
197178151Srpaulo		return (ENXIO);
198178151Srpaulo	}
199197102Sjkim	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
200197102Sjkim
201178151Srpaulo	return (BUS_PROBE_GENERIC);
202178151Srpaulo}
203178151Srpaulo
204178151Srpaulostatic int
205189769Srpauloamdtemp_attach(device_t dev)
206178151Srpaulo{
207189769Srpaulo	struct amdtemp_softc *sc = device_get_softc(dev);
208178151Srpaulo	struct sysctl_ctx_list *sysctlctx;
209178151Srpaulo	struct sysctl_oid *sysctlnode;
210197205Sjkim	uint32_t regs[4];
211197102Sjkim	uint32_t cpuid, family, model;
212178151Srpaulo
213197205Sjkim	/*
214197205Sjkim	 * Errata #154: Incorect Diode Offset
215197205Sjkim	 */
216197205Sjkim	if (cpu_id == 0x20f32) {
217197205Sjkim		do_cpuid(0x80000001, regs);
218197205Sjkim		if ((regs[1] & 0xfff) == 0x2c)
219197205Sjkim			sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK;
220197205Sjkim	}
221180312Srpaulo
222197205Sjkim	/*
223197205Sjkim	 * CPUID Register is available from Revision F.
224197205Sjkim	 */
225197205Sjkim	family = CPUID_TO_FAMILY(cpu_id);
226197205Sjkim	model = CPUID_TO_MODEL(cpu_id);
227197205Sjkim	if (family != 0x0f || model >= 0x40) {
228197205Sjkim		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
229197205Sjkim		family = CPUID_TO_FAMILY(cpuid);
230197205Sjkim		model = CPUID_TO_MODEL(cpuid);
231197205Sjkim	}
232197205Sjkim
233197102Sjkim	switch (family) {
234197102Sjkim	case 0x0f:
235197102Sjkim		/*
236197205Sjkim		 * Thermaltrip Status Register
237197102Sjkim		 *
238197205Sjkim		 * - DiodeOffsetSignBit
239197205Sjkim		 *
240197205Sjkim		 * Revision D & E:	bit 24
241197205Sjkim		 * Other:		N/A
242197205Sjkim		 *
243197205Sjkim		 * - ThermSenseCoreSel
244197205Sjkim		 *
245197205Sjkim		 * Revision F & G:	0 - Core1, 1 - Core0
246197205Sjkim		 * Other:		0 - Core0, 1 - Core1
247197205Sjkim		 *
248197205Sjkim		 * - CurTmp
249197205Sjkim		 *
250197102Sjkim		 * Revision G:		bits 23-14
251197205Sjkim		 * Other:		bits 23-16
252197102Sjkim		 *
253197205Sjkim		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
254197205Sjkim		 * ThermSenseCoreSel bits were introduced in Revision F
255197205Sjkim		 * but CurTmp seems working fine as early as Revision C.
256197205Sjkim		 * However, it is not clear whether ThermSenseSel and/or
257197205Sjkim		 * ThermSenseCoreSel work in undocumented cases as well.
258197205Sjkim		 * In fact, the Linux driver suggests it may not work but
259197205Sjkim		 * we just assume it does until we find otherwise.
260197102Sjkim		 */
261197205Sjkim		if (model < 0x40) {
262197205Sjkim			sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO;
263197205Sjkim			if (model >= 0x10)
264197205Sjkim				sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN;
265197205Sjkim		} else {
266197205Sjkim			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
267197205Sjkim			if (model >= 0x60 && model != 0xc1)
268197205Sjkim				sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
269197205Sjkim		}
270197102Sjkim
271197102Sjkim		/*
272197102Sjkim		 * There are two sensors per core.
273197102Sjkim		 */
274197102Sjkim		sc->sc_ntemps = 2;
275197102Sjkim
276189769Srpaulo		sc->sc_gettemp = amdtemp_gettemp0f;
277197102Sjkim		break;
278197102Sjkim	case 0x10:
279197102Sjkim	case 0x11:
280263870Sbrueffer	case 0x16:
281197102Sjkim		/*
282197102Sjkim		 * There is only one sensor per package.
283197102Sjkim		 */
284197102Sjkim		sc->sc_ntemps = 1;
285197102Sjkim
286189769Srpaulo		sc->sc_gettemp = amdtemp_gettemp;
287197102Sjkim		break;
288189769Srpaulo	}
289189769Srpaulo
290197102Sjkim	/* Find number of cores per package. */
291197102Sjkim	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
292197102Sjkim	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
293197102Sjkim	if (sc->sc_ncores > MAXCPU)
294197102Sjkim		return (ENXIO);
295197102Sjkim
296197102Sjkim	if (bootverbose)
297197102Sjkim		device_printf(dev, "Found %d cores and %d sensors.\n",
298197102Sjkim		    sc->sc_ncores,
299197102Sjkim		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
300197102Sjkim
301178151Srpaulo	/*
302189769Srpaulo	 * dev.amdtemp.N tree.
303178151Srpaulo	 */
304178151Srpaulo	sysctlctx = device_get_sysctl_ctx(dev);
305178151Srpaulo	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
306197102Sjkim	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
307197102Sjkim	    "sensor0", CTLFLAG_RD, 0, "Sensor 0");
308197102Sjkim
309178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
310178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
311178151Srpaulo	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
312189769Srpaulo	    dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
313178151Srpaulo	    "Sensor 0 / Core 0 temperature");
314178151Srpaulo
315197102Sjkim	if (sc->sc_ntemps > 1) {
316197102Sjkim		if (sc->sc_ncores > 1)
317197102Sjkim			SYSCTL_ADD_PROC(sysctlctx,
318197102Sjkim			    SYSCTL_CHILDREN(sysctlnode),
319197102Sjkim			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
320197102Sjkim			    dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
321197102Sjkim			    "Sensor 0 / Core 1 temperature");
322197102Sjkim
323197102Sjkim		sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
324197102Sjkim		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
325197102Sjkim		    "sensor1", CTLFLAG_RD, 0, "Sensor 1");
326197102Sjkim
327197102Sjkim		SYSCTL_ADD_PROC(sysctlctx,
328197102Sjkim		    SYSCTL_CHILDREN(sysctlnode),
329197102Sjkim		    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
330197102Sjkim		    dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
331197102Sjkim		    "Sensor 1 / Core 0 temperature");
332197102Sjkim
333197102Sjkim		if (sc->sc_ncores > 1)
334197102Sjkim			SYSCTL_ADD_PROC(sysctlctx,
335197102Sjkim			    SYSCTL_CHILDREN(sysctlnode),
336197102Sjkim			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
337197102Sjkim			    dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
338197102Sjkim			    "Sensor 1 / Core 1 temperature");
339197102Sjkim	}
340197102Sjkim
341197102Sjkim	/*
342197102Sjkim	 * Try to create dev.cpu sysctl entries and setup intrhook function.
343197102Sjkim	 * This is needed because the cpu driver may be loaded late on boot,
344197102Sjkim	 * after us.
345197102Sjkim	 */
346197102Sjkim	amdtemp_intrhook(dev);
347197102Sjkim	sc->sc_ich.ich_func = amdtemp_intrhook;
348197102Sjkim	sc->sc_ich.ich_arg = dev;
349197102Sjkim	if (config_intrhook_establish(&sc->sc_ich) != 0) {
350197102Sjkim		device_printf(dev, "config_intrhook_establish failed!\n");
351197102Sjkim		return (ENXIO);
352197102Sjkim	}
353197102Sjkim
354178151Srpaulo	return (0);
355178151Srpaulo}
356178151Srpaulo
357180312Srpaulovoid
358189769Srpauloamdtemp_intrhook(void *arg)
359180312Srpaulo{
360189769Srpaulo	struct amdtemp_softc *sc;
361180312Srpaulo	struct sysctl_ctx_list *sysctlctx;
362197102Sjkim	device_t dev = (device_t)arg;
363197102Sjkim	device_t acpi, cpu, nexus;
364197102Sjkim	amdsensor_t sensor;
365197102Sjkim	int i;
366180312Srpaulo
367180312Srpaulo	sc = device_get_softc(dev);
368197102Sjkim
369180312Srpaulo	/*
370180312Srpaulo	 * dev.cpu.N.temperature.
371180312Srpaulo	 */
372180312Srpaulo	nexus = device_find_child(root_bus, "nexus", 0);
373180312Srpaulo	acpi = device_find_child(nexus, "acpi", 0);
374180312Srpaulo
375197102Sjkim	for (i = 0; i < sc->sc_ncores; i++) {
376197102Sjkim		if (sc->sc_sysctl_cpu[i] != NULL)
377197102Sjkim			continue;
378180312Srpaulo		cpu = device_find_child(acpi, "cpu",
379197102Sjkim		    device_get_unit(dev) * sc->sc_ncores + i);
380197102Sjkim		if (cpu != NULL) {
381180312Srpaulo			sysctlctx = device_get_sysctl_ctx(cpu);
382180312Srpaulo
383197102Sjkim			sensor = sc->sc_ntemps > 1 ?
384197102Sjkim			    (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0;
385180312Srpaulo			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
386180312Srpaulo			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
387180312Srpaulo			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
388197102Sjkim			    dev, sensor, amdtemp_sysctl, "IK",
389197102Sjkim			    "Current temparature");
390180312Srpaulo		}
391180312Srpaulo	}
392197102Sjkim	if (sc->sc_ich.ich_arg != NULL)
393197102Sjkim		config_intrhook_disestablish(&sc->sc_ich);
394180312Srpaulo}
395180312Srpaulo
396178151Srpauloint
397189769Srpauloamdtemp_detach(device_t dev)
398178151Srpaulo{
399197102Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
400178151Srpaulo	int i;
401197102Sjkim
402197102Sjkim	for (i = 0; i < sc->sc_ncores; i++)
403197102Sjkim		if (sc->sc_sysctl_cpu[i] != NULL)
404178151Srpaulo			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
405178151Srpaulo
406189769Srpaulo	/* NewBus removes the dev.amdtemp.N tree by itself. */
407197102Sjkim
408178151Srpaulo	return (0);
409178151Srpaulo}
410178151Srpaulo
411178151Srpaulostatic int
412189769Srpauloamdtemp_sysctl(SYSCTL_HANDLER_ARGS)
413178151Srpaulo{
414197102Sjkim	device_t dev = (device_t)arg1;
415189769Srpaulo	struct amdtemp_softc *sc = device_get_softc(dev);
416197102Sjkim	amdsensor_t sensor = (amdsensor_t)arg2;
417197102Sjkim	int32_t auxtemp[2], temp;
418178151Srpaulo	int error;
419178151Srpaulo
420197102Sjkim	switch (sensor) {
421178151Srpaulo	case CORE0:
422189769Srpaulo		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
423189769Srpaulo		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
424178988Srpaulo		temp = imax(auxtemp[0], auxtemp[1]);
425178151Srpaulo		break;
426178151Srpaulo	case CORE1:
427189769Srpaulo		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1);
428189769Srpaulo		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1);
429178988Srpaulo		temp = imax(auxtemp[0], auxtemp[1]);
430178151Srpaulo		break;
431178151Srpaulo	default:
432197102Sjkim		temp = sc->sc_gettemp(dev, sensor);
433178151Srpaulo		break;
434178151Srpaulo	}
435178151Srpaulo	error = sysctl_handle_int(oidp, &temp, 0, req);
436197102Sjkim
437178151Srpaulo	return (error);
438178151Srpaulo}
439178151Srpaulo
440197102Sjkim#define	AMDTEMP_ZERO_C_TO_K	2732
441197102Sjkim
442178151Srpaulostatic int32_t
443189769Srpauloamdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
444178151Srpaulo{
445197102Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
446197205Sjkim	uint32_t mask, temp;
447197102Sjkim	int32_t diode_offset, offset;
448197102Sjkim	uint8_t cfg, sel;
449197102Sjkim
450197102Sjkim	/* Set Sensor/Core selector. */
451197102Sjkim	sel = 0;
452178151Srpaulo	switch (sensor) {
453197102Sjkim	case SENSOR1_CORE0:
454197102Sjkim		sel |= AMDTEMP_TTSR_SELSENSOR;
455197103Sjkim		/* FALLTHROUGH */
456178151Srpaulo	case SENSOR0_CORE0:
457197102Sjkim	case CORE0:
458197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
459197102Sjkim			sel |= AMDTEMP_TTSR_SELCORE;
460178151Srpaulo		break;
461197102Sjkim	case SENSOR1_CORE1:
462197102Sjkim		sel |= AMDTEMP_TTSR_SELSENSOR;
463197103Sjkim		/* FALLTHROUGH */
464178151Srpaulo	case SENSOR0_CORE1:
465197102Sjkim	case CORE1:
466197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
467197102Sjkim			sel |= AMDTEMP_TTSR_SELCORE;
468178151Srpaulo		break;
469178151Srpaulo	}
470197102Sjkim	cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
471197102Sjkim	cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE);
472197102Sjkim	pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1);
473197102Sjkim
474197102Sjkim	/* CurTmp starts from -49C. */
475197102Sjkim	offset = AMDTEMP_ZERO_C_TO_K - 490;
476197102Sjkim
477197102Sjkim	/* Adjust offset if DiodeOffset is set and valid. */
478197102Sjkim	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
479197102Sjkim	diode_offset = (temp >> 8) & 0x3f;
480197205Sjkim	if ((sc->sc_flags & AMDTEMP_FLAG_DO_ZERO) != 0) {
481197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_DO_SIGN) != 0 &&
482197205Sjkim		    ((temp >> 24) & 0x1) != 0)
483197205Sjkim			diode_offset *= -1;
484197205Sjkim		if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 &&
485197205Sjkim		    ((temp >> 25) & 0xf) <= 2)
486197205Sjkim			diode_offset += 10;
487197205Sjkim		offset += diode_offset * 10;
488197205Sjkim	} else if (diode_offset != 0)
489197102Sjkim		offset += (diode_offset - 11) * 10;
490197102Sjkim
491197205Sjkim	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
492197205Sjkim	temp = ((temp >> 14) & mask) * 5 / 2 + offset;
493197102Sjkim
494178151Srpaulo	return (temp);
495178151Srpaulo}
496189769Srpaulo
497189769Srpaulostatic int32_t
498189769Srpauloamdtemp_gettemp(device_t dev, amdsensor_t sensor)
499189769Srpaulo{
500189769Srpaulo	uint32_t temp;
501197102Sjkim	int32_t diode_offset, offset;
502189769Srpaulo
503197102Sjkim	/* CurTmp starts from 0C. */
504197102Sjkim	offset = AMDTEMP_ZERO_C_TO_K;
505189769Srpaulo
506197102Sjkim	/* Adjust offset if DiodeOffset is set and valid. */
507197102Sjkim	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
508197102Sjkim	diode_offset = (temp >> 8) & 0x7f;
509197102Sjkim	if (diode_offset > 0 && diode_offset < 0x40)
510197102Sjkim		offset += (diode_offset - 11) * 10;
511197102Sjkim
512197102Sjkim	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
513197205Sjkim	temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset;
514197102Sjkim
515189769Srpaulo	return (temp);
516189769Srpaulo}
517