1/* $NetBSD: coretemp.c,v 1.35 2016/07/07 06:55:40 msaitoh Exp $ */
2
3/*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*-
34 * Copyright (c) 2007 Juan Romero Pardines.
35 * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org>
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
49 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
50 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
51 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
52 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
53 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
55 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
56 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57 * POSSIBILITY OF SUCH DAMAGE.
58 *
59 * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.4 2007/10/15 20:00:21 netchild Exp $
60 *
61 */
62
63#include <sys/cdefs.h>
64__KERNEL_RCSID(0, "$NetBSD: coretemp.c,v 1.35 2016/07/07 06:55:40 msaitoh Exp $");
65
66#include <sys/param.h>
67#include <sys/device.h>
68#include <sys/cpu.h>
69#include <sys/module.h>
70#include <sys/xcall.h>
71
72#include <dev/sysmon/sysmonvar.h>
73
74#include <machine/cpuvar.h>
75#include <machine/cpufunc.h>
76#include <machine/cputypes.h>
77#include <machine/specialreg.h>
78
79#define MSR_THERM_STATUS_STA __BIT(0)
80#define MSR_THERM_STATUS_LOG __BIT(1)
81#define MSR_THERM_STATUS_PROCHOT_EVT __BIT(2)
82#define MSR_THERM_STATUS_PROCHOT_LOG __BIT(3)
83#define MSR_THERM_STATUS_CRIT_STA __BIT(4)
84#define MSR_THERM_STATUS_CRIT_LOG __BIT(5)
85#define MSR_THERM_STATUS_TRIP1_STA __BIT(6)
86#define MSR_THERM_STATUS_TRIP1_LOG __BIT(7)
87#define MSR_THERM_STATUS_TRIP2_STA __BIT(8)
88#define MSR_THERM_STATUS_TRIP2_LOG __BIT(9)
89#define MSR_THERM_STATUS_READOUT __BITS(16, 22)
90#define MSR_THERM_STATUS_RESOLUTION __BITS(27, 30)
91#define MSR_THERM_STATUS_VALID __BIT(31)
92
93#define MSR_THERM_INTR_HITEMP __BIT(0)
94#define MSR_THERM_INTR_LOTEMPT __BIT(1)
95#define MSR_THERM_INTR_PROCHOT __BIT(2)
96#define MSR_THERM_INTR_FORCPR __BIT(3)
97#define MSR_THERM_INTR_OVERHEAT __BIT(4)
98#define MSR_THERM_INTR_TRIP1_VAL __BITS(8, 14)
99#define MSR_THERM_INTR_TRIP1 __BIT(15)
100#define MSR_THERM_INTR_TRIP2_VAL __BITS(16, 22)
101#define MSR_THERM_INTR_TRIP2 __BIT(23)
102
103#define MSR_TEMP_TARGET_READOUT __BITS(16, 23)
104
105static int coretemp_match(device_t, cfdata_t, void *);
106static void coretemp_attach(device_t, device_t, void *);
107static int coretemp_detach(device_t, int);
108static int coretemp_quirks(struct cpu_info *);
109static void coretemp_tjmax(device_t);
110static void coretemp_refresh(struct sysmon_envsys *, envsys_data_t *);
111static void coretemp_refresh_xcall(void *, void *);
112
113struct coretemp_softc {
114 device_t sc_dev;
115 struct cpu_info *sc_ci;
116 struct sysmon_envsys *sc_sme;
117 envsys_data_t sc_sensor;
118 int sc_tjmax;
119};
120
121CFATTACH_DECL_NEW(coretemp, sizeof(struct coretemp_softc),
122 coretemp_match, coretemp_attach, coretemp_detach, NULL);
123
124static int
125coretemp_match(device_t parent, cfdata_t cf, void *aux)
126{
127 struct cpufeature_attach_args *cfaa = aux;
128 struct cpu_info *ci = cfaa->ci;
129 uint32_t regs[4];
130
131 if (strcmp(cfaa->name, "temperature") != 0)
132 return 0;
133
134 if (cpu_vendor != CPUVENDOR_INTEL || cpuid_level < 0x06)
135 return 0;
136
137 /*
138 * Only attach on the first SMT ID.
139 */
140 if (ci->ci_smt_id != 0)
141 return 0;
142
143 /*
144 * CPUID 0x06 returns 1 if the processor
145 * has on-die thermal sensors. EBX[0:3]
146 * contains the number of sensors.
147 */
148 x86_cpuid(0x06, regs);
149
150 if ((regs[0] & CPUID_DSPM_DTS) == 0)
151 return 0;
152
153 return coretemp_quirks(ci);
154}
155
156static void
157coretemp_attach(device_t parent, device_t self, void *aux)
158{
159 struct coretemp_softc *sc = device_private(self);
160 struct cpufeature_attach_args *cfaa = aux;
161 struct cpu_info *ci = cfaa->ci;
162 uint64_t msr;
163
164 sc->sc_ci = ci;
165 sc->sc_dev = self;
166
167 msr = rdmsr(MSR_THERM_STATUS);
168 msr = __SHIFTOUT(msr, MSR_THERM_STATUS_RESOLUTION);
169
170 aprint_naive("\n");
171 aprint_normal(": thermal sensor, %u C resolution", (uint32_t)msr);
172
173 sc->sc_sensor.units = ENVSYS_STEMP;
174 sc->sc_sensor.state = ENVSYS_SINVALID;
175 sc->sc_sensor.flags = ENVSYS_FMONCRITICAL | ENVSYS_FHAS_ENTROPY;
176
177 (void)pmf_device_register(self, NULL, NULL);
178 (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc),
179 "%s temperature", device_xname(ci->ci_dev));
180
181 sc->sc_sme = sysmon_envsys_create();
182
183 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
184 goto fail;
185
186 sc->sc_sme->sme_cookie = sc;
187 sc->sc_sme->sme_name = device_xname(self);
188 sc->sc_sme->sme_refresh = coretemp_refresh;
189
190 if (sysmon_envsys_register(sc->sc_sme) != 0)
191 goto fail;
192
193 coretemp_tjmax(self);
194 aprint_verbose(", Tjmax=%d", sc->sc_tjmax);
195 aprint_normal("\n");
196 return;
197
198fail:
199 sysmon_envsys_destroy(sc->sc_sme);
200 sc->sc_sme = NULL;
201 aprint_normal("\n");
202}
203
204static int
205coretemp_detach(device_t self, int flags)
206{
207 struct coretemp_softc *sc = device_private(self);
208
209 if (sc->sc_sme != NULL)
210 sysmon_envsys_unregister(sc->sc_sme);
211
212 pmf_device_deregister(self);
213
214 return 0;
215}
216
217static int
218coretemp_quirks(struct cpu_info *ci)
219{
220 uint32_t model, stepping;
221 uint64_t msr;
222
223 model = CPUID_TO_MODEL(ci->ci_signature);
224 stepping = CPUID_TO_STEPPING(ci->ci_signature);
225
226 /*
227 * Check if the MSR contains thermal
228 * reading valid bit, this avoid false
229 * positives on systems that fake up
230 * a compatible CPU that doesn't have
231 * access to these MSRs; such as VMWare.
232 */
233 msr = rdmsr(MSR_THERM_STATUS);
234
235 if ((msr & MSR_THERM_STATUS_VALID) == 0)
236 return 0;
237
238 /*
239 * Check for errata AE18, "Processor Digital
240 * Thermal Sensor (DTS) Readout Stops Updating
241 * upon Returning from C3/C4 State".
242 *
243 * Adapted from the Linux coretemp driver.
244 */
245 if (model == 0x0E && stepping < 0x0C) {
246
247 msr = rdmsr(MSR_BIOS_SIGN);
248 msr = msr >> 32;
249
250 if (msr < 0x39)
251 return 0;
252 }
253
254 return 1;
255}
256
257void
258coretemp_tjmax(device_t self)
259{
260 struct coretemp_softc *sc = device_private(self);
261 struct cpu_info *ci = sc->sc_ci;
262 uint32_t model, stepping;
263 uint64_t msr;
264
265 model = CPUID_TO_MODEL(ci->ci_signature);
266 stepping = CPUID_TO_STEPPING(ci->ci_signature);
267
268 sc->sc_tjmax = 100;
269
270 /*
271 * On some Core 2 CPUs, there is an undocumented
272 * MSR that tells if Tj(max) is 100 or 85. Note
273 * that MSR_IA32_EXT_CONFIG is not safe on all CPUs.
274 */
275 if ((model == 0x0F && stepping >= 2) || (model == 0x0E)) {
276
277 if (rdmsr_safe(MSR_IA32_EXT_CONFIG, &msr) == EFAULT)
278 return;
279
280 if ((msr & __BIT(30)) != 0) {
281 sc->sc_tjmax = 85;
282 return;
283 }
284 } else if (model == 0x17 && stepping == 0x06) {
285 /* The mobile Penryn family. */
286 sc->sc_tjmax = 105;
287 return;
288 } else {
289 /*
290 * Attempt to get Tj(max) from IA32_TEMPERATURE_TARGET,
291 * but only consider the interval [70, 110] C as valid.
292 * It is not fully known which CPU models have the MSR.
293 */
294 if (rdmsr_safe(MSR_TEMPERATURE_TARGET, &msr) == EFAULT)
295 return;
296
297 msr = __SHIFTOUT(msr, MSR_TEMP_TARGET_READOUT);
298
299 if (msr >= 70 && msr <= 110) {
300 sc->sc_tjmax = msr;
301 return;
302 }
303 }
304}
305
306static void
307coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
308{
309 struct coretemp_softc *sc = sme->sme_cookie;
310 uint64_t xc;
311
312 xc = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci);
313 xc_wait(xc);
314}
315
316static void
317coretemp_refresh_xcall(void *arg0, void *arg1)
318{
319 struct coretemp_softc *sc = arg0;
320 envsys_data_t *edata = arg1;
321 uint64_t msr;
322
323 msr = rdmsr(MSR_THERM_STATUS);
324
325 if ((msr & MSR_THERM_STATUS_VALID) == 0)
326 edata->state = ENVSYS_SINVALID;
327 else {
328 /*
329 * The temperature is computed by
330 * subtracting the reading by Tj(max).
331 */
332 edata->value_cur = sc->sc_tjmax;
333 edata->value_cur -= __SHIFTOUT(msr, MSR_THERM_STATUS_READOUT);
334
335 /*
336 * Convert to mK.
337 */
338 edata->value_cur *= 1000000;
339 edata->value_cur += 273150000;
340 edata->state = ENVSYS_SVALID;
341 }
342
343 if ((msr & MSR_THERM_STATUS_CRIT_STA) != 0)
344 edata->state = ENVSYS_SCRITICAL;
345}
346
347MODULE(MODULE_CLASS_DRIVER, coretemp, "sysmon_envsys");
348
349#ifdef _MODULE
350#include "ioconf.c"
351#endif
352
353static int
354coretemp_modcmd(modcmd_t cmd, void *aux)
355{
356 int error = 0;
357
358 switch (cmd) {
359 case MODULE_CMD_INIT:
360#ifdef _MODULE
361 error = config_init_component(cfdriver_ioconf_coretemp,
362 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp);
363#endif
364 return error;
365 case MODULE_CMD_FINI:
366#ifdef _MODULE
367 error = config_fini_component(cfdriver_ioconf_coretemp,
368 cfattach_ioconf_coretemp, cfdata_ioconf_coretemp);
369#endif
370 return error;
371 default:
372 return ENOTTY;
373 }
374}
375