55d54e5a05b68f6d5aeae89bd3385a289768b793
[dragonfly.git] / sys / dev / powermng / coretemp / coretemp.c
1 /*
2  * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.2 2007/08/23 10:53:03 des Exp $
27  * $DragonFly: src/sys/dev/powermng/coretemp/coretemp.c,v 1.3 2008/10/03 00:26:21 hasso Exp $
28  */
29
30 /*
31  * Device driver for Intel's On Die thermal sensor via MSR.
32  * First introduced in Intel's Core line of processors.
33  */
34
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/systm.h>
39 #include <sys/types.h>
40 #include <sys/module.h>
41 #include <sys/conf.h>
42 #include <sys/kernel.h>
43 #include <sys/sensors.h>
44 #include <sys/spinlock.h>
45 #include <sys/spinlock2.h>
46 #include <sys/proc.h>   /* for curthread */
47 #include <sys/sched.h>
48
49 #include <machine/specialreg.h>
50 #include <machine/cpufunc.h>
51 #include <machine/md_var.h>
52
53 static struct spinlock          coretemp_lock;
54
55 struct coretemp_softc {
56         struct ksensordev       sc_sensordev;
57         struct ksensor          sc_sensor;
58         device_t                sc_dev;
59         int                     sc_tjmax;
60 };
61
62 /*
63  * Device methods.
64  */
65 static void     coretemp_identify(driver_t *driver, device_t parent);
66 static int      coretemp_probe(device_t dev);
67 static int      coretemp_attach(device_t dev);
68 static int      coretemp_detach(device_t dev);
69
70 static int      coretemp_get_temp(device_t dev);
71 static void     coretemp_refresh(void *arg);
72
73 static device_method_t coretemp_methods[] = {
74         /* Device interface */
75         DEVMETHOD(device_identify,      coretemp_identify),
76         DEVMETHOD(device_probe,         coretemp_probe),
77         DEVMETHOD(device_attach,        coretemp_attach),
78         DEVMETHOD(device_detach,        coretemp_detach),
79
80         {0, 0}
81 };
82
83 static driver_t coretemp_driver = {
84         "coretemp",
85         coretemp_methods,
86         sizeof(struct coretemp_softc),
87 };
88
89 static devclass_t coretemp_devclass;
90 DRIVER_MODULE(coretemp, cpu, coretemp_driver, coretemp_devclass, NULL, NULL);
91
92 static void
93 coretemp_identify(driver_t *driver, device_t parent)
94 {
95         device_t child;
96         u_int regs[4];
97
98         /* Make sure we're not being doubly invoked. */
99         if (device_find_child(parent, "coretemp", -1) != NULL)
100                 return;
101
102         /* Check that CPUID is supported and the vendor is Intel.*/
103         if (cpu_high == 0 || strcmp(cpu_vendor, "GenuineIntel"))
104                 return;
105         /*
106          * CPUID 0x06 returns 1 if the processor has on-die thermal
107          * sensors. EBX[0:3] contains the number of sensors.
108          */
109         do_cpuid(0x06, regs);
110         if ((regs[0] & 0x1) != 1)
111                 return;
112
113         /*
114          * We add a child for each CPU since settings must be performed
115          * on each CPU in the SMP case.
116          */
117         child = device_add_child(parent, "coretemp", -1);
118         if (child == NULL)
119                 device_printf(parent, "add coretemp child failed\n");
120 }
121
122 static int
123 coretemp_probe(device_t dev)
124 {
125         if (resource_disabled("coretemp", 0))
126                 return (ENXIO);
127
128         device_set_desc(dev, "CPU On-Die Thermal Sensors");
129
130         return (BUS_PROBE_GENERIC);
131 }
132
133 static int
134 coretemp_attach(device_t dev)
135 {
136         struct coretemp_softc *sc = device_get_softc(dev);
137         device_t pdev;
138         uint64_t msr;
139         int cpu_model;
140         int cpu_mask;
141
142         sc->sc_dev = dev;
143         pdev = device_get_parent(dev);
144         cpu_model = (cpu_id >> 4) & 15;
145         /* extended model */
146         cpu_model += ((cpu_id >> 16) & 0xf) << 4;
147         cpu_mask = cpu_id & 15;
148
149         /*
150          * Check for errata AE18.
151          * "Processor Digital Thermal Sensor (DTS) Readout stops
152          *  updating upon returning from C3/C4 state."
153          *
154          * Adapted from the Linux coretemp driver.
155          */
156         if (cpu_model == 0xe && cpu_mask < 0xc) {
157                 msr = rdmsr(MSR_BIOS_SIGN);
158                 msr = msr >> 32;
159                 if (msr < 0x39) {
160                         device_printf(dev, "not supported (Intel errata "
161                             "AE18), try updating your BIOS\n");
162                         return (ENXIO);
163                 }
164         }
165         /*
166          * On some Core 2 CPUs, there's an undocumented MSR that
167          * can tell us if Tj(max) is 100 or 85.
168          *
169          * The if-clause for CPUs having the MSR_IA32_EXT_CONFIG was adapted
170          * from the Linux coretemp driver.
171          */
172         sc->sc_tjmax = 100;
173         if ((cpu_model == 0xf && cpu_mask >= 2) || cpu_model == 0xe) {
174                 msr = rdmsr(MSR_IA32_EXT_CONFIG);
175                 if (msr & (1 << 30))
176                         sc->sc_tjmax = 85;
177         }
178
179         /*
180          * Add hw.sensors.cpuN.temp0 MIB.
181          */
182         strlcpy(sc->sc_sensordev.xname, device_get_nameunit(pdev),
183             sizeof(sc->sc_sensordev.xname));
184         sc->sc_sensor.type = SENSOR_TEMP;
185         sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
186         if (sensor_task_register(sc, coretemp_refresh, 2)) {
187                 device_printf(dev, "unable to register update task\n");
188                 return (ENXIO);
189         }
190         sensordev_install(&sc->sc_sensordev);
191         spin_init(&coretemp_lock);
192
193         return (0);
194 }
195
196 static int
197 coretemp_detach(device_t dev)
198 {
199         struct coretemp_softc *sc = device_get_softc(dev);
200
201         spin_lock_wr(&coretemp_lock);
202         sensordev_deinstall(&sc->sc_sensordev);
203         sensor_task_unregister(sc);
204         spin_unlock_wr(&coretemp_lock);
205         spin_uninit(&coretemp_lock);
206
207         return (0);
208 }
209
210
211 static int
212 coretemp_get_temp(device_t dev)
213 {
214         uint64_t msr;
215         int temp, cpu, origcpu;
216         struct coretemp_softc *sc = device_get_softc(dev);
217         char stemp[16];
218
219         cpu = device_get_unit(device_get_parent(dev));
220
221         /*
222          * Bind to specific CPU to read the correct temperature.
223          * If not all CPUs are initialised, then only read from
224          * cpu0, returning -1 on all other CPUs.
225          */
226         if (ncpus > 1) {
227                 origcpu = mycpuid;
228                 lwkt_migratecpu(cpu);
229
230                 spin_lock_rd(&coretemp_lock);
231                 msr = rdmsr(MSR_THERM_STATUS);
232                 spin_unlock_rd(&coretemp_lock);
233
234                 lwkt_migratecpu(origcpu);
235         } else if (cpu != 0)
236                 return (-1);
237         else
238                 msr = rdmsr(MSR_THERM_STATUS);
239
240         /*
241          * Check for Thermal Status and Thermal Status Log.
242          */
243         if ((msr & 0x3) == 0x3)
244                 device_printf(dev, "PROCHOT asserted\n");
245
246         /*
247          * Bit 31 contains "Reading valid"
248          */
249         if (((msr >> 31) & 0x1) == 1) {
250                 /*
251                  * Starting on bit 16 and ending on bit 22.
252                  */
253                 temp = sc->sc_tjmax - ((msr >> 16) & 0x7f);
254         } else
255                 temp = -1;
256
257         /*
258          * Check for Critical Temperature Status and Critical
259          * Temperature Log.
260          * It doesn't really matter if the current temperature is
261          * invalid because the "Critical Temperature Log" bit will
262          * tell us if the Critical Temperature has been reached in
263          * past. It's not directly related to the current temperature.
264          *
265          * If we reach a critical level, allow devctl(4) to catch this
266          * and shutdown the system.
267          */
268         if (((msr >> 4) & 0x3) == 0x3) {
269                 device_printf(dev, "critical temperature detected, "
270                     "suggest system shutdown\n");
271                 ksnprintf(stemp, sizeof(stemp), "%d", temp);
272                 devctl_notify("coretemp", "Thermal", stemp, "notify=0xcc");
273         }
274
275         return (temp);
276 }
277
278 static void
279 coretemp_refresh(void *arg)
280 {
281         struct coretemp_softc *sc = arg;
282         device_t dev = sc->sc_dev;
283         struct ksensor *s = &sc->sc_sensor;
284         int temp;
285
286         temp = coretemp_get_temp(dev);
287
288         if (temp == -1) {
289                 s->flags |= SENSOR_FINVALID;
290                 s->value = 0;
291         } else {
292                 s->flags &= ~SENSOR_FINVALID;
293                 s->value = temp * 1000000 + 273150000;
294         }
295 }