diff options
author | Jean Delvare <khali@linux-fr.org> | 2011-05-25 14:43:33 -0400 |
---|---|---|
committer | Jean Delvare <khali@endymion.delvare> | 2011-05-25 14:43:33 -0400 |
commit | 949a9d70020defd7c241607ab3ed037ea88f551c (patch) | |
tree | b366a24968110e94f0ef753558be3b9d8cbbf7b3 /drivers/char | |
parent | 9c084dae5dc7ae0039e330230e70f2a5956e566a (diff) |
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>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/i8k.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index ee017166545e..6e40072fbf67 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c | |||
@@ -5,6 +5,9 @@ | |||
5 | * | 5 | * |
6 | * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> | 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 | * This program is free software; you can redistribute it and/or modify it | 11 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 12 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2, or (at your option) any | 13 | * Free Software Foundation; either version 2, or (at your option) any |
@@ -24,6 +27,8 @@ | |||
24 | #include <linux/dmi.h> | 27 | #include <linux/dmi.h> |
25 | #include <linux/capability.h> | 28 | #include <linux/capability.h> |
26 | #include <linux/mutex.h> | 29 | #include <linux/mutex.h> |
30 | #include <linux/hwmon.h> | ||
31 | #include <linux/hwmon-sysfs.h> | ||
27 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
28 | #include <asm/io.h> | 33 | #include <asm/io.h> |
29 | 34 | ||
@@ -58,6 +63,7 @@ | |||
58 | 63 | ||
59 | static DEFINE_MUTEX(i8k_mutex); | 64 | static DEFINE_MUTEX(i8k_mutex); |
60 | static char bios_version[4]; | 65 | static char bios_version[4]; |
66 | static struct device *i8k_hwmon_dev; | ||
61 | 67 | ||
62 | MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); | 68 | MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); |
63 | MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); | 69 | MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); |
@@ -455,6 +461,152 @@ static int i8k_open_fs(struct inode *inode, struct file *file) | |||
455 | return single_open(file, i8k_proc_show, NULL); | 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 | static struct dmi_system_id __initdata i8k_dmi_table[] = { | 610 | static struct dmi_system_id __initdata i8k_dmi_table[] = { |
459 | { | 611 | { |
460 | .ident = "Dell Inspiron", | 612 | .ident = "Dell Inspiron", |
@@ -580,6 +732,7 @@ static int __init i8k_probe(void) | |||
580 | static int __init i8k_init(void) | 732 | static int __init i8k_init(void) |
581 | { | 733 | { |
582 | struct proc_dir_entry *proc_i8k; | 734 | struct proc_dir_entry *proc_i8k; |
735 | int err; | ||
583 | 736 | ||
584 | /* Are we running on an supported laptop? */ | 737 | /* Are we running on an supported laptop? */ |
585 | if (i8k_probe()) | 738 | if (i8k_probe()) |
@@ -590,15 +743,24 @@ static int __init i8k_init(void) | |||
590 | if (!proc_i8k) | 743 | if (!proc_i8k) |
591 | return -ENOENT; | 744 | return -ENOENT; |
592 | 745 | ||
746 | err = i8k_init_hwmon(); | ||
747 | if (err) | ||
748 | goto exit_remove_proc; | ||
749 | |||
593 | printk(KERN_INFO | 750 | printk(KERN_INFO |
594 | "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", | 751 | "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", |
595 | I8K_VERSION); | 752 | I8K_VERSION); |
596 | 753 | ||
597 | return 0; | 754 | return 0; |
755 | |||
756 | exit_remove_proc: | ||
757 | remove_proc_entry("i8k", NULL); | ||
758 | return err; | ||
598 | } | 759 | } |
599 | 760 | ||
600 | static void __exit i8k_exit(void) | 761 | static void __exit i8k_exit(void) |
601 | { | 762 | { |
763 | i8k_exit_hwmon(); | ||
602 | remove_proc_entry("i8k", NULL); | 764 | remove_proc_entry("i8k", NULL); |
603 | } | 765 | } |
604 | 766 | ||