aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRudolf Marek <r.marek@assembler.cz>2007-05-08 11:22:02 -0400
committerJean Delvare <khali@hyperion.delvare>2007-05-08 11:22:02 -0400
commitbebe467823c0d8eeb7f49115c255d8a235a20ddb (patch)
treedf65dbf9fa88a0e6852c5cdc96c22cf26a97aff0 /drivers
parent78a62d2c9817dc1adfc563f5a7654df2c89be416 (diff)
hwmon: New coretemp driver
Add the support for the digital temperature sensor found in recent Intel Core CPUs. Signed-off-by: Rudolf Marek <r.marek@assembler.cz> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig8
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/coretemp.c406
3 files changed, 415 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 659a2abb3e90..e488d691da4a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -229,6 +229,14 @@ config SENSORS_GL520SM
229 This driver can also be built as a module. If so, the module 229 This driver can also be built as a module. If so, the module
230 will be called gl520sm. 230 will be called gl520sm.
231 231
232config SENSORS_CORETEMP
233 tristate "Intel Core (2) Duo/Solo temperature sensor"
234 depends on HWMON && X86 && EXPERIMENTAL
235 help
236 If you say yes here you get support for the temperature
237 sensor inside your CPU. Supported all are all known variants
238 of Intel Core family.
239
232config SENSORS_IT87 240config SENSORS_IT87
233 tristate "ITE IT87xx and compatibles" 241 tristate "ITE IT87xx and compatibles"
234 depends on HWMON && I2C 242 depends on HWMON && I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index bbbe86bf5da1..33780678bd4b 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
22obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o 22obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
23obj-$(CONFIG_SENSORS_AMS) += ams/ 23obj-$(CONFIG_SENSORS_AMS) += ams/
24obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o 24obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
25obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
25obj-$(CONFIG_SENSORS_DS1621) += ds1621.o 26obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
26obj-$(CONFIG_SENSORS_F71805F) += f71805f.o 27obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
27obj-$(CONFIG_SENSORS_FSCHER) += fscher.o 28obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
new file mode 100644
index 000000000000..03b1f650d1c4
--- /dev/null
+++ b/drivers/hwmon/coretemp.c
@@ -0,0 +1,406 @@
1/*
2 * coretemp.c - Linux kernel module for hardware monitoring
3 *
4 * Copyright (C) 2007 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; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23#include <linux/module.h>
24#include <linux/delay.h>
25#include <linux/init.h>
26#include <linux/slab.h>
27#include <linux/jiffies.h>
28#include <linux/hwmon.h>
29#include <linux/sysfs.h>
30#include <linux/hwmon-sysfs.h>
31#include <linux/err.h>
32#include <linux/mutex.h>
33#include <linux/list.h>
34#include <linux/platform_device.h>
35#include <linux/cpu.h>
36#include <asm/msr.h>
37#include <asm/processor.h>
38
39#define DRVNAME "coretemp"
40
41typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW;
42
43/*
44 * Functions declaration
45 */
46
47static struct coretemp_data *coretemp_update_device(struct device *dev);
48
49struct coretemp_data {
50 struct class_device *class_dev;
51 struct mutex update_lock;
52 const char *name;
53 u32 id;
54 char valid; /* zero until following fields are valid */
55 unsigned long last_updated; /* in jiffies */
56 int temp;
57 int tjmax;
58 u8 alarm;
59};
60
61static struct coretemp_data *coretemp_update_device(struct device *dev);
62
63/*
64 * Sysfs stuff
65 */
66
67static ssize_t show_name(struct device *dev, struct device_attribute
68 *devattr, char *buf)
69{
70 int ret;
71 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
72 struct coretemp_data *data = dev_get_drvdata(dev);
73
74 if (attr->index == SHOW_NAME)
75 ret = sprintf(buf, "%s\n", data->name);
76 else /* show label */
77 ret = sprintf(buf, "Core %d\n", data->id);
78 return ret;
79}
80
81static ssize_t show_alarm(struct device *dev, struct device_attribute
82 *devattr, char *buf)
83{
84 struct coretemp_data *data = coretemp_update_device(dev);
85 /* read the Out-of-spec log, never clear */
86 return sprintf(buf, "%d\n", data->alarm);
87}
88
89static ssize_t show_temp(struct device *dev,
90 struct device_attribute *devattr, char *buf)
91{
92 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
93 struct coretemp_data *data = coretemp_update_device(dev);
94 int err;
95
96 if (attr->index == SHOW_TEMP)
97 err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
98 else
99 err = sprintf(buf, "%d\n", data->tjmax);
100
101 return err;
102}
103
104static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
105 SHOW_TEMP);
106static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
107 SHOW_TJMAX);
108static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
109static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
110static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
111
112static struct attribute *coretemp_attributes[] = {
113 &sensor_dev_attr_name.dev_attr.attr,
114 &sensor_dev_attr_temp1_label.dev_attr.attr,
115 &dev_attr_temp1_crit_alarm.attr,
116 &sensor_dev_attr_temp1_input.dev_attr.attr,
117 &sensor_dev_attr_temp1_crit.dev_attr.attr,
118 NULL
119};
120
121static const struct attribute_group coretemp_group = {
122 .attrs = coretemp_attributes,
123};
124
125static struct coretemp_data *coretemp_update_device(struct device *dev)
126{
127 struct coretemp_data *data = dev_get_drvdata(dev);
128
129 mutex_lock(&data->update_lock);
130
131 if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
132 u32 eax, edx;
133
134 data->valid = 0;
135 rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
136 data->alarm = (eax >> 5) & 1;
137 /* update only if data has been valid */
138 if (eax & 0x80000000) {
139 data->temp = data->tjmax - (((eax >> 16)
140 & 0x7f) * 1000);
141 data->valid = 1;
142 } else {
143 dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
144 }
145 data->last_updated = jiffies;
146 }
147
148 mutex_unlock(&data->update_lock);
149 return data;
150}
151
152static int __devinit coretemp_probe(struct platform_device *pdev)
153{
154 struct coretemp_data *data;
155 struct cpuinfo_x86 *c = &(cpu_data)[pdev->id];
156 int err;
157 u32 eax, edx;
158
159 if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) {
160 err = -ENOMEM;
161 dev_err(&pdev->dev, "Out of memory\n");
162 goto exit;
163 }
164
165 data->id = pdev->id;
166 data->name = "coretemp";
167 mutex_init(&data->update_lock);
168 /* Tjmax default is 100 degrees C */
169 data->tjmax = 100000;
170
171 /* test if we can access the THERM_STATUS MSR */
172 err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
173 if (err) {
174 dev_err(&pdev->dev,
175 "Unable to access THERM_STATUS MSR, giving up\n");
176 goto exit_free;
177 }
178
179 /* Some processors have Tjmax 85 following magic should detect it
180 Intel won't disclose the information without signed NDA, but
181 individuals cannot sign it. Catch(ed) 22.
182 */
183
184 if (((c->x86_model == 0xf) && (c->x86_mask > 3)) ||
185 (c->x86_model == 0xe)) {
186 err = rdmsr_safe_on_cpu(data->id, 0xee, &eax, &edx);
187 if (err) {
188 dev_warn(&pdev->dev,
189 "Unable to access MSR 0xEE, Tjmax left at %d "
190 "degrees C\n", data->tjmax/1000);
191 } else if (eax & 0x40000000) {
192 data->tjmax = 85000;
193 }
194 }
195
196 platform_set_drvdata(pdev, data);
197
198 if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
199 goto exit_free;
200
201 data->class_dev = hwmon_device_register(&pdev->dev);
202 if (IS_ERR(data->class_dev)) {
203 err = PTR_ERR(data->class_dev);
204 dev_err(&pdev->dev, "Class registration failed (%d)\n",
205 err);
206 goto exit_class;
207 }
208
209 return 0;
210
211exit_class:
212 sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
213exit_free:
214 kfree(data);
215exit:
216 return err;
217}
218
219static int __devexit coretemp_remove(struct platform_device *pdev)
220{
221 struct coretemp_data *data = platform_get_drvdata(pdev);
222
223 hwmon_device_unregister(data->class_dev);
224 sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
225 platform_set_drvdata(pdev, NULL);
226 kfree(data);
227 return 0;
228}
229
230static struct platform_driver coretemp_driver = {
231 .driver = {
232 .owner = THIS_MODULE,
233 .name = DRVNAME,
234 },
235 .probe = coretemp_probe,
236 .remove = __devexit_p(coretemp_remove),
237};
238
239struct pdev_entry {
240 struct list_head list;
241 struct platform_device *pdev;
242 unsigned int cpu;
243};
244
245static LIST_HEAD(pdev_list);
246static DEFINE_MUTEX(pdev_list_mutex);
247
248static int __cpuinit coretemp_device_add(unsigned int cpu)
249{
250 int err;
251 struct platform_device *pdev;
252 struct pdev_entry *pdev_entry;
253
254 pdev = platform_device_alloc(DRVNAME, cpu);
255 if (!pdev) {
256 err = -ENOMEM;
257 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
258 goto exit;
259 }
260
261 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
262 if (!pdev_entry) {
263 err = -ENOMEM;
264 goto exit_device_put;
265 }
266
267 err = platform_device_add(pdev);
268 if (err) {
269 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
270 err);
271 goto exit_device_free;
272 }
273
274 pdev_entry->pdev = pdev;
275 pdev_entry->cpu = cpu;
276 mutex_lock(&pdev_list_mutex);
277 list_add_tail(&pdev_entry->list, &pdev_list);
278 mutex_unlock(&pdev_list_mutex);
279
280 return 0;
281
282exit_device_free:
283 kfree(pdev_entry);
284exit_device_put:
285 platform_device_put(pdev);
286exit:
287 return err;
288}
289
290#ifdef CONFIG_HOTPLUG_CPU
291void coretemp_device_remove(unsigned int cpu)
292{
293 struct pdev_entry *p, *n;
294 mutex_lock(&pdev_list_mutex);
295 list_for_each_entry_safe(p, n, &pdev_list, list) {
296 if (p->cpu == cpu) {
297 platform_device_unregister(p->pdev);
298 list_del(&p->list);
299 kfree(p);
300 }
301 }
302 mutex_unlock(&pdev_list_mutex);
303}
304
305static int coretemp_cpu_callback(struct notifier_block *nfb,
306 unsigned long action, void *hcpu)
307{
308 unsigned int cpu = (unsigned long) hcpu;
309
310 switch (action) {
311 case CPU_ONLINE:
312 coretemp_device_add(cpu);
313 break;
314 case CPU_DEAD:
315 coretemp_device_remove(cpu);
316 break;
317 }
318 return NOTIFY_OK;
319}
320
321static struct notifier_block __cpuinitdata coretemp_cpu_notifier = {
322 .notifier_call = coretemp_cpu_callback,
323};
324#endif /* !CONFIG_HOTPLUG_CPU */
325
326static int __init coretemp_init(void)
327{
328 int i, err = -ENODEV;
329 struct pdev_entry *p, *n;
330
331 printk(KERN_NOTICE DRVNAME ": This driver uses undocumented features "
332 "of Core CPU. Temperature might be wrong!\n");
333
334 /* quick check if we run Intel */
335 if (cpu_data[0].x86_vendor != X86_VENDOR_INTEL)
336 goto exit;
337
338 err = platform_driver_register(&coretemp_driver);
339 if (err)
340 goto exit;
341
342 for_each_online_cpu(i) {
343 struct cpuinfo_x86 *c = &(cpu_data)[i];
344
345 /* check if family 6, models e, f */
346 if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
347 !((c->x86_model == 0xe) || (c->x86_model == 0xf))) {
348
349 /* supported CPU not found, but report the unknown
350 family 6 CPU */
351 if ((c->x86 == 0x6) && (c->x86_model > 0xf))
352 printk(KERN_WARNING DRVNAME ": Unknown CPU "
353 "model %x\n", c->x86_model);
354 continue;
355 }
356
357 err = coretemp_device_add(i);
358 if (err)
359 goto exit_devices_unreg;
360 }
361 if (list_empty(&pdev_list)) {
362 err = -ENODEV;
363 goto exit_driver_unreg;
364 }
365
366#ifdef CONFIG_HOTPLUG_CPU
367 register_hotcpu_notifier(&coretemp_cpu_notifier);
368#endif
369 return 0;
370
371exit_devices_unreg:
372 mutex_lock(&pdev_list_mutex);
373 list_for_each_entry_safe(p, n, &pdev_list, list) {
374 platform_device_unregister(p->pdev);
375 list_del(&p->list);
376 kfree(p);
377 }
378 mutex_unlock(&pdev_list_mutex);
379exit_driver_unreg:
380 platform_driver_unregister(&coretemp_driver);
381exit:
382 return err;
383}
384
385static void __exit coretemp_exit(void)
386{
387 struct pdev_entry *p, *n;
388#ifdef CONFIG_HOTPLUG_CPU
389 unregister_hotcpu_notifier(&coretemp_cpu_notifier);
390#endif
391 mutex_lock(&pdev_list_mutex);
392 list_for_each_entry_safe(p, n, &pdev_list, list) {
393 platform_device_unregister(p->pdev);
394 list_del(&p->list);
395 kfree(p);
396 }
397 mutex_unlock(&pdev_list_mutex);
398 platform_driver_unregister(&coretemp_driver);
399}
400
401MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
402MODULE_DESCRIPTION("Intel Core temperature monitor");
403MODULE_LICENSE("GPL");
404
405module_init(coretemp_init)
406module_exit(coretemp_exit)