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