Browse Source

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>
Jean Delvare 14 years ago
parent
commit
949a9d7002
2 changed files with 163 additions and 0 deletions
  1. 1 0
      arch/x86/Kconfig
  2. 162 0
      drivers/char/i8k.c

+ 1 - 0
arch/x86/Kconfig

@@ -917,6 +917,7 @@ config TOSHIBA
 
 
 config I8K
 config I8K
 	tristate "Dell laptop support"
 	tristate "Dell laptop support"
+	select HWMON
 	---help---
 	---help---
 	  This adds a driver to safely access the System Management Mode
 	  This adds a driver to safely access the System Management Mode
 	  of the CPU on the Dell Inspiron 8000. The System Management Mode
 	  of the CPU on the Dell Inspiron 8000. The System Management Mode

+ 162 - 0
drivers/char/i8k.c

@@ -5,6 +5,9 @@
  *
  *
  * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
  * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
  *
  *
+ * Hwmon integration:
+ * Copyright (C) 2011  Jean Delvare <khali@linux-fr.org>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2, or (at your option) any
  * Free Software Foundation; either version 2, or (at your option) any
@@ -24,6 +27,8 @@
 #include <linux/dmi.h>
 #include <linux/dmi.h>
 #include <linux/capability.h>
 #include <linux/capability.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/io.h>
 
 
@@ -58,6 +63,7 @@
 
 
 static DEFINE_MUTEX(i8k_mutex);
 static DEFINE_MUTEX(i8k_mutex);
 static char bios_version[4];
 static char bios_version[4];
+static struct device *i8k_hwmon_dev;
 
 
 MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
 MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
 MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
 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)
 	return single_open(file, i8k_proc_show, NULL);
 	return single_open(file, i8k_proc_show, NULL);
 }
 }
 
 
+
+/*
+ * Hwmon interface
+ */
+
+static ssize_t i8k_hwmon_show_temp(struct device *dev,
+				   struct device_attribute *devattr,
+				   char *buf)
+{
+	int cpu_temp;
+
+	cpu_temp = i8k_get_temp(0);
+	if (cpu_temp < 0)
+		return cpu_temp;
+	return sprintf(buf, "%d\n", cpu_temp * 1000);
+}
+
+static ssize_t i8k_hwmon_show_fan(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	int fan_speed;
+
+	fan_speed = i8k_get_fan_speed(index);
+	if (fan_speed < 0)
+		return fan_speed;
+	return sprintf(buf, "%d\n", fan_speed);
+}
+
+static ssize_t i8k_hwmon_show_label(struct device *dev,
+				    struct device_attribute *devattr,
+				    char *buf)
+{
+	static const char *labels[4] = {
+		"i8k",
+		"CPU",
+		"Left Fan",
+		"Right Fan",
+	};
+	int index = to_sensor_dev_attr(devattr)->index;
+
+	return sprintf(buf, "%s\n", labels[index]);
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
+			  I8K_FAN_LEFT);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
+			  I8K_FAN_RIGHT);
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
+
+static void i8k_hwmon_remove_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_temp1_input);
+	device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
+	device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
+	device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
+	device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
+	device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
+	device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
+}
+
+static int __init i8k_init_hwmon(void)
+{
+	int err;
+
+	i8k_hwmon_dev = hwmon_device_register(NULL);
+	if (IS_ERR(i8k_hwmon_dev)) {
+		err = PTR_ERR(i8k_hwmon_dev);
+		i8k_hwmon_dev = NULL;
+		printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
+		return err;
+	}
+
+	/* Required name attribute */
+	err = device_create_file(i8k_hwmon_dev,
+				 &sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_unregister;
+
+	/* CPU temperature attributes, if temperature reading is OK */
+	err = i8k_get_temp(0);
+	if (err < 0) {
+		dev_dbg(i8k_hwmon_dev,
+			"Not creating temperature attributes (%d)\n", err);
+	} else {
+		err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
+		if (err)
+			goto exit_remove_files;
+		err = device_create_file(i8k_hwmon_dev,
+					 &sensor_dev_attr_temp1_label.dev_attr);
+		if (err)
+			goto exit_remove_files;
+	}
+
+	/* Left fan attributes, if left fan is present */
+	err = i8k_get_fan_status(I8K_FAN_LEFT);
+	if (err < 0) {
+		dev_dbg(i8k_hwmon_dev,
+			"Not creating %s fan attributes (%d)\n", "left", err);
+	} else {
+		err = device_create_file(i8k_hwmon_dev,
+					 &sensor_dev_attr_fan1_input.dev_attr);
+		if (err)
+			goto exit_remove_files;
+		err = device_create_file(i8k_hwmon_dev,
+					 &sensor_dev_attr_fan1_label.dev_attr);
+		if (err)
+			goto exit_remove_files;
+	}
+
+	/* Right fan attributes, if right fan is present */
+	err = i8k_get_fan_status(I8K_FAN_RIGHT);
+	if (err < 0) {
+		dev_dbg(i8k_hwmon_dev,
+			"Not creating %s fan attributes (%d)\n", "right", err);
+	} else {
+		err = device_create_file(i8k_hwmon_dev,
+					 &sensor_dev_attr_fan2_input.dev_attr);
+		if (err)
+			goto exit_remove_files;
+		err = device_create_file(i8k_hwmon_dev,
+					 &sensor_dev_attr_fan2_label.dev_attr);
+		if (err)
+			goto exit_remove_files;
+	}
+
+	return 0;
+
+ exit_remove_files:
+	i8k_hwmon_remove_files(i8k_hwmon_dev);
+ exit_unregister:
+	hwmon_device_unregister(i8k_hwmon_dev);
+	return err;
+}
+
+static void __exit i8k_exit_hwmon(void)
+{
+	i8k_hwmon_remove_files(i8k_hwmon_dev);
+	hwmon_device_unregister(i8k_hwmon_dev);
+}
+
 static struct dmi_system_id __initdata i8k_dmi_table[] = {
 static struct dmi_system_id __initdata i8k_dmi_table[] = {
 	{
 	{
 		.ident = "Dell Inspiron",
 		.ident = "Dell Inspiron",
@@ -580,6 +732,7 @@ static int __init i8k_probe(void)
 static int __init i8k_init(void)
 static int __init i8k_init(void)
 {
 {
 	struct proc_dir_entry *proc_i8k;
 	struct proc_dir_entry *proc_i8k;
+	int err;
 
 
 	/* Are we running on an supported laptop? */
 	/* Are we running on an supported laptop? */
 	if (i8k_probe())
 	if (i8k_probe())
@@ -590,15 +743,24 @@ static int __init i8k_init(void)
 	if (!proc_i8k)
 	if (!proc_i8k)
 		return -ENOENT;
 		return -ENOENT;
 
 
+	err = i8k_init_hwmon();
+	if (err)
+		goto exit_remove_proc;
+
 	printk(KERN_INFO
 	printk(KERN_INFO
 	       "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
 	       "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
 	       I8K_VERSION);
 	       I8K_VERSION);
 
 
 	return 0;
 	return 0;
+
+ exit_remove_proc:
+	remove_proc_entry("i8k", NULL);
+	return err;
 }
 }
 
 
 static void __exit i8k_exit(void)
 static void __exit i8k_exit(void)
 {
 {
+	i8k_exit_hwmon();
 	remove_proc_entry("i8k", NULL);
 	remove_proc_entry("i8k", NULL);
 }
 }