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 ee017166545..6e40072fbf6 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 | ||
