Commit 949a9d70020defd7c241607ab3ed037ea88f551c

Authored by Jean Delvare
Committed by Jean Delvare
1 parent 9c084dae5d

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

... ... @@ -917,6 +917,7 @@
917 917  
918 918 config I8K
919 919 tristate "Dell laptop support"
  920 + select HWMON
920 921 ---help---
921 922 This adds a driver to safely access the System Management Mode
922 923 of the CPU on the Dell Inspiron 8000. The System Management Mode
... ... @@ -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