1/* $NetBSD: coretemp.c,v 1.27 2011/09/24 10:52:56 jruoho 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.27 2011/09/24 10:52:56 jruoho 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\n", (uint32_t)msr);
172
173	sc->sc_sensor.units = ENVSYS_STEMP;
174	sc->sc_sensor.flags = ENVSYS_FMONCRITICAL;
175	sc->sc_sensor.state = ENVSYS_SINVALID;
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	return;
195
196fail:
197	sysmon_envsys_destroy(sc->sc_sme);
198	sc->sc_sme = NULL;
199}
200
201static int
202coretemp_detach(device_t self, int flags)
203{
204	struct coretemp_softc *sc = device_private(self);
205
206	if (sc->sc_sme != NULL)
207		sysmon_envsys_unregister(sc->sc_sme);
208
209	pmf_device_deregister(self);
210
211	return 0;
212}
213
214static int
215coretemp_quirks(struct cpu_info *ci)
216{
217	uint32_t model, stepping;
218	uint64_t msr;
219
220	model = CPUID2MODEL(ci->ci_signature);
221	stepping = CPUID2STEPPING(ci->ci_signature);
222
223	/*
224	 * Check if the MSR contains thermal
225	 * reading valid bit, this avoid false
226	 * positives on systems that fake up
227	 * a compatible CPU that doesn't have
228	 * access to these MSRs; such as VMWare.
229	 */
230	msr = rdmsr(MSR_THERM_STATUS);
231
232	if ((msr & MSR_THERM_STATUS_VALID) == 0)
233		return 0;
234
235	/*
236	 * Check for errata AE18, "Processor Digital
237	 * Thermal Sensor (DTS) Readout Stops Updating
238	 * upon Returning from C3/C4 State".
239	 *
240	 * Adapted from the Linux coretemp driver.
241	 */
242	if (model == 0x0E && stepping < 0x0C) {
243
244		msr = rdmsr(MSR_BIOS_SIGN);
245		msr = msr >> 32;
246
247		if (msr < 0x39)
248			return 0;
249	}
250
251	return 1;
252}
253
254void
255coretemp_tjmax(device_t self)
256{
257	struct coretemp_softc *sc = device_private(self);
258	struct cpu_info *ci = sc->sc_ci;
259	uint32_t extmodel, model, stepping;
260	uint64_t msr;
261
262	model = CPUID2MODEL(ci->ci_signature);
263	extmodel = CPUID2EXTMODEL(ci->ci_signature);
264	stepping = CPUID2STEPPING(ci->ci_signature);
265
266	sc->sc_tjmax = 100;
267
268	/*
269	 * The mobile Penryn family.
270	 */
271	if (model == 0x17 && stepping == 0x06) {
272		sc->sc_tjmax = 105;
273		return;
274	}
275
276	/*
277	 * On some Core 2 CPUs, there is an undocumented
278	 * MSR that tells if Tj(max) is 100 or 85. Note
279	 * that MSR_IA32_EXT_CONFIG is not safe on all CPUs.
280	 */
281	if ((model == 0x0F && stepping >= 2) ||
282	    (model == 0x0E && extmodel != 1)) {
283
284		if (rdmsr_safe(MSR_IA32_EXT_CONFIG, &msr) == EFAULT)
285			return;
286
287		if ((msr & __BIT(30)) != 0) {
288			sc->sc_tjmax = 85;
289			return;
290		}
291	}
292
293	/*
294	 * Attempt to get Tj(max) from IA32_TEMPERATURE_TARGET,
295	 * but only consider the interval [70, 100] C as valid.
296	 * It is not fully known which CPU models have the MSR.
297	 */
298	if (model == 0x0E && extmodel != 0) {
299
300		if (rdmsr_safe(MSR_TEMPERATURE_TARGET, &msr) == EFAULT)
301			return;
302
303		msr = __SHIFTOUT(msr, MSR_TEMP_TARGET_READOUT);
304
305		if (msr >= 70 && msr <= 100) {
306			sc->sc_tjmax = msr;
307			return;
308		}
309	}
310}
311
312static void
313coretemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
314{
315	struct coretemp_softc *sc = sme->sme_cookie;
316	uint64_t xc;
317
318	xc = xc_unicast(0, coretemp_refresh_xcall, sc, edata, sc->sc_ci);
319	xc_wait(xc);
320}
321
322static void
323coretemp_refresh_xcall(void *arg0, void *arg1)
324{
325        struct coretemp_softc *sc = arg0;
326	envsys_data_t *edata = arg1;
327	uint64_t msr;
328
329	msr = rdmsr(MSR_THERM_STATUS);
330
331	if ((msr & MSR_THERM_STATUS_VALID) == 0)
332		edata->state = ENVSYS_SINVALID;
333	else {
334		/*
335		 * The temperature is computed by
336		 * subtracting the reading by Tj(max).
337		 */
338		edata->value_cur = sc->sc_tjmax;
339		edata->value_cur -= __SHIFTOUT(msr, MSR_THERM_STATUS_READOUT);
340
341		/*
342		 * Convert to mK.
343		 */
344		edata->value_cur *= 1000000;
345		edata->value_cur += 273150000;
346		edata->state = ENVSYS_SVALID;
347	}
348
349	if ((msr & MSR_THERM_STATUS_CRIT_STA) != 0)
350		edata->state = ENVSYS_SCRITICAL;
351}
352
353MODULE(MODULE_CLASS_DRIVER, coretemp, NULL);
354
355#ifdef _MODULE
356#include "ioconf.c"
357#endif
358
359static int
360coretemp_modcmd(modcmd_t cmd, void *aux)
361{
362	int error = 0;
363
364	switch (cmd) {
365	case MODULE_CMD_INIT:
366#ifdef _MODULE
367		error = config_init_component(cfdriver_ioconf_coretemp,
368		    cfattach_ioconf_coretemp, cfdata_ioconf_coretemp);
369#endif
370		return error;
371	case MODULE_CMD_FINI:
372#ifdef _MODULE
373		error = config_fini_component(cfdriver_ioconf_coretemp,
374		    cfattach_ioconf_coretemp, cfdata_ioconf_coretemp);
375#endif
376		return error;
377	default:
378		return ENOTTY;
379	}
380}
381