amdtemp.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
5 * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
6 * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/*
32 * Driver for the AMD CPU on-die thermal sensors.
33 * Initially based on the k8temp Linux driver.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: stable/11/sys/dev/amdtemp/amdtemp.c 330897 2018-03-14 03:19:51Z eadler $");
38
39#include <sys/param.h>
40#include <sys/bus.h>
41#include <sys/conf.h>
42#include <sys/kernel.h>
43#include <sys/module.h>
44#include <sys/sysctl.h>
45#include <sys/systm.h>
46
47#include <machine/cpufunc.h>
48#include <machine/md_var.h>
49#include <machine/specialreg.h>
50
51#include <dev/pci/pcivar.h>
52#include <x86/pci_cfgreg.h>
53
54#include <dev/amdsmn/amdsmn.h>
55
56typedef enum {
57	CORE0_SENSOR0,
58	CORE0_SENSOR1,
59	CORE1_SENSOR0,
60	CORE1_SENSOR1,
61	CORE0,
62	CORE1
63} amdsensor_t;
64
65struct amdtemp_softc {
66	int		sc_ncores;
67	int		sc_ntemps;
68	int		sc_flags;
69#define	AMDTEMP_FLAG_CS_SWAP	0x01	/* ThermSenseCoreSel is inverted. */
70#define	AMDTEMP_FLAG_CT_10BIT	0x02	/* CurTmp is 10-bit wide. */
71#define	AMDTEMP_FLAG_ALT_OFFSET	0x04	/* CurTmp starts at -28C. */
72	int32_t		sc_offset;
73	int32_t		(*sc_gettemp)(device_t, amdsensor_t);
74	struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
75	struct intr_config_hook sc_ich;
76	device_t	sc_smn;
77};
78
79#define	VENDORID_AMD		0x1022
80#define	DEVICEID_AMD_MISC0F	0x1103
81#define	DEVICEID_AMD_MISC10	0x1203
82#define	DEVICEID_AMD_MISC11	0x1303
83#define	DEVICEID_AMD_MISC12	0x1403
84#define	DEVICEID_AMD_MISC14	0x1703
85#define	DEVICEID_AMD_MISC15	0x1603
86#define	DEVICEID_AMD_MISC16	0x1533
87#define	DEVICEID_AMD_MISC16_M30H	0x1583
88#define	DEVICEID_AMD_MISC17	0x141d
89#define	DEVICEID_AMD_HOSTB17H	0x1450
90
91static struct amdtemp_product {
92	uint16_t	amdtemp_vendorid;
93	uint16_t	amdtemp_deviceid;
94} amdtemp_products[] = {
95	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
96	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
97	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
98	{ VENDORID_AMD,	DEVICEID_AMD_MISC12 },
99	{ VENDORID_AMD,	DEVICEID_AMD_MISC14 },
100	{ VENDORID_AMD,	DEVICEID_AMD_MISC15 },
101	{ VENDORID_AMD,	DEVICEID_AMD_MISC16 },
102	{ VENDORID_AMD,	DEVICEID_AMD_MISC16_M30H },
103	{ VENDORID_AMD,	DEVICEID_AMD_MISC17 },
104	{ VENDORID_AMD,	DEVICEID_AMD_HOSTB17H },
105	{ 0, 0 }
106};
107
108/*
109 * Reported Temperature Control Register
110 */
111#define	AMDTEMP_REPTMP_CTRL	0xa4
112
113/*
114 * Reported Temperature, Family 17h
115 */
116#define	AMDTEMP_17H_CUR_TMP	0x59800
117
118/*
119 * Thermaltrip Status Register (Family 0Fh only)
120 */
121#define	AMDTEMP_THERMTP_STAT	0xe4
122#define	AMDTEMP_TTSR_SELCORE	0x04
123#define	AMDTEMP_TTSR_SELSENSOR	0x40
124
125/*
126 * DRAM Configuration High Register
127 */
128#define	AMDTEMP_DRAM_CONF_HIGH	0x94	/* Function 2 */
129#define	AMDTEMP_DRAM_MODE_DDR3	0x0100
130
131/*
132 * CPU Family/Model Register
133 */
134#define	AMDTEMP_CPUID		0xfc
135
136/*
137 * Device methods.
138 */
139static void 	amdtemp_identify(driver_t *driver, device_t parent);
140static int	amdtemp_probe(device_t dev);
141static int	amdtemp_attach(device_t dev);
142static void	amdtemp_intrhook(void *arg);
143static int	amdtemp_detach(device_t dev);
144static int 	amdtemp_match(device_t dev);
145static int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
146static int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
147static int32_t	amdtemp_gettemp17h(device_t dev, amdsensor_t sensor);
148static int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
149
150static device_method_t amdtemp_methods[] = {
151	/* Device interface */
152	DEVMETHOD(device_identify,	amdtemp_identify),
153	DEVMETHOD(device_probe,		amdtemp_probe),
154	DEVMETHOD(device_attach,	amdtemp_attach),
155	DEVMETHOD(device_detach,	amdtemp_detach),
156
157	DEVMETHOD_END
158};
159
160static driver_t amdtemp_driver = {
161	"amdtemp",
162	amdtemp_methods,
163	sizeof(struct amdtemp_softc),
164};
165
166static devclass_t amdtemp_devclass;
167DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
168MODULE_VERSION(amdtemp, 1);
169MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1);
170
171static int
172amdtemp_match(device_t dev)
173{
174	int i;
175	uint16_t vendor, devid;
176
177	vendor = pci_get_vendor(dev);
178	devid = pci_get_device(dev);
179
180	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
181		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
182		    devid == amdtemp_products[i].amdtemp_deviceid)
183			return (1);
184	}
185
186	return (0);
187}
188
189static void
190amdtemp_identify(driver_t *driver, device_t parent)
191{
192	device_t child;
193
194	/* Make sure we're not being doubly invoked. */
195	if (device_find_child(parent, "amdtemp", -1) != NULL)
196		return;
197
198	if (amdtemp_match(parent)) {
199		child = device_add_child(parent, "amdtemp", -1);
200		if (child == NULL)
201			device_printf(parent, "add amdtemp child failed\n");
202	}
203}
204
205static int
206amdtemp_probe(device_t dev)
207{
208	uint32_t family, model;
209
210	if (resource_disabled("amdtemp", 0))
211		return (ENXIO);
212	if (!amdtemp_match(device_get_parent(dev)))
213		return (ENXIO);
214
215	family = CPUID_TO_FAMILY(cpu_id);
216	model = CPUID_TO_MODEL(cpu_id);
217
218	switch (family) {
219	case 0x0f:
220		if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
221		    (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
222			return (ENXIO);
223		break;
224	case 0x10:
225	case 0x11:
226	case 0x12:
227	case 0x14:
228	case 0x15:
229	case 0x16:
230	case 0x17:
231		break;
232	default:
233		return (ENXIO);
234	}
235	device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
236
237	return (BUS_PROBE_GENERIC);
238}
239
240static int
241amdtemp_attach(device_t dev)
242{
243	char tn[32];
244	u_int regs[4];
245	struct amdtemp_softc *sc = device_get_softc(dev);
246	struct sysctl_ctx_list *sysctlctx;
247	struct sysctl_oid *sysctlnode;
248	uint32_t cpuid, family, model;
249	u_int bid;
250	int erratum319, unit;
251
252	erratum319 = 0;
253
254	/*
255	 * CPUID Register is available from Revision F.
256	 */
257	cpuid = cpu_id;
258	family = CPUID_TO_FAMILY(cpuid);
259	model = CPUID_TO_MODEL(cpuid);
260	if ((family != 0x0f || model >= 0x40) && family != 0x17) {
261		cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
262		family = CPUID_TO_FAMILY(cpuid);
263		model = CPUID_TO_MODEL(cpuid);
264	}
265
266	switch (family) {
267	case 0x0f:
268		/*
269		 * Thermaltrip Status Register
270		 *
271		 * - ThermSenseCoreSel
272		 *
273		 * Revision F & G:	0 - Core1, 1 - Core0
274		 * Other:		0 - Core0, 1 - Core1
275		 *
276		 * - CurTmp
277		 *
278		 * Revision G:		bits 23-14
279		 * Other:		bits 23-16
280		 *
281		 * XXX According to the BKDG, CurTmp, ThermSenseSel and
282		 * ThermSenseCoreSel bits were introduced in Revision F
283		 * but CurTmp seems working fine as early as Revision C.
284		 * However, it is not clear whether ThermSenseSel and/or
285		 * ThermSenseCoreSel work in undocumented cases as well.
286		 * In fact, the Linux driver suggests it may not work but
287		 * we just assume it does until we find otherwise.
288		 *
289		 * XXX According to Linux, CurTmp starts at -28C on
290		 * Socket AM2 Revision G processors, which is not
291		 * documented anywhere.
292		 */
293		if (model >= 0x40)
294			sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
295		if (model >= 0x60 && model != 0xc1) {
296			do_cpuid(0x80000001, regs);
297			bid = (regs[1] >> 9) & 0x1f;
298			switch (model) {
299			case 0x68: /* Socket S1g1 */
300			case 0x6c:
301			case 0x7c:
302				break;
303			case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
304				if (bid != 0x0b && bid != 0x0c)
305					sc->sc_flags |=
306					    AMDTEMP_FLAG_ALT_OFFSET;
307				break;
308			case 0x6f: /* Socket AM2 and ASB1 (1 core) */
309			case 0x7f:
310				if (bid != 0x07 && bid != 0x09 &&
311				    bid != 0x0c)
312					sc->sc_flags |=
313					    AMDTEMP_FLAG_ALT_OFFSET;
314				break;
315			default:
316				sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
317			}
318			sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
319		}
320
321		/*
322		 * There are two sensors per core.
323		 */
324		sc->sc_ntemps = 2;
325
326		sc->sc_gettemp = amdtemp_gettemp0f;
327		break;
328	case 0x10:
329		/*
330		 * Erratum 319 Inaccurate Temperature Measurement
331		 *
332		 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
333		 */
334		do_cpuid(0x80000001, regs);
335		switch ((regs[1] >> 28) & 0xf) {
336		case 0:	/* Socket F */
337			erratum319 = 1;
338			break;
339		case 1:	/* Socket AM2+ or AM3 */
340			if ((pci_cfgregread(pci_get_bus(dev),
341			    pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
342			    AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
343			    (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
344				break;
345			/* XXX 00100F42h (RB-C2) exists in both formats. */
346			erratum319 = 1;
347			break;
348		}
349		/* FALLTHROUGH */
350	case 0x11:
351	case 0x12:
352	case 0x14:
353	case 0x15:
354	case 0x16:
355		/*
356		 * There is only one sensor per package.
357		 */
358		sc->sc_ntemps = 1;
359
360		sc->sc_gettemp = amdtemp_gettemp;
361		break;
362	case 0x17:
363		sc->sc_ntemps = 1;
364		sc->sc_gettemp = amdtemp_gettemp17h;
365		sc->sc_smn = device_find_child(
366		    device_get_parent(dev), "amdsmn", -1);
367		if (sc->sc_smn == NULL) {
368			if (bootverbose)
369				device_printf(dev, "No SMN device found\n");
370			return (ENXIO);
371		}
372		break;
373	}
374
375	/* Find number of cores per package. */
376	sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
377	    (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
378	if (sc->sc_ncores > MAXCPU)
379		return (ENXIO);
380
381	if (erratum319)
382		device_printf(dev,
383		    "Erratum 319: temperature measurement may be inaccurate\n");
384	if (bootverbose)
385		device_printf(dev, "Found %d cores and %d sensors.\n",
386		    sc->sc_ncores,
387		    sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
388
389	/*
390	 * dev.amdtemp.N tree.
391	 */
392	unit = device_get_unit(dev);
393	snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
394	TUNABLE_INT_FETCH(tn, &sc->sc_offset);
395
396	sysctlctx = device_get_sysctl_ctx(dev);
397	SYSCTL_ADD_INT(sysctlctx,
398	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
399	    "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
400	    "Temperature sensor offset");
401	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
402	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
403	    "core0", CTLFLAG_RD, 0, "Core 0");
404
405	SYSCTL_ADD_PROC(sysctlctx,
406	    SYSCTL_CHILDREN(sysctlnode),
407	    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
408	    dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
409	    "Core 0 / Sensor 0 temperature");
410
411	if (sc->sc_ntemps > 1) {
412		SYSCTL_ADD_PROC(sysctlctx,
413		    SYSCTL_CHILDREN(sysctlnode),
414		    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
415		    dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
416		    "Core 0 / Sensor 1 temperature");
417
418		if (sc->sc_ncores > 1) {
419			sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
420			    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
421			    OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
422
423			SYSCTL_ADD_PROC(sysctlctx,
424			    SYSCTL_CHILDREN(sysctlnode),
425			    OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
426			    dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
427			    "Core 1 / Sensor 0 temperature");
428
429			SYSCTL_ADD_PROC(sysctlctx,
430			    SYSCTL_CHILDREN(sysctlnode),
431			    OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
432			    dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
433			    "Core 1 / Sensor 1 temperature");
434		}
435	}
436
437	/*
438	 * Try to create dev.cpu sysctl entries and setup intrhook function.
439	 * This is needed because the cpu driver may be loaded late on boot,
440	 * after us.
441	 */
442	amdtemp_intrhook(dev);
443	sc->sc_ich.ich_func = amdtemp_intrhook;
444	sc->sc_ich.ich_arg = dev;
445	if (config_intrhook_establish(&sc->sc_ich) != 0) {
446		device_printf(dev, "config_intrhook_establish failed!\n");
447		return (ENXIO);
448	}
449
450	return (0);
451}
452
453void
454amdtemp_intrhook(void *arg)
455{
456	struct amdtemp_softc *sc;
457	struct sysctl_ctx_list *sysctlctx;
458	device_t dev = (device_t)arg;
459	device_t acpi, cpu, nexus;
460	amdsensor_t sensor;
461	int i;
462
463	sc = device_get_softc(dev);
464
465	/*
466	 * dev.cpu.N.temperature.
467	 */
468	nexus = device_find_child(root_bus, "nexus", 0);
469	acpi = device_find_child(nexus, "acpi", 0);
470
471	for (i = 0; i < sc->sc_ncores; i++) {
472		if (sc->sc_sysctl_cpu[i] != NULL)
473			continue;
474		cpu = device_find_child(acpi, "cpu",
475		    device_get_unit(dev) * sc->sc_ncores + i);
476		if (cpu != NULL) {
477			sysctlctx = device_get_sysctl_ctx(cpu);
478
479			sensor = sc->sc_ntemps > 1 ?
480			    (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
481			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
482			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
483			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
484			    dev, sensor, amdtemp_sysctl, "IK",
485			    "Current temparature");
486		}
487	}
488	if (sc->sc_ich.ich_arg != NULL)
489		config_intrhook_disestablish(&sc->sc_ich);
490}
491
492int
493amdtemp_detach(device_t dev)
494{
495	struct amdtemp_softc *sc = device_get_softc(dev);
496	int i;
497
498	for (i = 0; i < sc->sc_ncores; i++)
499		if (sc->sc_sysctl_cpu[i] != NULL)
500			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
501
502	/* NewBus removes the dev.amdtemp.N tree by itself. */
503
504	return (0);
505}
506
507static int
508amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
509{
510	device_t dev = (device_t)arg1;
511	struct amdtemp_softc *sc = device_get_softc(dev);
512	amdsensor_t sensor = (amdsensor_t)arg2;
513	int32_t auxtemp[2], temp;
514	int error;
515
516	switch (sensor) {
517	case CORE0:
518		auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
519		auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
520		temp = imax(auxtemp[0], auxtemp[1]);
521		break;
522	case CORE1:
523		auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
524		auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
525		temp = imax(auxtemp[0], auxtemp[1]);
526		break;
527	default:
528		temp = sc->sc_gettemp(dev, sensor);
529		break;
530	}
531	error = sysctl_handle_int(oidp, &temp, 0, req);
532
533	return (error);
534}
535
536#define	AMDTEMP_ZERO_C_TO_K	2731
537
538static int32_t
539amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
540{
541	struct amdtemp_softc *sc = device_get_softc(dev);
542	uint32_t mask, offset, temp;
543
544	/* Set Sensor/Core selector. */
545	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
546	temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
547	switch (sensor) {
548	case CORE0_SENSOR1:
549		temp |= AMDTEMP_TTSR_SELSENSOR;
550		/* FALLTHROUGH */
551	case CORE0_SENSOR0:
552	case CORE0:
553		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
554			temp |= AMDTEMP_TTSR_SELCORE;
555		break;
556	case CORE1_SENSOR1:
557		temp |= AMDTEMP_TTSR_SELSENSOR;
558		/* FALLTHROUGH */
559	case CORE1_SENSOR0:
560	case CORE1:
561		if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
562			temp |= AMDTEMP_TTSR_SELCORE;
563		break;
564	}
565	pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
566
567	mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
568	offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
569	temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
570	temp = ((temp >> 14) & mask) * 5 / 2;
571	temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
572
573	return (temp);
574}
575
576static int32_t
577amdtemp_gettemp(device_t dev, amdsensor_t sensor)
578{
579	struct amdtemp_softc *sc = device_get_softc(dev);
580	uint32_t temp;
581
582	temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
583	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
584	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
585
586	return (temp);
587}
588
589static int32_t
590amdtemp_gettemp17h(device_t dev, amdsensor_t sensor)
591{
592	struct amdtemp_softc *sc = device_get_softc(dev);
593	uint32_t temp;
594	int error;
595
596	error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &temp);
597	KASSERT(error == 0, ("amdsmn_read"));
598
599	temp = ((temp >> 21) & 0x7ff) * 5 / 4;
600	temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
601
602	return (temp);
603}
604