Commit 949a9d70020defd7c241607ab3ed037ea88f551c
Committed by
Jean Delvare
1 parent
9c084dae5d
Exists in
master
and in
7 other branches
i8k: Integrate with the hwmon subsystem
Let i8k create an hwmon class device so that libsensors will expose the CPU temperature and fan speeds to monitoring applications. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Guenter Roeck <guenter.roeck@ericsson.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Massimo Dal Zotto <dz@debian.org>
Showing 2 changed files with 163 additions and 0 deletions Side-by-side Diff
arch/x86/Kconfig
drivers/char/i8k.c
... | ... | @@ -5,6 +5,9 @@ |
5 | 5 | * |
6 | 6 | * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> |
7 | 7 | * |
8 | + * Hwmon integration: | |
9 | + * Copyright (C) 2011 Jean Delvare <khali@linux-fr.org> | |
10 | + * | |
8 | 11 | * This program is free software; you can redistribute it and/or modify it |
9 | 12 | * under the terms of the GNU General Public License as published by the |
10 | 13 | * Free Software Foundation; either version 2, or (at your option) any |
... | ... | @@ -24,6 +27,8 @@ |
24 | 27 | #include <linux/dmi.h> |
25 | 28 | #include <linux/capability.h> |
26 | 29 | #include <linux/mutex.h> |
30 | +#include <linux/hwmon.h> | |
31 | +#include <linux/hwmon-sysfs.h> | |
27 | 32 | #include <asm/uaccess.h> |
28 | 33 | #include <asm/io.h> |
29 | 34 | |
... | ... | @@ -58,6 +63,7 @@ |
58 | 63 | |
59 | 64 | static DEFINE_MUTEX(i8k_mutex); |
60 | 65 | static char bios_version[4]; |
66 | +static struct device *i8k_hwmon_dev; | |
61 | 67 | |
62 | 68 | MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); |
63 | 69 | MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); |
... | ... | @@ -455,6 +461,152 @@ |
455 | 461 | return single_open(file, i8k_proc_show, NULL); |
456 | 462 | } |
457 | 463 | |
464 | + | |
465 | +/* | |
466 | + * Hwmon interface | |
467 | + */ | |
468 | + | |
469 | +static ssize_t i8k_hwmon_show_temp(struct device *dev, | |
470 | + struct device_attribute *devattr, | |
471 | + char *buf) | |
472 | +{ | |
473 | + int cpu_temp; | |
474 | + | |
475 | + cpu_temp = i8k_get_temp(0); | |
476 | + if (cpu_temp < 0) | |
477 | + return cpu_temp; | |
478 | + return sprintf(buf, "%d\n", cpu_temp * 1000); | |
479 | +} | |
480 | + | |
481 | +static ssize_t i8k_hwmon_show_fan(struct device *dev, | |
482 | + struct device_attribute *devattr, | |
483 | + char *buf) | |
484 | +{ | |
485 | + int index = to_sensor_dev_attr(devattr)->index; | |
486 | + int fan_speed; | |
487 | + | |
488 | + fan_speed = i8k_get_fan_speed(index); | |
489 | + if (fan_speed < 0) | |
490 | + return fan_speed; | |
491 | + return sprintf(buf, "%d\n", fan_speed); | |
492 | +} | |
493 | + | |
494 | +static ssize_t i8k_hwmon_show_label(struct device *dev, | |
495 | + struct device_attribute *devattr, | |
496 | + char *buf) | |
497 | +{ | |
498 | + static const char *labels[4] = { | |
499 | + "i8k", | |
500 | + "CPU", | |
501 | + "Left Fan", | |
502 | + "Right Fan", | |
503 | + }; | |
504 | + int index = to_sensor_dev_attr(devattr)->index; | |
505 | + | |
506 | + return sprintf(buf, "%s\n", labels[index]); | |
507 | +} | |
508 | + | |
509 | +static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL); | |
510 | +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, | |
511 | + I8K_FAN_LEFT); | |
512 | +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, | |
513 | + I8K_FAN_RIGHT); | |
514 | +static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0); | |
515 | +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); | |
516 | +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); | |
517 | +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3); | |
518 | + | |
519 | +static void i8k_hwmon_remove_files(struct device *dev) | |
520 | +{ | |
521 | + device_remove_file(dev, &dev_attr_temp1_input); | |
522 | + device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr); | |
523 | + device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr); | |
524 | + device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr); | |
525 | + device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr); | |
526 | + device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr); | |
527 | + device_remove_file(dev, &sensor_dev_attr_name.dev_attr); | |
528 | +} | |
529 | + | |
530 | +static int __init i8k_init_hwmon(void) | |
531 | +{ | |
532 | + int err; | |
533 | + | |
534 | + i8k_hwmon_dev = hwmon_device_register(NULL); | |
535 | + if (IS_ERR(i8k_hwmon_dev)) { | |
536 | + err = PTR_ERR(i8k_hwmon_dev); | |
537 | + i8k_hwmon_dev = NULL; | |
538 | + printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err); | |
539 | + return err; | |
540 | + } | |
541 | + | |
542 | + /* Required name attribute */ | |
543 | + err = device_create_file(i8k_hwmon_dev, | |
544 | + &sensor_dev_attr_name.dev_attr); | |
545 | + if (err) | |
546 | + goto exit_unregister; | |
547 | + | |
548 | + /* CPU temperature attributes, if temperature reading is OK */ | |
549 | + err = i8k_get_temp(0); | |
550 | + if (err < 0) { | |
551 | + dev_dbg(i8k_hwmon_dev, | |
552 | + "Not creating temperature attributes (%d)\n", err); | |
553 | + } else { | |
554 | + err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input); | |
555 | + if (err) | |
556 | + goto exit_remove_files; | |
557 | + err = device_create_file(i8k_hwmon_dev, | |
558 | + &sensor_dev_attr_temp1_label.dev_attr); | |
559 | + if (err) | |
560 | + goto exit_remove_files; | |
561 | + } | |
562 | + | |
563 | + /* Left fan attributes, if left fan is present */ | |
564 | + err = i8k_get_fan_status(I8K_FAN_LEFT); | |
565 | + if (err < 0) { | |
566 | + dev_dbg(i8k_hwmon_dev, | |
567 | + "Not creating %s fan attributes (%d)\n", "left", err); | |
568 | + } else { | |
569 | + err = device_create_file(i8k_hwmon_dev, | |
570 | + &sensor_dev_attr_fan1_input.dev_attr); | |
571 | + if (err) | |
572 | + goto exit_remove_files; | |
573 | + err = device_create_file(i8k_hwmon_dev, | |
574 | + &sensor_dev_attr_fan1_label.dev_attr); | |
575 | + if (err) | |
576 | + goto exit_remove_files; | |
577 | + } | |
578 | + | |
579 | + /* Right fan attributes, if right fan is present */ | |
580 | + err = i8k_get_fan_status(I8K_FAN_RIGHT); | |
581 | + if (err < 0) { | |
582 | + dev_dbg(i8k_hwmon_dev, | |
583 | + "Not creating %s fan attributes (%d)\n", "right", err); | |
584 | + } else { | |
585 | + err = device_create_file(i8k_hwmon_dev, | |
586 | + &sensor_dev_attr_fan2_input.dev_attr); | |
587 | + if (err) | |
588 | + goto exit_remove_files; | |
589 | + err = device_create_file(i8k_hwmon_dev, | |
590 | + &sensor_dev_attr_fan2_label.dev_attr); | |
591 | + if (err) | |
592 | + goto exit_remove_files; | |
593 | + } | |
594 | + | |
595 | + return 0; | |
596 | + | |
597 | + exit_remove_files: | |
598 | + i8k_hwmon_remove_files(i8k_hwmon_dev); | |
599 | + exit_unregister: | |
600 | + hwmon_device_unregister(i8k_hwmon_dev); | |
601 | + return err; | |
602 | +} | |
603 | + | |
604 | +static void __exit i8k_exit_hwmon(void) | |
605 | +{ | |
606 | + i8k_hwmon_remove_files(i8k_hwmon_dev); | |
607 | + hwmon_device_unregister(i8k_hwmon_dev); | |
608 | +} | |
609 | + | |
458 | 610 | static struct dmi_system_id __initdata i8k_dmi_table[] = { |
459 | 611 | { |
460 | 612 | .ident = "Dell Inspiron", |
... | ... | @@ -580,6 +732,7 @@ |
580 | 732 | static int __init i8k_init(void) |
581 | 733 | { |
582 | 734 | struct proc_dir_entry *proc_i8k; |
735 | + int err; | |
583 | 736 | |
584 | 737 | /* Are we running on an supported laptop? */ |
585 | 738 | if (i8k_probe()) |
586 | 739 | |
587 | 740 | |
... | ... | @@ -590,15 +743,24 @@ |
590 | 743 | if (!proc_i8k) |
591 | 744 | return -ENOENT; |
592 | 745 | |
746 | + err = i8k_init_hwmon(); | |
747 | + if (err) | |
748 | + goto exit_remove_proc; | |
749 | + | |
593 | 750 | printk(KERN_INFO |
594 | 751 | "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", |
595 | 752 | I8K_VERSION); |
596 | 753 | |
597 | 754 | return 0; |
755 | + | |
756 | + exit_remove_proc: | |
757 | + remove_proc_entry("i8k", NULL); | |
758 | + return err; | |
598 | 759 | } |
599 | 760 | |
600 | 761 | static void __exit i8k_exit(void) |
601 | 762 | { |
763 | + i8k_exit_hwmon(); | |
602 | 764 | remove_proc_entry("i8k", NULL); |
603 | 765 | } |
604 | 766 |