xref: /netbsd/sys/arch/x86/x86/coretemp.c (revision 6550d01e)
1 /* $NetBSD: coretemp.c,v 1.16 2010/08/25 05:07:43 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Juan Romero Pardines.
5  * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.4 2007/10/15 20:00:21 netchild Exp $
30  *
31  */
32 
33 /*
34  * Device driver for Intel's On Die thermal sensor via MSR.
35  * First introduced in Intel's Core line of processors.
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: coretemp.c,v 1.16 2010/08/25 05:07:43 jruoho Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/device.h>
43 #include <sys/kmem.h>
44 #include <sys/xcall.h>
45 #include <sys/cpu.h>
46 
47 #include <dev/sysmon/sysmonvar.h>
48 
49 #include <machine/cpuvar.h>
50 #include <machine/specialreg.h>
51 #include <machine/cpufunc.h>
52 
53 struct coretemp_softc {
54 	struct cpu_info		*sc_ci;
55 	struct sysmon_envsys 	*sc_sme;
56 	envsys_data_t 		sc_sensor;
57 	char			sc_dvname[32];
58 	int 			sc_tjmax;
59 };
60 
61 static void	coretemp_refresh(struct sysmon_envsys *, envsys_data_t *);
62 static void	coretemp_refresh_xcall(void *, void *);
63 
64 void
65 coretemp_register(struct cpu_info *ci)
66 {
67 	struct coretemp_softc *sc;
68 	uint32_t regs[4];
69 	uint64_t msr;
70 	int cpumodel, cpuextmodel, cpumask;
71 
72 	/*
73 	 * Don't attach on anything but the first SMT ID.
74 	 */
75 	if (ci->ci_smt_id != 0)
76 		return;
77 
78 	/*
79 	 * CPUID 0x06 returns 1 if the processor has on-die thermal
80 	 * sensors. EBX[0:3] contains the number of sensors.
81 	 */
82 	x86_cpuid(0x06, regs);
83 	if ((regs[0] & CPUID_DSPM_DTS) == 0)
84 		return;
85 
86 	sc = kmem_zalloc(sizeof(struct coretemp_softc), KM_NOSLEEP);
87 	if (!sc)
88 		return;
89 
90 	(void)snprintf(sc->sc_dvname, sizeof(sc->sc_dvname), "coretemp%d",
91 	    (int)device_unit(ci->ci_dev));
92 	cpumodel = CPUID2MODEL(ci->ci_signature);
93 	/* extended model */
94 	cpuextmodel = CPUID2EXTMODEL(ci->ci_signature);
95 	cpumask = ci->ci_signature & 15;
96 
97 	/*
98 	 * Check for errata AE18.
99 	 * "Processor Digital Thermal Sensor (DTS) Readout stops
100 	 *  updating upon returning from C3/C4 state."
101 	 *
102 	 * Adapted from the Linux coretemp driver.
103 	 */
104 	if (cpumodel == 0xe && cpumask < 0xc) {
105 		msr = rdmsr(MSR_BIOS_SIGN);
106 		msr = msr >> 32;
107 		if (msr < 0x39) {
108 			aprint_debug("%s: not supported (Intel errata AE18), "
109 			    "try updating your BIOS\n", sc->sc_dvname);
110 			goto bad;
111 		}
112 	}
113 
114 	/*
115 	 * On some Core 2 CPUs, there's an undocumented MSR that
116 	 * can tell us if Tj(max) is 100 or 85.
117 	 *
118 	 * The if-clause for CPUs having the MSR_IA32_EXT_CONFIG was adapted
119 	 * from the Linux coretemp driver.
120 	 *
121 	 * MSR_IA32_EXT_CONFIG is NOT safe on all CPUs
122 	 */
123 	sc->sc_tjmax = 100;
124 	if ((cpumodel == 0xf && cpumask >= 2) ||
125 	    (cpumodel == 0xe && cpuextmodel != 1)) {
126 		msr = rdmsr(MSR_IA32_EXT_CONFIG);
127 		if (msr & (1 << 30))
128 			sc->sc_tjmax = 85;
129 	}
130 
131 	/*
132 	 * Check if the MSR contains thermal reading valid bit, this
133 	 * avoid false positives on systems that fake up a compatible
134 	 * CPU that doesn't have access to these MSRs; such as VMWare.
135 	 */
136 	msr = rdmsr(MSR_THERM_STATUS);
137 	if ((msr & __BIT(31)) == 0)
138 		goto bad;
139 
140 	sc->sc_ci = ci;
141 
142 	/*
143 	 * Only a temperature sensor and monitor for a critical state.
144 	 */
145 	sc->sc_sensor.units = ENVSYS_STEMP;
146 	sc->sc_sensor.flags = ENVSYS_FMONCRITICAL;
147 	(void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc),
148 	    "%s temperature", device_xname(ci->ci_dev));
149 
150 	sc->sc_sme = sysmon_envsys_create();
151 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
152 		sysmon_envsys_destroy(sc->sc_sme);
153 		goto bad;
154 	}
155 
156 	/*
157 	 * Hook into the system monitor.
158 	 */
159 	sc->sc_sme->sme_name = sc->sc_dvname;
160 	sc->sc_sme->sme_cookie = sc;
161 	sc->sc_sme->sme_refresh = coretemp_refresh;
162 
163 	if (sysmon_envsys_register(sc->sc_sme)) {
164 		aprint_error("%s: unable to register with sysmon\n",
165 		    sc->sc_dvname);
166 		sysmon_envsys_destroy(sc->sc_sme);
167 		goto bad;
168 	}
169 
170 	return;
171 
172 bad:
173 	kmem_free(sc, sizeof(struct coretemp_softc));
174 }
175 
176 static void
177 coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
178 {
179 	struct coretemp_softc *sc = sme->sme_cookie;
180 	uint64_t where;
181 
182 	where = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci);
183 	xc_wait(where);
184 }
185 
186 static void
187 coretemp_refresh_xcall(void *arg0, void *arg1)
188 {
189 	struct coretemp_softc *sc = (struct coretemp_softc *)arg0;
190 	envsys_data_t *edata = (envsys_data_t *)arg1;
191 	uint64_t msr;
192 
193 	/*
194 	 * The digital temperature reading is located at bit 16
195 	 * of MSR_THERM_STATUS.
196 	 *
197 	 * There is a bit on that MSR that indicates whether the
198 	 * temperature is valid or not.
199 	 *
200 	 * The temperature is computed by subtracting the temperature
201 	 * reading by Tj(max).
202 	 */
203 	msr = rdmsr(MSR_THERM_STATUS);
204 
205 	/*
206 	 * Check for Thermal Status and Thermal Status Log.
207 	 */
208 	if ((msr & 0x3) == 0x3)
209 		aprint_debug("%s: PROCHOT asserted\n", sc->sc_dvname);
210 
211 	/*
212 	 * Bit 31 contains "Reading valid"
213 	 */
214 	if (((msr >> 31) & 0x1) == 1) {
215 		/*
216 		 * Starting on bit 16 and ending on bit 22.
217 		 */
218 		edata->value_cur = sc->sc_tjmax - ((msr >> 16) & 0x7f);
219 		/*
220 		 * Convert to mK.
221 		 */
222 		edata->value_cur *= 1000000;
223 		edata->value_cur += 273150000;
224 		edata->state = ENVSYS_SVALID;
225 	} else
226 		edata->state = ENVSYS_SINVALID;
227 
228 	/*
229 	 * Check for Critical Temperature Status and Critical
230 	 * Temperature Log.
231 	 * It doesn't really matter if the current temperature is
232 	 * invalid because the "Critical Temperature Log" bit will
233 	 * tell us if the Critical Temperature has been reached in
234 	 * past. It's not directly related to the current temperature.
235 	 *
236 	 * If we reach a critical level, send a critical event to
237 	 * powerd(8) (if running).
238 	 */
239 	if (((msr >> 4) & 0x3) == 0x3)
240 		edata->state = ENVSYS_SCRITICAL;
241 }
242