CoreTemp
Attachment 'coretemp.c'
Download 1 /*
2 * coretemp.c - Linux kernel module for hardware monitoring
3 *
4 * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz>
5 *
6 * Inspired from many hwmon drivers
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301 USA.
22 */
23
24 #include <linux/module.h>
25 #include <linux/delay.h>
26 #include <linux/init.h>
27 #include <linux/slab.h>
28 #include <linux/jiffies.h>
29 #include <linux/hwmon.h>
30 #include <linux/sysfs.h>
31 #include <linux/hwmon-sysfs.h>
32 #include <linux/err.h>
33 #include <linux/mutex.h>
34 #include <linux/list.h>
35 #include <linux/platform_device.h>
36 #include <linux/cpu.h>
37 #include <asm/msr.h>
38 #include <asm/processor.h>
39
40 #define DRVNAME "coretemp"
41 #undef CONFIG_HOTPLUG_CPU
42
43 /*
44 Following part ripped from the msr.c. It should be merged with generic MSR
45 infrastructure (once ready)
46 */
47
48 static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
49 {
50 int err;
51
52 err = rdmsr_safe(reg, eax, edx);
53 if (err)
54 err = -EIO;
55 return err;
56 }
57
58 #ifdef CONFIG_SMP
59
60 struct msr_command {
61 int cpu;
62 int err;
63 u32 reg;
64 u32 data[2];
65 };
66
67 static void msr_smp_rdmsr(void *cmd_block)
68 {
69 struct msr_command *cmd = (struct msr_command *)cmd_block;
70
71 if (cmd->cpu == smp_processor_id())
72 cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
73 }
74
75 static inline int msr_read(int cpu, u32 reg, u32 * eax, u32 * edx)
76 {
77 struct msr_command cmd;
78 int ret;
79
80 preempt_disable();
81 if (cpu == smp_processor_id()) {
82 ret = rdmsr_eio(reg, eax, edx);
83 } else {
84 cmd.cpu = cpu;
85 cmd.reg = reg;
86
87 smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
88
89 *eax = cmd.data[0];
90 *edx = cmd.data[1];
91
92 ret = cmd.err;
93 }
94 preempt_enable();
95 return ret;
96 }
97
98 #else /* ! CONFIG_SMP */
99
100 static inline int msr_read(int cpu, u32 reg, u32 *eax, u32 *edx)
101 {
102 return rdmsr_eio(reg, eax, edx);
103 }
104
105 #endif /* ! CONFIG_SMP */
106
107 /* cut here */
108
109 typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW;
110
111 /*
112 * Functions declaration
113 */
114
115 static struct coretemp_data *coretemp_update_device(struct device *dev);
116
117 struct coretemp_data {
118 struct class_device *class_dev;
119 struct mutex update_lock;
120 const char *name;
121 u32 id;
122 char valid; /* zero until following fields are valid */
123 unsigned long last_updated; /* in jiffies */
124 int temp;
125 int tjmax;
126 /* registers values */
127 u32 therm_status;
128 };
129
130 static struct coretemp_data *coretemp_update_device(struct device *dev);
131
132 /*
133 * Sysfs stuff
134 */
135
136 static ssize_t show_name(struct device *dev, struct device_attribute
137 *devattr, char *buf)
138 {
139 int ret;
140 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
141 struct coretemp_data *data = dev_get_drvdata(dev);
142
143 if (attr->index == SHOW_NAME)
144 ret = sprintf(buf, "%s\n", data->name);
145 else /* show label */
146 ret = sprintf(buf, "Core %d\n", data->id);
147 return ret;
148 }
149
150 static ssize_t show_alarm(struct device *dev, struct device_attribute
151 *devattr, char *buf)
152 {
153 struct coretemp_data *data = coretemp_update_device(dev);
154 /* read the Out-of-spec log, never clear */
155 return sprintf(buf, "%d\n", (data->therm_status >> 5) & 1);
156 }
157
158 static ssize_t show_temp(struct device *dev,
159 struct device_attribute *devattr, char *buf)
160 {
161 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
162 struct coretemp_data *data = coretemp_update_device(dev);
163 return sprintf(buf, "%d\n",
164 attr->index ==
165 SHOW_TEMP ? data->temp : data->tjmax);
166 }
167
168 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
169 SHOW_TEMP);
170 static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
171 SHOW_TJMAX);
172 static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
173 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
174 static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
175
176 static struct attribute *coretemp_attributes[] = {
177 &sensor_dev_attr_name.dev_attr.attr,
178 &sensor_dev_attr_temp1_label.dev_attr.attr,
179 &dev_attr_temp1_crit_alarm.attr,
180 &sensor_dev_attr_temp1_input.dev_attr.attr,
181 &sensor_dev_attr_temp1_crit.dev_attr.attr,
182 NULL
183 };
184
185 static const struct attribute_group coretemp_group = {
186 .attrs = coretemp_attributes,
187 };
188
189 static struct coretemp_data *coretemp_update_device(struct device *dev)
190 {
191 struct coretemp_data *data = dev_get_drvdata(dev);
192
193 mutex_lock(&data->update_lock);
194
195 if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
196 u32 eax, edx;
197
198 data->valid = 0;
199 msr_read(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
200 data->therm_status = eax;
201
202 /* update only if data has been valid */
203 if (eax & 0x80000000) {
204 data->temp = data->tjmax - (((data->therm_status >> 16)
205 & 0x7f) * 1000);
206 data->valid = 1;
207 }
208 data->last_updated = jiffies;
209 }
210
211 mutex_unlock(&data->update_lock);
212 return data;
213 }
214
215 static int __devinit coretemp_probe(struct platform_device *pdev)
216 {
217 struct coretemp_data *data;
218 struct cpuinfo_x86 *c = &(cpu_data)[pdev->id];
219 int err;
220 u32 eax, edx;
221
222 if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) {
223 err = -ENOMEM;
224 dev_err(&pdev->dev, "Out of memory\n");
225 goto exit;
226 }
227
228 data->id = pdev->id;
229 data->name = "coretemp";
230 mutex_init(&data->update_lock);
231 /* Tjmax default is 100C */
232 data->tjmax = 100000;
233
234 /* Some processors have Tjmax 85 following magic should detect it
235 Intel won't disclose the information without signed NDA, but
236 individuals cannot sign it. Catch(ed) 22.
237 */
238
239 if (((c->x86_model == 0xf) && (c->x86_mask > 3 )) ||
240 (c->x86_model == 0xe)) {
241
242 err = msr_read(data->id, 0xee, &eax, &edx);
243 if (err) {
244 dev_warn(&pdev->dev,
245 "Unable to access MSR 0xEE, Tjmax left at %d\n",
246 data->tjmax);
247 } else if (eax & 0x40000000) {
248 data->tjmax = 85000;
249 }
250 }
251
252 /* test if we can access the THERM_STATUS MSR */
253 err = msr_read(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
254
255 if (err) {
256 dev_err(&pdev->dev,
257 "Unable to access THERM_STATUS MSR, giving up\n");
258 goto exit_free;
259 }
260 platform_set_drvdata(pdev, data);
261
262 if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
263 goto exit_free;
264
265 data->class_dev = hwmon_device_register(&pdev->dev);
266 if (IS_ERR(data->class_dev)) {
267 err = PTR_ERR(data->class_dev);
268 dev_err(&pdev->dev, "Class registration failed (%d)\n",
269 err);
270 goto exit_class;
271 }
272
273 dev_warn(&pdev->dev, "This driver uses undocumented features of Core "
274 "CPU (Intel will not disclose the information to "
275 "individuals). Temperature might be wrong!\n");
276 return 0;
277
278 exit_class:
279 sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
280 exit_free:
281 kfree(data);
282 exit:
283 return err;
284 }
285
286 static int __devexit coretemp_remove(struct platform_device *pdev)
287 {
288 struct coretemp_data *data = platform_get_drvdata(pdev);
289
290 hwmon_device_unregister(data->class_dev);
291 sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
292 platform_set_drvdata(pdev, NULL);
293 kfree(data);
294 return 0;
295 }
296
297 static struct platform_driver coretemp_driver = {
298 .driver = {
299 .owner = THIS_MODULE,
300 .name = DRVNAME,
301 },
302 .probe = coretemp_probe,
303 .remove = __devexit_p(coretemp_remove),
304 };
305
306 struct pdev_entry {
307 struct list_head list;
308 struct platform_device *pdev;
309 unsigned int cpu;
310 };
311
312 static LIST_HEAD(pdev_list);
313 static DEFINE_MUTEX(pdev_list_mutex);
314
315 static int __cpuinit coretemp_devices_add(unsigned int cpu)
316 {
317 int err;
318 struct platform_device *pdev;
319 struct pdev_entry *pdev_entry;
320
321 pdev = platform_device_alloc(DRVNAME, cpu);
322 if (!pdev) {
323 err = -ENOMEM;
324 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
325 goto exit;
326
327 }
328
329 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
330
331 if (!pdev_entry) {
332 err = -ENOMEM;
333 goto exit_device_put;
334 }
335
336 err = platform_device_add(pdev);
337
338 if (err) {
339 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
340 err);
341 goto exit_device_free;
342 }
343
344 pdev_entry->pdev = pdev;
345 pdev_entry->cpu = cpu;
346 mutex_lock(&pdev_list_mutex);
347 list_add_tail(&pdev_entry->list, &pdev_list);
348 mutex_unlock(&pdev_list_mutex);
349
350 return 0;
351
352 exit_device_free:
353 kfree(pdev_entry);
354 exit_device_put:
355 platform_device_put(pdev);
356 exit:
357 return err;
358 }
359
360 #ifdef CONFIG_HOTPLUG_CPU
361 void coretemp_devices_remove(unsigned int cpu)
362 {
363 struct pdev_entry *p, *n;
364 mutex_lock(&pdev_list_mutex);
365 list_for_each_entry_safe(p, n, &pdev_list, list) {
366 if (p->cpu == cpu) {
367 platform_device_unregister(p->pdev);
368 list_del(&p->list);
369 kfree(p);
370 }
371 }
372 mutex_unlock(&pdev_list_mutex);
373 }
374
375 static int coretemp_cpu_callback(struct notifier_block *nfb,
376 unsigned long action, void *hcpu)
377 {
378 unsigned int cpu = (unsigned long) hcpu;
379
380 switch (action) {
381 case CPU_ONLINE:
382 coretemp_devices_add(cpu);
383 break;
384 case CPU_DEAD:
385 coretemp_devices_remove(cpu);
386 break;
387 }
388 return NOTIFY_OK;
389 }
390
391 static struct notifier_block __cpuinitdata coretemp_cpu_notifier = {
392 .notifier_call = coretemp_cpu_callback,
393 };
394 #endif /* !CONFIG_HOTPLUG_CPU */
395
396 static int __init coretemp_init(void)
397 {
398 int i, err = -ENODEV;
399 struct pdev_entry *p, *n;
400
401 /* quick check if we run Intel */
402 if (cpu_data[0].x86_vendor != X86_VENDOR_INTEL)
403 goto exit;
404
405 err = platform_driver_register(&coretemp_driver);
406 if (err)
407 goto exit;
408
409 for_each_online_cpu(i) {
410 struct cpuinfo_x86 *c = &(cpu_data)[i];
411
412 /* check if family 6, models e, f */
413 if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
414 !((c->x86_model == 0xe) || (c->x86_model == 0xf))) {
415
416 /* supported CPU not found, but report the unknown
417 family 6 CPU */
418 if ((c->x86 == 0x6) && (c->x86_model > 0xf))
419 printk(KERN_WARNING DRVNAME ": Unknown CPU, please"
420 " report to the lm-sensors@lm-sensors.org\n");
421 continue;
422 }
423
424 err = coretemp_devices_add(i);
425 if (err)
426 goto exit_driver;
427 }
428 if (list_empty(&pdev_list)) {
429 err = -ENODEV;
430 goto exit_driver_unreg;
431 }
432
433 #ifdef CONFIG_HOTPLUG_CPU
434 register_hotcpu_notifier(&coretemp_cpu_notifier);
435 #endif
436 return 0;
437
438 exit_driver:
439 mutex_lock(&pdev_list_mutex);
440 list_for_each_entry_safe(p, n, &pdev_list, list) {
441 platform_device_unregister(p->pdev);
442 list_del(&p->list);
443 kfree(p);
444 }
445 mutex_unlock(&pdev_list_mutex);
446 exit_driver_unreg:
447 platform_driver_unregister(&coretemp_driver);
448 exit:
449 return err;
450 }
451
452 static void __exit coretemp_exit(void)
453 {
454 struct pdev_entry *p, *n;
455 #ifdef CONFIG_HOTPLUG_CPU
456 unregister_hotcpu_notifier(&coretemp_cpu_notifier);
457 #endif
458 mutex_lock(&pdev_list_mutex);
459 list_for_each_entry_safe(p, n, &pdev_list, list) {
460 platform_device_unregister(p->pdev);
461 list_del(&p->list);
462 kfree(p);
463 }
464 mutex_unlock(&pdev_list_mutex);
465 platform_driver_unregister(&coretemp_driver);
466 }
467
468 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
469 MODULE_DESCRIPTION("Intel Core temperature monitor");
470 MODULE_LICENSE("GPL");
471
472 module_init(coretemp_init)
473 module_exit(coretemp_exit)
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.