amdtemp.c revision 197205
1169689Skan/*-
2169689Skan * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3169689Skan * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4169689Skan * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org>
5169689Skan * All rights reserved.
6169689Skan *
7169689Skan * Redistribution and use in source and binary forms, with or without
8169689Skan * modification, are permitted provided that the following conditions
9169689Skan * are met:
10169689Skan * 1. Redistributions of source code must retain the above copyright
11169689Skan *    notice, this list of conditions and the following disclaimer.
12169689Skan * 2. Redistributions in binary form must reproduce the above copyright
13169689Skan *    notice, this list of conditions and the following disclaimer in the
14169689Skan *    documentation and/or other materials provided with the distribution.
15169689Skan *
16169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17169689Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18169689Skan * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19169689Skan * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20169689Skan * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21169689Skan * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22169689Skan * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24169689Skan * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25169689Skan * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26169689Skan * POSSIBILITY OF SUCH DAMAGE.
27169689Skan */
28169689Skan
29169689Skan/*
30169689Skan * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs.
31169689Skan * Initially based on the k8temp Linux driver.
32169689Skan */
33169689Skan
34169689Skan#include <sys/cdefs.h>
35169689Skan__FBSDID("$FreeBSD: head/sys/dev/amdtemp/amdtemp.c 197205 2009-09-14 23:08:19Z jkim $");
36169689Skan
37169689Skan#include <sys/param.h>
38169689Skan#include <sys/bus.h>
39169689Skan#include <sys/conf.h>
40169689Skan#include <sys/kernel.h>
41169689Skan#include <sys/module.h>
42169689Skan#include <sys/sysctl.h>
43169689Skan#include <sys/systm.h>
44169689Skan
45169689Skan#include <machine/cpufunc.h>
46169689Skan#include <machine/md_var.h>
47169689Skan#include <machine/specialreg.h>
48169689Skan
49169689Skan#include <dev/pci/pcivar.h>
50169689Skan
51169689Skantypedef enum {
52169689Skan	SENSOR0_CORE0,
53169689Skan	SENSOR0_CORE1,
54169689Skan	SENSOR1_CORE0,
55169689Skan	SENSOR1_CORE1,
56169689Skan	CORE0,
57169689Skan	CORE1
58169689Skan} amdsensor_t;
59169689Skan
60169689Skanstruct amdtemp_softc {
61169689Skan	device_t	sc_dev;
62169689Skan	int		sc_ncores;
63169689Skan	int		sc_ntemps;
64169689Skan	int		sc_flags;
65169689Skan#define	AMDTEMP_FLAG_DO_QUIRK	0x01	/* DiodeOffset may be incorrect. */
66169689Skan#define	AMDTEMP_FLAG_DO_ZERO	0x02	/* DiodeOffset starts from 0C. */
67169689Skan#define	AMDTEMP_FLAG_DO_SIGN	0x04	/* DiodeOffsetSignBit is present. */
68169689Skan#define	AMDTEMP_FLAG_CS_SWAP	0x08	/* ThermSenseCoreSel is inverted. */
69169689Skan#define	AMDTEMP_FLAG_CT_10BIT	0x10	/* CurTmp is 10-bit wide. */
70169689Skan	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
71169689Skan	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
72169689Skan	struct intr_config_hook sc_ich;
73169689Skan};
74169689Skan
75169689Skan#define	VENDORID_AMD		0x1022
76169689Skan#define	DEVICEID_AMD_MISC0F	0x1103
77169689Skan#define	DEVICEID_AMD_MISC10	0x1203
78169689Skan#define	DEVICEID_AMD_MISC11	0x1303
79169689Skan
80169689Skanstatic struct amdtemp_product {
81169689Skan	uint16_t	amdtemp_vendorid;
82169689Skan	uint16_t	amdtemp_deviceid;
83169689Skan} amdtemp_products[] = {
84169689Skan	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
85169689Skan	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
86169689Skan	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
87169689Skan	{ 0, 0 }
88169689Skan};
89169689Skan
90169689Skan/*
91169689Skan * Reported Temperature Control Register (Family 10h/11h only)
92169689Skan */
93169689Skan#define	AMDTEMP_REPTMP_CTRL	0xa4
94169689Skan
95169689Skan/*
96169689Skan * Thermaltrip Status Register
97169689Skan */
98169689Skan#define	AMDTEMP_THERMTP_STAT	0xe4
99169689Skan#define	AMDTEMP_TTSR_SELCORE	0x04	/* Family 0Fh only */
100169689Skan#define	AMDTEMP_TTSR_SELSENSOR	0x40	/* Family 0Fh only */
101169689Skan
102169689Skan/*
103169689Skan * CPU Family/Model Register
104169689Skan */
105169689Skan#define	AMDTEMP_CPUID		0xfc
106169689Skan
107169689Skan/*
108169689Skan * Device methods.
109169689Skan */
110169689Skanstatic void 	amdtemp_identify(driver_t *driver, device_t parent);
111169689Skanstatic int	amdtemp_probe(device_t dev);
112169689Skanstatic int	amdtemp_attach(device_t dev);
113169689Skanstatic void	amdtemp_intrhook(void *arg);
114169689Skanstatic int	amdtemp_detach(device_t dev);
115169689Skanstatic int 	amdtemp_match(device_t dev);
116169689Skanstatic int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
117169689Skanstatic int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
118169689Skanstatic int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
119169689Skan
120169689Skanstatic device_method_t amdtemp_methods[] = {
121169689Skan	/* Device interface */
122169689Skan	DEVMETHOD(device_identify,	amdtemp_identify),
123169689Skan	DEVMETHOD(device_probe,		amdtemp_probe),
124169689Skan	DEVMETHOD(device_attach,	amdtemp_attach),
125169689Skan	DEVMETHOD(device_detach,	amdtemp_detach),
126169689Skan
127169689Skan	{0, 0}
128169689Skan};
129169689Skan
130169689Skanstatic driver_t amdtemp_driver = {
131169689Skan	"amdtemp",
132169689Skan	amdtemp_methods,
133169689Skan	sizeof(struct amdtemp_softc),
134169689Skan};
135169689Skan
136169689Skanstatic devclass_t amdtemp_devclass;
137169689SkanDRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
138169689Skan
139169689Skanstatic int
140169689Skanamdtemp_match(device_t dev)
141169689Skan{
142169689Skan	int i;
143169689Skan	uint16_t vendor, devid;
144169689Skan
145169689Skan	vendor = pci_get_vendor(dev);
146169689Skan	devid = pci_get_device(dev);
147169689Skan
148169689Skan	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
149169689Skan		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
150169689Skan		    devid == amdtemp_products[i].amdtemp_deviceid)
151169689Skan			return (1);
152169689Skan	}
153169689Skan
154169689Skan	return (0);
155169689Skan}
156169689Skan
157169689Skanstatic void
158169689Skanamdtemp_identify(driver_t *driver, device_t parent)
159169689Skan{
160169689Skan	device_t child;
161169689Skan
162169689Skan	/* Make sure we're not being doubly invoked. */
163169689Skan	if (device_find_child(parent, "amdtemp", -1) != NULL)
164169689Skan		return;
165169689Skan
166169689Skan	if (amdtemp_match(parent)) {
167169689Skan		child = device_add_child(parent, "amdtemp", -1);
168169689Skan		if (child == NULL)
169169689Skan			device_printf(parent, "add amdtemp child failed\n");
170169689Skan	}
171169689Skan}
172169689Skan
173169689Skanstatic int
174169689Skanamdtemp_probe(device_t dev)
175169689Skan{
176169689Skan	uint32_t family, model;
177169689Skan
178169689Skan	if (resource_disabled("amdtemp", 0))
179169689Skan		return (ENXIO);
180169689Skan
181169689Skan	family = CPUID_TO_FAMILY(cpu_id);
182169689Skan	model = CPUID_TO_MODEL(cpu_id);
183169689Skan
184169689Skan	switch (family) {
185169689Skan	case 0x0f:
186169689Skan		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
187169689Skan		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
188169689Skan			return (ENXIO);
189169689Skan		break;
190169689Skan	case 0x10:
191169689Skan	case 0x11:
192169689Skan		break;
193169689Skan	default:
194169689Skan		return (ENXIO);
195169689Skan	}
196169689Skan	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
197169689Skan
198169689Skan	return (BUS_PROBE_GENERIC);
199169689Skan}
200169689Skan
201169689Skanstatic int
202169689Skanamdtemp_attach(device_t dev)
203169689Skan{
204169689Skan	struct amdtemp_softc *sc = device_get_softc(dev);
205169689Skan	struct sysctl_ctx_list *sysctlctx;
206169689Skan	struct sysctl_oid *sysctlnode;
207169689Skan	uint32_t regs[4];
208169689Skan	uint32_t cpuid, family, model;
209169689Skan
210169689Skan	/*
211169689Skan	 * Errata #154: Incorect Diode Offset
212169689Skan	 */
213169689Skan	if (cpu_id == 0x20f32) {
214169689Skan		do_cpuid(0x80000001, regs);
215169689Skan		if ((regs[1] & 0xfff) == 0x2c)
216169689Skan			sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK;
217169689Skan	}
218169689Skan
219169689Skan	/*
220169689Skan	 * CPUID Register is available from Revision F.
221169689Skan	 */
222169689Skan	family = CPUID_TO_FAMILY(cpu_id);
223169689Skan	model = CPUID_TO_MODEL(cpu_id);
224169689Skan	if (family != 0x0f || model >= 0x40) {
225169689Skan		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
226169689Skan		family = CPUID_TO_FAMILY(cpuid);
227169689Skan		model = CPUID_TO_MODEL(cpuid);
228169689Skan	}
229169689Skan
230169689Skan	switch (family) {
231169689Skan	case 0x0f:
232169689Skan		/*
233169689Skan		 * Thermaltrip Status Register
234169689Skan		 *
235169689Skan		 * - DiodeOffsetSignBit
236169689Skan		 *
237169689Skan		 * Revision D & E:	bit 24
238169689Skan		 * Other:		N/A
239169689Skan		 *
240169689Skan		 * - ThermSenseCoreSel
241169689Skan		 *
242169689Skan		 * Revision F & G:	0 - Core1, 1 - Core0
243169689Skan		 * Other:		0 - Core0, 1 - Core1
244169689Skan		 *
245169689Skan		 * - CurTmp
246169689Skan		 *
247169689Skan		 * Revision G:		bits 23-14
248169689Skan		 * Other:		bits 23-16
249169689Skan		 *
250169689Skan		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
251169689Skan		 * ThermSenseCoreSel bits were introduced in Revision F
252169689Skan		 * but CurTmp seems working fine as early as Revision C.
253169689Skan		 * However, it is not clear whether ThermSenseSel and/or
254169689Skan		 * ThermSenseCoreSel work in undocumented cases as well.
255169689Skan		 * In fact, the Linux driver suggests it may not work but
256169689Skan		 * we just assume it does until we find otherwise.
257169689Skan		 */
258169689Skan		if (model < 0x40) {
259169689Skan			sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO;
260169689Skan			if (model >= 0x10)
261169689Skan				sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN;
262169689Skan		} else {
263169689Skan			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
264169689Skan			if (model >= 0x60 && model != 0xc1)
265169689Skan				sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
266169689Skan		}
267169689Skan
268169689Skan		/*
269169689Skan		 * There are two sensors per core.
270169689Skan		 */
271169689Skan		sc->sc_ntemps = 2;
272169689Skan
273169689Skan		sc->sc_gettemp = amdtemp_gettemp0f;
274169689Skan		break;
275169689Skan	case 0x10:
276169689Skan	case 0x11:
277169689Skan		/*
278169689Skan		 * There is only one sensor per package.
279169689Skan		 */
280169689Skan		sc->sc_ntemps = 1;
281169689Skan
282169689Skan		sc->sc_gettemp = amdtemp_gettemp;
283169689Skan		break;
284169689Skan	}
285169689Skan
286169689Skan	/* Find number of cores per package. */
287169689Skan	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
288169689Skan	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
289169689Skan	if (sc->sc_ncores > MAXCPU)
290169689Skan		return (ENXIO);
291169689Skan
292169689Skan	if (bootverbose)
293169689Skan		device_printf(dev, "Found %d cores and %d sensors.\n",
294169689Skan		    sc->sc_ncores,
295169689Skan		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
296169689Skan
297169689Skan	/*
298169689Skan	 * dev.amdtemp.N tree.
299169689Skan	 */
300169689Skan	sysctlctx = device_get_sysctl_ctx(dev);
301169689Skan	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
302169689Skan	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
303169689Skan	    "sensor0", CTLFLAG_RD, 0, "Sensor 0");
304169689Skan
305169689Skan	SYSCTL_ADD_PROC(sysctlctx,
306169689Skan	    SYSCTL_CHILDREN(sysctlnode),
307169689Skan	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
308169689Skan	    dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
309169689Skan	    "Sensor 0 / Core 0 temperature");
310169689Skan
311169689Skan	if (sc->sc_ntemps > 1) {
312169689Skan		if (sc->sc_ncores > 1)
313169689Skan			SYSCTL_ADD_PROC(sysctlctx,
314169689Skan			    SYSCTL_CHILDREN(sysctlnode),
315169689Skan			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
316169689Skan			    dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
317169689Skan			    "Sensor 0 / Core 1 temperature");
318169689Skan
319169689Skan		sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
320169689Skan		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
321169689Skan		    "sensor1", CTLFLAG_RD, 0, "Sensor 1");
322169689Skan
323169689Skan		SYSCTL_ADD_PROC(sysctlctx,
324169689Skan		    SYSCTL_CHILDREN(sysctlnode),
325169689Skan		    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
326169689Skan		    dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
327169689Skan		    "Sensor 1 / Core 0 temperature");
328169689Skan
329169689Skan		if (sc->sc_ncores > 1)
330169689Skan			SYSCTL_ADD_PROC(sysctlctx,
331169689Skan			    SYSCTL_CHILDREN(sysctlnode),
332169689Skan			    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
333169689Skan			    dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
334169689Skan			    "Sensor 1 / Core 1 temperature");
335169689Skan	}
336169689Skan
337169689Skan	/*
338169689Skan	 * Try to create dev.cpu sysctl entries and setup intrhook function.
339169689Skan	 * This is needed because the cpu driver may be loaded late on boot,
340169689Skan	 * after us.
341169689Skan	 */
342169689Skan	amdtemp_intrhook(dev);
343169689Skan	sc->sc_ich.ich_func = amdtemp_intrhook;
344169689Skan	sc->sc_ich.ich_arg = dev;
345169689Skan	if (config_intrhook_establish(&sc->sc_ich) != 0) {
346169689Skan		device_printf(dev, "config_intrhook_establish failed!\n");
347169689Skan		return (ENXIO);
348169689Skan	}
349169689Skan
350169689Skan	return (0);
351169689Skan}
352169689Skan
353169689Skanvoid
354169689Skanamdtemp_intrhook(void *arg)
355169689Skan{
356169689Skan	struct amdtemp_softc *sc;
357169689Skan	struct sysctl_ctx_list *sysctlctx;
358169689Skan	device_t dev = (device_t)arg;
359169689Skan	device_t acpi, cpu, nexus;
360169689Skan	amdsensor_t sensor;
361169689Skan	int i;
362169689Skan
363169689Skan	sc = device_get_softc(dev);
364169689Skan
365169689Skan	/*
366169689Skan	 * dev.cpu.N.temperature.
367169689Skan	 */
368169689Skan	nexus = device_find_child(root_bus, "nexus", 0);
369169689Skan	acpi = device_find_child(nexus, "acpi", 0);
370169689Skan
371169689Skan	for (i = 0; i < sc->sc_ncores; i++) {
372169689Skan		if (sc->sc_sysctl_cpu[i] != NULL)
373169689Skan			continue;
374169689Skan		cpu = device_find_child(acpi, "cpu",
375169689Skan		    device_get_unit(dev) * sc->sc_ncores + i);
376169689Skan		if (cpu != NULL) {
377169689Skan			sysctlctx = device_get_sysctl_ctx(cpu);
378169689Skan
379169689Skan			sensor = sc->sc_ntemps > 1 ?
380169689Skan			    (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0;
381169689Skan			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
382169689Skan			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
383169689Skan			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
384169689Skan			    dev, sensor, amdtemp_sysctl, "IK",
385169689Skan			    "Current temparature");
386169689Skan		}
387169689Skan	}
388169689Skan	if (sc->sc_ich.ich_arg != NULL)
389169689Skan		config_intrhook_disestablish(&sc->sc_ich);
390169689Skan}
391169689Skan
392169689Skanint
393169689Skanamdtemp_detach(device_t dev)
394169689Skan{
395169689Skan	struct amdtemp_softc *sc = device_get_softc(dev);
396169689Skan	int i;
397169689Skan
398169689Skan	for (i = 0; i < sc->sc_ncores; i++)
399169689Skan		if (sc->sc_sysctl_cpu[i] != NULL)
400169689Skan			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
401169689Skan
402169689Skan	/* NewBus removes the dev.amdtemp.N tree by itself. */
403169689Skan
404169689Skan	return (0);
405169689Skan}
406169689Skan
407169689Skanstatic int
408169689Skanamdtemp_sysctl(SYSCTL_HANDLER_ARGS)
409169689Skan{
410169689Skan	device_t dev = (device_t)arg1;
411169689Skan	struct amdtemp_softc *sc = device_get_softc(dev);
412169689Skan	amdsensor_t sensor = (amdsensor_t)arg2;
413169689Skan	int32_t auxtemp[2], temp;
414169689Skan	int error;
415169689Skan
416169689Skan	switch (sensor) {
417169689Skan	case CORE0:
418169689Skan		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
419169689Skan		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
420169689Skan		temp = imax(auxtemp[0], auxtemp[1]);
421169689Skan		break;
422169689Skan	case CORE1:
423169689Skan		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1);
424169689Skan		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1);
425169689Skan		temp = imax(auxtemp[0], auxtemp[1]);
426169689Skan		break;
427169689Skan	default:
428169689Skan		temp = sc->sc_gettemp(dev, sensor);
429169689Skan		break;
430169689Skan	}
431169689Skan	error = sysctl_handle_int(oidp, &temp, 0, req);
432169689Skan
433169689Skan	return (error);
434169689Skan}
435169689Skan
436169689Skan#define	AMDTEMP_ZERO_C_TO_K	2732
437169689Skan
438169689Skanstatic int32_t
439169689Skanamdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
440169689Skan{
441169689Skan	struct amdtemp_softc *sc = device_get_softc(dev);
442169689Skan	uint32_t mask, temp;
443169689Skan	int32_t diode_offset, offset;
444169689Skan	uint8_t cfg, sel;
445169689Skan
446169689Skan	/* Set Sensor/Core selector. */
447169689Skan	sel = 0;
448169689Skan	switch (sensor) {
449169689Skan	case SENSOR1_CORE0:
450169689Skan		sel |= AMDTEMP_TTSR_SELSENSOR;
451169689Skan		/* FALLTHROUGH */
452169689Skan	case SENSOR0_CORE0:
453169689Skan	case CORE0:
454169689Skan		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
455169689Skan			sel |= AMDTEMP_TTSR_SELCORE;
456169689Skan		break;
457169689Skan	case SENSOR1_CORE1:
458169689Skan		sel |= AMDTEMP_TTSR_SELSENSOR;
459169689Skan		/* FALLTHROUGH */
460169689Skan	case SENSOR0_CORE1:
461169689Skan	case CORE1:
462169689Skan		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
463169689Skan			sel |= AMDTEMP_TTSR_SELCORE;
464169689Skan		break;
465169689Skan	}
466169689Skan	cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
467169689Skan	cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE);
468169689Skan	pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1);
469169689Skan
470169689Skan	/* CurTmp starts from -49C. */
471169689Skan	offset = AMDTEMP_ZERO_C_TO_K - 490;
472169689Skan
473169689Skan	/* Adjust offset if DiodeOffset is set and valid. */
474169689Skan	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
475169689Skan	diode_offset = (temp >> 8) & 0x3f;
476169689Skan	if ((sc->sc_flags & AMDTEMP_FLAG_DO_ZERO) != 0) {
477169689Skan		if ((sc->sc_flags & AMDTEMP_FLAG_DO_SIGN) != 0 &&
478169689Skan		    ((temp >> 24) & 0x1) != 0)
479169689Skan			diode_offset *= -1;
480169689Skan		if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 &&
481169689Skan		    ((temp >> 25) & 0xf) <= 2)
482169689Skan			diode_offset += 10;
483169689Skan		offset += diode_offset * 10;
484169689Skan	} else if (diode_offset != 0)
485169689Skan		offset += (diode_offset - 11) * 10;
486169689Skan
487169689Skan	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
488169689Skan	temp = ((temp >> 14) & mask) * 5 / 2 + offset;
489169689Skan
490169689Skan	return (temp);
491169689Skan}
492169689Skan
493169689Skanstatic int32_t
494169689Skanamdtemp_gettemp(device_t dev, amdsensor_t sensor)
495169689Skan{
496169689Skan	uint32_t temp;
497169689Skan	int32_t diode_offset, offset;
498169689Skan
499169689Skan	/* CurTmp starts from 0C. */
500169689Skan	offset = AMDTEMP_ZERO_C_TO_K;
501169689Skan
502169689Skan	/* Adjust offset if DiodeOffset is set and valid. */
503169689Skan	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
504169689Skan	diode_offset = (temp >> 8) & 0x7f;
505169689Skan	if (diode_offset > 0 && diode_offset < 0x40)
506169689Skan		offset += (diode_offset - 11) * 10;
507169689Skan
508169689Skan	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
509169689Skan	temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset;
510169689Skan
511169689Skan	return (temp);
512169689Skan}
513169689Skan