aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorFenghua Yu <fenghua.yu@intel.com>2010-07-29 20:13:43 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2010-08-03 18:58:07 -0400
commitcb84b19474384c572ba3aa2345815e555112ebf5 (patch)
treeee703b0fc3ed0ff8908b5e9aab63853195c8b845 /drivers/hwmon
parent9792db6174d9927700ed288e6d74b9391bf785d1 (diff)
x86, hwmon: Package Level Thermal/Power: pkgtemp hwmon driver
This patch adds a hwmon driver for package level thermal control. The driver dumps package level thermal information through sysfs interface so that upper level application (e.g. lm_sensor) can retrive the information. Instead of having the package level hwmon code in coretemp, I write a seperate driver pkgtemp because: First, package level thermal sensors include not only sensors for each core, but also sensors for uncore, memory controller or other components in the package. Logically it will be clear to have a seperate hwmon driver for package level hwmon to monitor wider range of sensors in a package. Merging package thermal driver into core thermal driver doesn't make sense and may mislead. Secondly, merging the two drivers together may cause coding mess. It's easier to include various package level sensors info if more sensor information is implemented. Coretemp code needs to consider a lot of legacy machine cases. Pkgtemp code only considers platform starting from Sandy Bridge. On a 1Sx4Cx2T Sandy Bridge platform, lm-sensors dumps the pkgtemp and coretemp: pkgtemp-isa-0000 Adapter: ISA adapter physical id 0: +33.0°C (high = +79.0°C, crit = +99.0°C) coretemp-isa-0000 Adapter: ISA adapter Core 0: +32.0°C (high = +79.0°C, crit = +99.0°C) coretemp-isa-0001 Adapter: ISA adapter Core 1: +32.0°C (high = +79.0°C, crit = +99.0°C) coretemp-isa-0002 Adapter: ISA adapter Core 2: +32.0°C (high = +79.0°C, crit = +99.0°C) coretemp-isa-0003 Adapter: ISA adapter Core 3: +32.0°C (high = +79.0°C, crit = +99.0°C) [ hpa: folded v3 patch removing improper global variable "SHOW" ] Signed-off-by: Fenghua Yu <fenghua.yu@intel.com> LKML-Reference: <1280448826-12004-3-git-send-email-fenghua.yu@intel.com> Reviewed-by: Len Brown <len.brown@intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig7
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/pkgtemp.c456
3 files changed, 464 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e19cf8eb6ccf..3a858e854356 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -407,6 +407,13 @@ config SENSORS_CORETEMP
407 sensor inside your CPU. Most of the family 6 CPUs 407 sensor inside your CPU. Most of the family 6 CPUs
408 are supported. Check documentation/driver for details. 408 are supported. Check documentation/driver for details.
409 409
410config SENSORS_PKGTEMP
411 tristate "Intel processor package temperature sensor"
412 depends on X86 && PCI && EXPERIMENTAL
413 help
414 If you say yes here you get support for the package level temperature
415 sensor inside your CPU. Check documentation/driver for details.
416
410config SENSORS_IBMAEM 417config SENSORS_IBMAEM
411 tristate "IBM Active Energy Manager temperature/power sensors and control" 418 tristate "IBM Active Energy Manager temperature/power sensors and control"
412 select IPMI_SI 419 select IPMI_SI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2138ceb1a713..879814e60878 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SENSORS_AMS) += ams/
39obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o 39obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
40obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o 40obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
41obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o 41obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
42obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
42obj-$(CONFIG_SENSORS_DME1737) += dme1737.o 43obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
43obj-$(CONFIG_SENSORS_DS1621) += ds1621.o 44obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
44obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o 45obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c
new file mode 100644
index 000000000000..74157fcda6ed
--- /dev/null
+++ b/drivers/hwmon/pkgtemp.c
@@ -0,0 +1,456 @@
1/*
2 * pkgtemp.c - Linux kernel module for processor package hardware monitoring
3 *
4 * Copyright (C) 2010 Fenghua Yu <fenghua.yu@intel.com>
5 *
6 * Inspired from many hwmon drivers especially coretemp.
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 <linux/pci.h>
37#include <asm/msr.h>
38#include <asm/processor.h>
39
40#define DRVNAME "pkgtemp"
41
42enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, SHOW_NAME };
43
44/*
45 * Functions declaration
46 */
47
48static struct pkgtemp_data *pkgtemp_update_device(struct device *dev);
49
50struct pkgtemp_data {
51 struct device *hwmon_dev;
52 struct mutex update_lock;
53 const char *name;
54 u32 id;
55 u16 phys_proc_id;
56 char valid; /* zero until following fields are valid */
57 unsigned long last_updated; /* in jiffies */
58 int temp;
59 int tjmax;
60 int ttarget;
61 u8 alarm;
62};
63
64/*
65 * Sysfs stuff
66 */
67
68static ssize_t show_name(struct device *dev, struct device_attribute
69 *devattr, char *buf)
70{
71 int ret;
72 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
73 struct pkgtemp_data *data = dev_get_drvdata(dev);
74
75 if (attr->index == SHOW_NAME)
76 ret = sprintf(buf, "%s\n", data->name);
77 else /* show label */
78 ret = sprintf(buf, "physical id %d\n",
79 data->phys_proc_id);
80 return ret;
81}
82
83static ssize_t show_alarm(struct device *dev, struct device_attribute
84 *devattr, char *buf)
85{
86 struct pkgtemp_data *data = pkgtemp_update_device(dev);
87 /* read the Out-of-spec log, never clear */
88 return sprintf(buf, "%d\n", data->alarm);
89}
90
91static ssize_t show_temp(struct device *dev,
92 struct device_attribute *devattr, char *buf)
93{
94 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
95 struct pkgtemp_data *data = pkgtemp_update_device(dev);
96 int err = 0;
97
98 if (attr->index == SHOW_TEMP)
99 err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
100 else if (attr->index == SHOW_TJMAX)
101 err = sprintf(buf, "%d\n", data->tjmax);
102 else
103 err = sprintf(buf, "%d\n", data->ttarget);
104 return err;
105}
106
107static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP);
108static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, SHOW_TJMAX);
109static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, SHOW_TTARGET);
110static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
111static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
112static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
113
114static struct attribute *pkgtemp_attributes[] = {
115 &sensor_dev_attr_name.dev_attr.attr,
116 &sensor_dev_attr_temp1_label.dev_attr.attr,
117 &dev_attr_temp1_crit_alarm.attr,
118 &sensor_dev_attr_temp1_input.dev_attr.attr,
119 &sensor_dev_attr_temp1_crit.dev_attr.attr,
120 NULL
121};
122
123static const struct attribute_group pkgtemp_group = {
124 .attrs = pkgtemp_attributes,
125};
126
127static struct pkgtemp_data *pkgtemp_update_device(struct device *dev)
128{
129 struct pkgtemp_data *data = dev_get_drvdata(dev);
130 unsigned int cpu;
131 int err;
132
133 mutex_lock(&data->update_lock);
134
135 if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
136 u32 eax, edx;
137
138 data->valid = 0;
139 cpu = data->id;
140 err = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_STATUS,
141 &eax, &edx);
142 if (!err) {
143 data->alarm = (eax >> 5) & 1;
144 data->temp = data->tjmax - (((eax >> 16)
145 & 0x7f) * 1000);
146 data->valid = 1;
147 } else
148 dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
149
150 data->last_updated = jiffies;
151 }
152
153 mutex_unlock(&data->update_lock);
154 return data;
155}
156
157static int get_tjmax(int cpu, struct device *dev)
158{
159 int default_tjmax = 100000;
160 int err;
161 u32 eax, edx;
162 u32 val;
163
164 /* IA32_TEMPERATURE_TARGET contains the TjMax value */
165 err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
166 if (!err) {
167 val = (eax >> 16) & 0xff;
168 if ((val > 80) && (val < 120)) {
169 dev_info(dev, "TjMax is %d C.\n", val);
170 return val * 1000;
171 }
172 }
173 dev_warn(dev, "Unable to read TjMax from CPU.\n");
174 return default_tjmax;
175}
176
177static int __devinit pkgtemp_probe(struct platform_device *pdev)
178{
179 struct pkgtemp_data *data;
180 int err;
181 u32 eax, edx;
182#ifdef CONFIG_SMP
183 struct cpuinfo_x86 *c = &cpu_data(pdev->id);
184#endif
185
186 data = kzalloc(sizeof(struct pkgtemp_data), GFP_KERNEL);
187 if (!data) {
188 err = -ENOMEM;
189 dev_err(&pdev->dev, "Out of memory\n");
190 goto exit;
191 }
192
193 data->id = pdev->id;
194#ifdef CONFIG_SMP
195 data->phys_proc_id = c->phys_proc_id;
196#endif
197 data->name = "pkgtemp";
198 mutex_init(&data->update_lock);
199
200 /* test if we can access the THERM_STATUS MSR */
201 err = rdmsr_safe_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_STATUS,
202 &eax, &edx);
203 if (err) {
204 dev_err(&pdev->dev,
205 "Unable to access THERM_STATUS MSR, giving up\n");
206 goto exit_free;
207 }
208
209 data->tjmax = get_tjmax(data->id, &pdev->dev);
210 platform_set_drvdata(pdev, data);
211
212 err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET,
213 &eax, &edx);
214 if (err) {
215 dev_warn(&pdev->dev, "Unable to read"
216 " IA32_TEMPERATURE_TARGET MSR\n");
217 } else {
218 data->ttarget = data->tjmax - (((eax >> 8) & 0xff) * 1000);
219 err = device_create_file(&pdev->dev,
220 &sensor_dev_attr_temp1_max.dev_attr);
221 if (err)
222 goto exit_free;
223 }
224
225 err = sysfs_create_group(&pdev->dev.kobj, &pkgtemp_group);
226 if (err)
227 goto exit_free;
228
229 data->hwmon_dev = hwmon_device_register(&pdev->dev);
230 if (IS_ERR(data->hwmon_dev)) {
231 err = PTR_ERR(data->hwmon_dev);
232 dev_err(&pdev->dev, "Class registration failed (%d)\n",
233 err);
234 goto exit_class;
235 }
236
237 return 0;
238
239exit_class:
240 sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
241exit_free:
242 kfree(data);
243exit:
244 return err;
245}
246
247static int __devexit pkgtemp_remove(struct platform_device *pdev)
248{
249 struct pkgtemp_data *data = platform_get_drvdata(pdev);
250
251 hwmon_device_unregister(data->hwmon_dev);
252 sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
253 platform_set_drvdata(pdev, NULL);
254 kfree(data);
255 return 0;
256}
257
258static struct platform_driver pkgtemp_driver = {
259 .driver = {
260 .owner = THIS_MODULE,
261 .name = DRVNAME,
262 },
263 .probe = pkgtemp_probe,
264 .remove = __devexit_p(pkgtemp_remove),
265};
266
267struct pdev_entry {
268 struct list_head list;
269 struct platform_device *pdev;
270 unsigned int cpu;
271#ifdef CONFIG_SMP
272 u16 phys_proc_id;
273#endif
274};
275
276static LIST_HEAD(pdev_list);
277static DEFINE_MUTEX(pdev_list_mutex);
278
279static int __cpuinit pkgtemp_device_add(unsigned int cpu)
280{
281 int err;
282 struct platform_device *pdev;
283 struct pdev_entry *pdev_entry;
284#ifdef CONFIG_SMP
285 struct cpuinfo_x86 *c = &cpu_data(cpu);
286#endif
287
288 mutex_lock(&pdev_list_mutex);
289
290#ifdef CONFIG_SMP
291 /* Only keep the first entry in each package */
292 list_for_each_entry(pdev_entry, &pdev_list, list) {
293 if (c->phys_proc_id == pdev_entry->phys_proc_id) {
294 err = 0; /* Not an error */
295 goto exit;
296 }
297 }
298#endif
299
300 pdev = platform_device_alloc(DRVNAME, cpu);
301 if (!pdev) {
302 err = -ENOMEM;
303 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
304 goto exit;
305 }
306
307 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
308 if (!pdev_entry) {
309 err = -ENOMEM;
310 goto exit_device_put;
311 }
312
313 err = platform_device_add(pdev);
314 if (err) {
315 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
316 err);
317 goto exit_device_free;
318 }
319
320#ifdef CONFIG_SMP
321 pdev_entry->phys_proc_id = c->phys_proc_id;
322#endif
323 pdev_entry->pdev = pdev;
324 pdev_entry->cpu = cpu;
325 list_add_tail(&pdev_entry->list, &pdev_list);
326 mutex_unlock(&pdev_list_mutex);
327
328 return 0;
329
330exit_device_free:
331 kfree(pdev_entry);
332exit_device_put:
333 platform_device_put(pdev);
334exit:
335 mutex_unlock(&pdev_list_mutex);
336 return err;
337}
338
339#ifdef CONFIG_HOTPLUG_CPU
340static void pkgtemp_device_remove(unsigned int cpu)
341{
342 struct pdev_entry *p, *n;
343 unsigned int i;
344 int err;
345
346 mutex_lock(&pdev_list_mutex);
347 list_for_each_entry_safe(p, n, &pdev_list, list) {
348 if (p->cpu != cpu)
349 continue;
350
351 platform_device_unregister(p->pdev);
352 list_del(&p->list);
353 kfree(p);
354 for_each_cpu(i, cpu_core_mask(cpu)) {
355 if (i != cpu) {
356 err = pkgtemp_device_add(i);
357 if (!err)
358 break;
359 }
360 }
361 break;
362 }
363 mutex_unlock(&pdev_list_mutex);
364}
365
366static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb,
367 unsigned long action, void *hcpu)
368{
369 unsigned int cpu = (unsigned long) hcpu;
370
371 switch (action) {
372 case CPU_ONLINE:
373 case CPU_DOWN_FAILED:
374 pkgtemp_device_add(cpu);
375 break;
376 case CPU_DOWN_PREPARE:
377 pkgtemp_device_remove(cpu);
378 break;
379 }
380 return NOTIFY_OK;
381}
382
383static struct notifier_block pkgtemp_cpu_notifier __refdata = {
384 .notifier_call = pkgtemp_cpu_callback,
385};
386#endif /* !CONFIG_HOTPLUG_CPU */
387
388static int __init pkgtemp_init(void)
389{
390 int i, err = -ENODEV;
391 struct pdev_entry *p, *n;
392
393 /* quick check if we run Intel */
394 if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
395 goto exit;
396
397 err = platform_driver_register(&pkgtemp_driver);
398 if (err)
399 goto exit;
400
401 for_each_online_cpu(i) {
402 struct cpuinfo_x86 *c = &cpu_data(i);
403
404 if (!cpu_has(c, X86_FEATURE_PTS))
405 continue;
406
407 err = pkgtemp_device_add(i);
408 if (err)
409 goto exit_devices_unreg;
410 }
411 if (list_empty(&pdev_list)) {
412 err = -ENODEV;
413 goto exit_driver_unreg;
414 }
415
416#ifdef CONFIG_HOTPLUG_CPU
417 register_hotcpu_notifier(&pkgtemp_cpu_notifier);
418#endif
419 return 0;
420
421exit_devices_unreg:
422 mutex_lock(&pdev_list_mutex);
423 list_for_each_entry_safe(p, n, &pdev_list, list) {
424 platform_device_unregister(p->pdev);
425 list_del(&p->list);
426 kfree(p);
427 }
428 mutex_unlock(&pdev_list_mutex);
429exit_driver_unreg:
430 platform_driver_unregister(&pkgtemp_driver);
431exit:
432 return err;
433}
434
435static void __exit pkgtemp_exit(void)
436{
437 struct pdev_entry *p, *n;
438#ifdef CONFIG_HOTPLUG_CPU
439 unregister_hotcpu_notifier(&pkgtemp_cpu_notifier);
440#endif
441 mutex_lock(&pdev_list_mutex);
442 list_for_each_entry_safe(p, n, &pdev_list, list) {
443 platform_device_unregister(p->pdev);
444 list_del(&p->list);
445 kfree(p);
446 }
447 mutex_unlock(&pdev_list_mutex);
448 platform_driver_unregister(&pkgtemp_driver);
449}
450
451MODULE_AUTHOR("Fenghua Yu <fenghua.yu@intel.com>");
452MODULE_DESCRIPTION("Intel processor package temperature monitor");
453MODULE_LICENSE("GPL");
454
455module_init(pkgtemp_init)
456module_exit(pkgtemp_exit)