All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] v1 of IBM power meter driver
@ 2007-08-27 21:14 ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-27 21:14 UTC (permalink / raw)
  To: Mark M. Hoffman, lm-sensors, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 19771 bytes --]

Hi everyone,

Attached is a driver to export sensor readings from power meters that
are found in several IBM x86 systems.  At the moment, the hwmon sysfs
documentation doesn't mention any naming conventions for sensors that
measure Watts, so I am proposing that they be called "powerX_input" in a
fashion similar to temperature/rpm/current sensors.  If that is
agreeable to everyone, I'll post a follow-up patch to amend the
documentation.

The patch should apply against 2.6.23-rc3 and has been tested on the
x3550, x3650, x3655, x3755 and HS20 blades that support it.  As far
as I know, those are the only systems in existence that have this
interface.
---
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  615 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 628 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..15ae9ec
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,615 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			value;
+	s16			max;
+	s16			min;
+	int			divisor;
+
+	struct sensor_device_attribute	attr_value;
+	struct sensor_device_attribute	attr_max;
+	struct sensor_device_attribute	attr_min;
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x1;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       data->rx_msg_data[3] | ((u16)data->rx_msg_data[2] << 8),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x2;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x3;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x6;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].value = ((u16)data->rx_msg_data[16] << 8) |
+					 data->rx_msg_data[17];
+		data->sensors[i].min = ((u16)data->rx_msg_data[18] << 8) |
+				       data->rx_msg_data[19];
+		data->sensors[i].max = ((u16)data->rx_msg_data[20] << 8) |
+				       data->rx_msg_data[21];
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].value);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].value / divisor,
+		       data->sensors[sensor].value % divisor);
+}
+
+static ssize_t ibmpex_show_max(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].max);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].max / divisor,
+		       data->sensors[sensor].max % divisor);
+}
+
+static ssize_t ibmpex_show_min(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].min);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].min / divisor,
+		       data->sensors[sensor].min % divisor);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x70 &&
+	    sensor_id[1] == 0x77 &&
+	    sensor_id[2] == 0x72)
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x74 &&
+	    sensor_id[1] == 0x65 &&
+	    sensor_id[2] == 0x6D)
+		return 1;
+	return 0;
+}
+
+static int power_sensor_divisor(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = 3; i < len - 1; i++)
+		if (sensor_id[i] == 0x41 &&
+		    sensor_id[i + 1] == 0x43)
+			return 1;
+
+	return 10;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+	char *n, *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].divisor =
+				power_sensor_divisor(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].divisor = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create value attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_value.dev_attr.attr.name = n;
+		data->sensors[i].attr_value.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_value.dev_attr.show = ibmpex_show_sensor;
+		data->sensors[i].attr_value.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_value.dev_attr);
+		if (err) {
+			data->sensors[i].attr_value.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create max attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_max_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_max.dev_attr.attr.name = n;
+		data->sensors[i].attr_max.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_max.dev_attr.show = ibmpex_show_max;
+		data->sensors[i].attr_max.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_max.dev_attr);
+		if (err) {
+			data->sensors[i].attr_max.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create min attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_min_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_min.dev_attr.attr.name = n;
+		data->sensors[i].attr_min.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_min.dev_attr.show = ibmpex_show_min;
+		data->sensors[i].attr_min.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_min.dev_attr);
+		if (err) {
+			data->sensors[i].attr_min.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+	}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = 0x3A;
+	data->tx_message.cmd = 0x3C;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+	}
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-27 21:14 ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-27 21:14 UTC (permalink / raw)
  To: Mark M. Hoffman, lm-sensors, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 19771 bytes --]

Hi everyone,

Attached is a driver to export sensor readings from power meters that
are found in several IBM x86 systems.  At the moment, the hwmon sysfs
documentation doesn't mention any naming conventions for sensors that
measure Watts, so I am proposing that they be called "powerX_input" in a
fashion similar to temperature/rpm/current sensors.  If that is
agreeable to everyone, I'll post a follow-up patch to amend the
documentation.

The patch should apply against 2.6.23-rc3 and has been tested on the
x3550, x3650, x3655, x3755 and HS20 blades that support it.  As far
as I know, those are the only systems in existence that have this
interface.
---
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  615 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 628 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..15ae9ec
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,615 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			value;
+	s16			max;
+	s16			min;
+	int			divisor;
+
+	struct sensor_device_attribute	attr_value;
+	struct sensor_device_attribute	attr_max;
+	struct sensor_device_attribute	attr_min;
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x1;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       data->rx_msg_data[3] | ((u16)data->rx_msg_data[2] << 8),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x2;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x3;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x6;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].value = ((u16)data->rx_msg_data[16] << 8) |
+					 data->rx_msg_data[17];
+		data->sensors[i].min = ((u16)data->rx_msg_data[18] << 8) |
+				       data->rx_msg_data[19];
+		data->sensors[i].max = ((u16)data->rx_msg_data[20] << 8) |
+				       data->rx_msg_data[21];
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].value);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].value / divisor,
+		       data->sensors[sensor].value % divisor);
+}
+
+static ssize_t ibmpex_show_max(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].max);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].max / divisor,
+		       data->sensors[sensor].max % divisor);
+}
+
+static ssize_t ibmpex_show_min(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int divisor = data->sensors[sensor].divisor;
+	ibmpex_update_device(data);
+
+	if (divisor == 1)
+		return sprintf(buf, "%d\n", data->sensors[sensor].min);
+
+	return sprintf(buf, "%d.%d\n",
+		       data->sensors[sensor].min / divisor,
+		       data->sensors[sensor].min % divisor);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x70 &&
+	    sensor_id[1] == 0x77 &&
+	    sensor_id[2] == 0x72)
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x74 &&
+	    sensor_id[1] == 0x65 &&
+	    sensor_id[2] == 0x6D)
+		return 1;
+	return 0;
+}
+
+static int power_sensor_divisor(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = 3; i < len - 1; i++)
+		if (sensor_id[i] == 0x41 &&
+		    sensor_id[i + 1] == 0x43)
+			return 1;
+
+	return 10;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+	char *n, *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].divisor =
+				power_sensor_divisor(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].divisor = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create value attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_value.dev_attr.attr.name = n;
+		data->sensors[i].attr_value.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_value.dev_attr.show = ibmpex_show_sensor;
+		data->sensors[i].attr_value.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_value.dev_attr);
+		if (err) {
+			data->sensors[i].attr_value.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create max attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_max_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_max.dev_attr.attr.name = n;
+		data->sensors[i].attr_max.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_max.dev_attr.show = ibmpex_show_max;
+		data->sensors[i].attr_max.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_max.dev_attr);
+		if (err) {
+			data->sensors[i].attr_max.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create min attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_min_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_min.dev_attr.attr.name = n;
+		data->sensors[i].attr_min.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_min.dev_attr.show = ibmpex_show_min;
+		data->sensors[i].attr_min.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_min.dev_attr);
+		if (err) {
+			data->sensors[i].attr_min.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+	}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = 0x3A;
+	data->tx_message.cmd = 0x3C;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+	}
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH] v1 of IBM power meter driver
  2007-08-27 21:14 ` [lm-sensors] " Darrick J. Wong
@ 2007-08-28  1:50   ` Henrique de Moraes Holschuh
  -1 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-08-28  1:50 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Mark M. Hoffman, lm-sensors, linux-kernel

On Mon, 27 Aug 2007, Darrick J. Wong wrote:
> documentation doesn't mention any naming conventions for sensors that
> measure Watts, so I am proposing that they be called "powerX_input" in a
> fashion similar to temperature/rpm/current sensors.  If that is
> agreeable to everyone, I'll post a follow-up patch to amend the
> documentation.

What unit should we use? Watts are way, way too big as there is no
floating/fixed point in sysfs.  10^-6 W is probably what is called for,
since we already need 10^-3 V and 10^-3 A.  Small portable devices can
easily draw less than 10^-3 W nowadays.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-28  1:50   ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-08-28  1:50 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Mark M. Hoffman, lm-sensors, linux-kernel

On Mon, 27 Aug 2007, Darrick J. Wong wrote:
> documentation doesn't mention any naming conventions for sensors that
> measure Watts, so I am proposing that they be called "powerX_input" in a
> fashion similar to temperature/rpm/current sensors.  If that is
> agreeable to everyone, I'll post a follow-up patch to amend the
> documentation.

What unit should we use? Watts are way, way too big as there is no
floating/fixed point in sysfs.  10^-6 W is probably what is called for,
since we already need 10^-3 V and 10^-3 A.  Small portable devices can
easily draw less than 10^-3 W nowadays.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
  2007-08-28  1:50   ` [lm-sensors] " Henrique de Moraes Holschuh
@ 2007-08-28 11:19     ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-28 11:19 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Darrick J. Wong, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick, hi Henrique,

Good thing that manufacturers start including wattmeters in their
hardware. Hopefully this will help users better control their power
consumption in the long run.

On Mon, 27 Aug 2007 22:50:29 -0300, Henrique de Moraes Holschuh wrote:
> On Mon, 27 Aug 2007, Darrick J. Wong wrote:
> > documentation doesn't mention any naming conventions for sensors that
> > measure Watts, so I am proposing that they be called "powerX_input" in a
> > fashion similar to temperature/rpm/current sensors.  If that is
> > agreeable to everyone, I'll post a follow-up patch to amend the
> > documentation.
> 
> What unit should we use? Watts are way, way too big as there is no
> floating/fixed point in sysfs.  10^-6 W is probably what is called for,
> since we already need 10^-3 V and 10^-3 A.  Small portable devices can
> easily draw less than 10^-3 W nowadays.

Good point. The driver currently exports non-integer values, which is
not acceptable, so indeed it needs to be changed. We want at least a
resolution of 10^-3 W. Not sure about 10^-6 W. I am surprised that
portable devices can really draw less than 1 mW, and be it the case, I
doubt that manufacturers will embed a wattmetter: it would probably
draw more current than the rest of the device ;) so it may not be
relevant for our decision.

So I think I'd go with 10^-3 W, but I welcome diverging opinions. Out
of curiosity, what is the physical resolution of IBM's device?

I see that the driver relies on IPMI. Can't it be merged with the
out-of-tree impisensors driver then? This would give that driver some
momentum so that it can finally be merged, and I would like to avoid
having two drivers if one is enough. Note though that I don't know
anything about IPMI so I might as well be totally wrong ;)

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-28 11:19     ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-28 11:19 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Darrick J. Wong, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick, hi Henrique,

Good thing that manufacturers start including wattmeters in their
hardware. Hopefully this will help users better control their power
consumption in the long run.

On Mon, 27 Aug 2007 22:50:29 -0300, Henrique de Moraes Holschuh wrote:
> On Mon, 27 Aug 2007, Darrick J. Wong wrote:
> > documentation doesn't mention any naming conventions for sensors that
> > measure Watts, so I am proposing that they be called "powerX_input" in a
> > fashion similar to temperature/rpm/current sensors.  If that is
> > agreeable to everyone, I'll post a follow-up patch to amend the
> > documentation.
> 
> What unit should we use? Watts are way, way too big as there is no
> floating/fixed point in sysfs.  10^-6 W is probably what is called for,
> since we already need 10^-3 V and 10^-3 A.  Small portable devices can
> easily draw less than 10^-3 W nowadays.

Good point. The driver currently exports non-integer values, which is
not acceptable, so indeed it needs to be changed. We want at least a
resolution of 10^-3 W. Not sure about 10^-6 W. I am surprised that
portable devices can really draw less than 1 mW, and be it the case, I
doubt that manufacturers will embed a wattmetter: it would probably
draw more current than the rest of the device ;) so it may not be
relevant for our decision.

So I think I'd go with 10^-3 W, but I welcome diverging opinions. Out
of curiosity, what is the physical resolution of IBM's device?

I see that the driver relies on IPMI. Can't it be merged with the
out-of-tree impisensors driver then? This would give that driver some
momentum so that it can finally be merged, and I would like to avoid
having two drivers if one is enough. Note though that I don't know
anything about IPMI so I might as well be totally wrong ;)

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
  2007-08-28 11:19     ` Jean Delvare
@ 2007-08-28 16:28       ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:28 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

[-- Attachment #1: Type: text/plain, Size: 2472 bytes --]

On Tue, Aug 28, 2007 at 01:19:42PM +0200, Jean Delvare wrote:
> Hi Darrick, hi Henrique,
> 
> Good thing that manufacturers start including wattmeters in their
> hardware. Hopefully this will help users better control their power
> consumption in the long run.

Yes, we're working on that too. :)

> > What unit should we use? Watts are way, way too big as there is no
> > floating/fixed point in sysfs.  10^-6 W is probably what is called for,
> > since we already need 10^-3 V and 10^-3 A.  Small portable devices can
> > easily draw less than 10^-3 W nowadays.
> 
> Good point. The driver currently exports non-integer values, which is
> not acceptable, so indeed it needs to be changed. We want at least a

I had a feeling you might say that. :)  I'll post a follow-up patch that
presents the power meters in mW instead of W.

> resolution of 10^-3 W. Not sure about 10^-6 W. I am surprised that
> portable devices can really draw less than 1 mW, and be it the case, I

Are there meters that can measure that small a quantity of power?  If
so, then I'll vote for uW; otherwise, mW is fine enough for me.

> doubt that manufacturers will embed a wattmetter: it would probably
> draw more current than the rest of the device ;) so it may not be
> relevant for our decision.
> 
> So I think I'd go with 10^-3 W, but I welcome diverging opinions. Out
> of curiosity, what is the physical resolution of IBM's device?

I don't know for sure, but my observation is that they're no more
accurate than a Watt.  Some of those meters appear to be averages, which
would explain why they occasionally have deciWatt components.

> I see that the driver relies on IPMI. Can't it be merged with the
> out-of-tree impisensors driver then? This would give that driver some
> momentum so that it can finally be merged, and I would like to avoid
> having two drivers if one is enough. Note though that I don't know
> anything about IPMI so I might as well be totally wrong ;)

The IBM PEx meters are accessible via custom IPMI commands, not the
standard IPMI sensor interface.  I considered modifying the ipmisensors
driver, but reached the opinion that it would clutter that driver
unnecessarily, particularly since there are a lot of systems with IPMI
BMCs, but most of them will not have this interface.

That said, what is standing in the way of ipmisensors being merged?  I
think I can shake out some free time to apply TLC.

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-28 16:28       ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:28 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors


[-- Attachment #1.1: Type: text/plain, Size: 2472 bytes --]

On Tue, Aug 28, 2007 at 01:19:42PM +0200, Jean Delvare wrote:
> Hi Darrick, hi Henrique,
> 
> Good thing that manufacturers start including wattmeters in their
> hardware. Hopefully this will help users better control their power
> consumption in the long run.

Yes, we're working on that too. :)

> > What unit should we use? Watts are way, way too big as there is no
> > floating/fixed point in sysfs.  10^-6 W is probably what is called for,
> > since we already need 10^-3 V and 10^-3 A.  Small portable devices can
> > easily draw less than 10^-3 W nowadays.
> 
> Good point. The driver currently exports non-integer values, which is
> not acceptable, so indeed it needs to be changed. We want at least a

I had a feeling you might say that. :)  I'll post a follow-up patch that
presents the power meters in mW instead of W.

> resolution of 10^-3 W. Not sure about 10^-6 W. I am surprised that
> portable devices can really draw less than 1 mW, and be it the case, I

Are there meters that can measure that small a quantity of power?  If
so, then I'll vote for uW; otherwise, mW is fine enough for me.

> doubt that manufacturers will embed a wattmetter: it would probably
> draw more current than the rest of the device ;) so it may not be
> relevant for our decision.
> 
> So I think I'd go with 10^-3 W, but I welcome diverging opinions. Out
> of curiosity, what is the physical resolution of IBM's device?

I don't know for sure, but my observation is that they're no more
accurate than a Watt.  Some of those meters appear to be averages, which
would explain why they occasionally have deciWatt components.

> I see that the driver relies on IPMI. Can't it be merged with the
> out-of-tree impisensors driver then? This would give that driver some
> momentum so that it can finally be merged, and I would like to avoid
> having two drivers if one is enough. Note though that I don't know
> anything about IPMI so I might as well be totally wrong ;)

The IBM PEx meters are accessible via custom IPMI commands, not the
standard IPMI sensor interface.  I considered modifying the ipmisensors
driver, but reached the opinion that it would clutter that driver
unnecessarily, particularly since there are a lot of systems with IPMI
BMCs, but most of them will not have this interface.

That said, what is standing in the way of ipmisensors being merged?  I
think I can shake out some free time to apply TLC.

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-28 11:19     ` Jean Delvare
@ 2007-08-28 16:44       ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:44 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

[-- Attachment #1: Type: text/plain, Size: 867 bytes --]

Update the hwmon sysfs interface documentation to include a specification
for power meters.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 Documentation/hwmon/sysfs-interface |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index b3a9e1b..da546ce 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
 		Unit: milliampere
 		RO
 
+*********
+* Power *
+*********
+
+power[1-*]_input	Current power use
+			Unit: milliWatt
+			RO
+
+power[1-*]_max_input	Historical maximum power use
+			Unit: milliWatt
+			RO
+
+power[1-*]_min_input	Historical minimum power use
+			Unit: milliWatt
+			RO
 
 **********
 * Alarms *

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-08-28 16:44       ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:44 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors


[-- Attachment #1.1: Type: text/plain, Size: 867 bytes --]

Update the hwmon sysfs interface documentation to include a specification
for power meters.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 Documentation/hwmon/sysfs-interface |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index b3a9e1b..da546ce 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
 		Unit: milliampere
 		RO
 
+*********
+* Power *
+*********
+
+power[1-*]_input	Current power use
+			Unit: milliWatt
+			RO
+
+power[1-*]_max_input	Historical maximum power use
+			Unit: milliWatt
+			RO
+
+power[1-*]_min_input	Historical minimum power use
+			Unit: milliWatt
+			RO
 
 **********
 * Alarms *

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH] v2 of IBM power meter driver
  2007-08-28 11:19     ` Jean Delvare
@ 2007-08-28 16:49       ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:49 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

[-- Attachment #1: Type: text/plain, Size: 18716 bytes --]

ibm_pex: Driver to export IBM PowerExecutive power meter sensor readings
in mW.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  600 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 613 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..10f1205
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,600 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			value;
+	s16			max;
+	s16			min;
+	int			multiplier;
+
+	struct sensor_device_attribute	attr_value;
+	struct sensor_device_attribute	attr_max;
+	struct sensor_device_attribute	attr_min;
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x1;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       data->rx_msg_data[3] | ((u16)data->rx_msg_data[2] << 8),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x2;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x3;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x6;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].value = ((u16)data->rx_msg_data[16] << 8) |
+					 data->rx_msg_data[17];
+		data->sensors[i].min = ((u16)data->rx_msg_data[18] << 8) |
+				       data->rx_msg_data[19];
+		data->sensors[i].max = ((u16)data->rx_msg_data[20] << 8) |
+				       data->rx_msg_data[21];
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].value * multiplier);
+}
+
+static ssize_t ibmpex_show_max(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].max * multiplier);
+}
+
+static ssize_t ibmpex_show_min(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].min * multiplier);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x70 &&
+	    sensor_id[1] == 0x77 &&
+	    sensor_id[2] == 0x72)
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x74 &&
+	    sensor_id[1] == 0x65 &&
+	    sensor_id[2] == 0x6D)
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = 3; i < len - 1; i++)
+		if (sensor_id[i] == 0x41 &&
+		    sensor_id[i + 1] == 0x43)
+			return 1000;
+
+	return 100;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+	char *n, *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create value attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_value.dev_attr.attr.name = n;
+		data->sensors[i].attr_value.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_value.dev_attr.show = ibmpex_show_sensor;
+		data->sensors[i].attr_value.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_value.dev_attr);
+		if (err) {
+			data->sensors[i].attr_value.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create max attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_max_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_max.dev_attr.attr.name = n;
+		data->sensors[i].attr_max.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_max.dev_attr.show = ibmpex_show_max;
+		data->sensors[i].attr_max.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_max.dev_attr);
+		if (err) {
+			data->sensors[i].attr_max.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create min attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_min_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_min.dev_attr.attr.name = n;
+		data->sensors[i].attr_min.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_min.dev_attr.show = ibmpex_show_min;
+		data->sensors[i].attr_min.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_min.dev_attr);
+		if (err) {
+			data->sensors[i].attr_min.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+	}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = 0x3A;
+	data->tx_message.cmd = 0x3C;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+	}
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH] v2 of IBM power meter driver
@ 2007-08-28 16:49       ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 16:49 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors


[-- Attachment #1.1: Type: text/plain, Size: 18716 bytes --]

ibm_pex: Driver to export IBM PowerExecutive power meter sensor readings
in mW.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  600 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 613 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..10f1205
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,600 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			value;
+	s16			max;
+	s16			min;
+	int			multiplier;
+
+	struct sensor_device_attribute	attr_value;
+	struct sensor_device_attribute	attr_max;
+	struct sensor_device_attribute	attr_min;
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x1;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       data->rx_msg_data[3] | ((u16)data->rx_msg_data[2] << 8),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = 0x2;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x3;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = 0x6;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].value = ((u16)data->rx_msg_data[16] << 8) |
+					 data->rx_msg_data[17];
+		data->sensors[i].min = ((u16)data->rx_msg_data[18] << 8) |
+				       data->rx_msg_data[19];
+		data->sensors[i].max = ((u16)data->rx_msg_data[20] << 8) |
+				       data->rx_msg_data[21];
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].value * multiplier);
+}
+
+static ssize_t ibmpex_show_max(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].max * multiplier);
+}
+
+static ssize_t ibmpex_show_min(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = attr->index >> 8;
+	int sensor = attr->index & 0xFF;
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n", data->sensors[sensor].min * multiplier);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x70 &&
+	    sensor_id[1] == 0x77 &&
+	    sensor_id[2] == 0x72)
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < 3)
+		return 0;
+
+	if (sensor_id[0] == 0x74 &&
+	    sensor_id[1] == 0x65 &&
+	    sensor_id[2] == 0x6D)
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = 3; i < len - 1; i++)
+		if (sensor_id[i] == 0x41 &&
+		    sensor_id[i + 1] == 0x43)
+			return 1000;
+
+	return 100;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+	char *n, *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create value attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_value.dev_attr.attr.name = n;
+		data->sensors[i].attr_value.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_value.dev_attr.show = ibmpex_show_sensor;
+		data->sensors[i].attr_value.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_value.dev_attr);
+		if (err) {
+			data->sensors[i].attr_value.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create max attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_max_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_max.dev_attr.attr.name = n;
+		data->sensors[i].attr_max.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_max.dev_attr.show = ibmpex_show_max;
+		data->sensors[i].attr_max.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_max.dev_attr);
+		if (err) {
+			data->sensors[i].attr_max.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+
+		/* Create min attribute */
+		n = kmalloc(32, GFP_KERNEL);
+		if (!n)
+			goto exit_remove;
+		sprintf(n, "%s%d_min_input", sensor_type, sensor_counter);
+		data->sensors[i].attr_min.dev_attr.attr.name = n;
+		data->sensors[i].attr_min.dev_attr.attr.mode = S_IRUGO;
+		data->sensors[i].attr_min.dev_attr.show = ibmpex_show_min;
+		data->sensors[i].attr_min.index = (data->interface << 8) |
+						    (i & 0xFF);
+
+		err = device_create_file(data->bmc_device,
+					 &data->sensors[i].attr_min.dev_attr);
+		if (err) {
+			data->sensors[i].attr_min.dev_attr.attr.name = NULL;
+			kfree(n);
+			goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+	}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = 0x3A;
+	data->tx_message.cmd = 0x3C;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++) {
+		if (data->sensors[i].attr_max.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_max.dev_attr);
+		kfree(data->sensors[i].attr_max.dev_attr.attr.name);
+		if (data->sensors[i].attr_min.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_min.dev_attr);
+		kfree(data->sensors[i].attr_min.dev_attr.attr.name);
+		if (data->sensors[i].attr_value.dev_attr.attr.name)
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr_value.dev_attr);
+		kfree(data->sensors[i].attr_value.dev_attr.attr.name);
+	}
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH] v3 of IBM power meter driver
  2007-08-28 16:49       ` [lm-sensors] " Darrick J. Wong
@ 2007-08-28 23:25         ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 23:25 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 17368 bytes --]

Dave Hansen complained about the magic numbers, repetitive code, and
various other minor problems with the driver code, so here's a v3 with
the magic numbers migrated to the top of the file and #define'd,
helper macros taking place of the bit shifting/masking activities, and
the compression of the value/min/max sysfs code into parameterized
functions.
--
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  564 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 577 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..632f897
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,564 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+#define PEX_GET_VERSION		1
+#define PEX_GET_SENSOR_COUNT	2
+#define PEX_GET_SENSOR_NAME	3
+#define PEX_GET_SENSOR_DATA	6
+
+#define PEX_NET_FUNCTION	0x3A
+#define PEX_COMMAND		0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+	u16 val = *(u16*)&data[offset];
+	return be16_to_cpu(val);
+}
+
+#define PEX_INTERFACE(idx)		((idx) >> 16)
+#define PEX_SENSOR(idx)			(((idx) >> 8) & 0xFF)
+#define PEX_FUNC(idx)			((idx) & 0xFF)
+#define PEX_INDEX(iface, num, fn)	(((iface) << 16) | ((num) << 8) | (fn))
+
+#define PEX_SENSOR_TYPE_LEN	3
+static char power_sensor_sig[] = {0x70, 0x77, 0x72};
+static char temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN		2
+static char watt_sensor_sig[]  = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS	3
+static char *sensor_name_templates[] = {
+	"%s%d_input",
+	"%s%d_min_input",
+	"%s%d_max_input"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			values[PEX_NUM_SENSOR_FUNCS];
+	int			multiplier;
+
+	struct sensor_device_attribute	attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_VERSION;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       extract_value(data->rx_msg_data, 2),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].values[0] =
+			extract_value(data->rx_msg_data, 16);
+		data->sensors[i].values[1] =
+			extract_value(data->rx_msg_data, 18);
+		data->sensors[i].values[2] =
+			extract_value(data->rx_msg_data, 20);
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = PEX_INTERFACE(attr->index);
+	int sensor = PEX_SENSOR(attr->index);
+	int func = PEX_FUNC(attr->index);
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n",
+		       data->sensors[sensor].values[func] * multiplier);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
+		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
+			return 1000;
+
+	return 100;
+}
+
+static int create_sensor(struct ibmpex_bmc_data *data, const char *type,
+			 int counter, int sensor, int func)
+{
+	int err;
+	char *n;
+
+	n = kmalloc(32, GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	sprintf(n, sensor_name_templates[func], type, counter);
+	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
+	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
+	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
+	data->sensors[sensor].attr[func].index =
+		PEX_INDEX(data->interface, sensor, func);
+
+	err = device_create_file(data->bmc_device,
+				 &data->sensors[sensor].attr[func].dev_attr);
+	if (err) {
+		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
+		kfree(n);
+		return err;
+	}
+
+	return 0;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, j, err;
+	char *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create attributes */
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
+			if (create_sensor(data, sensor_type, sensor_counter,
+					  i, j))
+				goto exit_remove;
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = PEX_NET_FUNCTION;
+	data->tx_message.cmd = PEX_COMMAND;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i, j;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH] v3 of IBM power meter driver
@ 2007-08-28 23:25         ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-28 23:25 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 17368 bytes --]

Dave Hansen complained about the magic numbers, repetitive code, and
various other minor problems with the driver code, so here's a v3 with
the magic numbers migrated to the top of the file and #define'd,
helper macros taking place of the bit shifting/masking activities, and
the compression of the value/min/max sysfs code into parameterized
functions.
--
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   12 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  564 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 577 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 555f470..41ffa2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -275,6 +275,18 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	depends on IPMI_SI
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a133981..31da6fe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..632f897
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,564 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(5 * HZ)
+#define DRVNAME			"ibmpex"
+
+#define PEX_GET_VERSION		1
+#define PEX_GET_SENSOR_COUNT	2
+#define PEX_GET_SENSOR_NAME	3
+#define PEX_GET_SENSOR_DATA	6
+
+#define PEX_NET_FUNCTION	0x3A
+#define PEX_COMMAND		0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+	u16 val = *(u16*)&data[offset];
+	return be16_to_cpu(val);
+}
+
+#define PEX_INTERFACE(idx)		((idx) >> 16)
+#define PEX_SENSOR(idx)			(((idx) >> 8) & 0xFF)
+#define PEX_FUNC(idx)			((idx) & 0xFF)
+#define PEX_INDEX(iface, num, fn)	(((iface) << 16) | ((num) << 8) | (fn))
+
+#define PEX_SENSOR_TYPE_LEN	3
+static char power_sensor_sig[] = {0x70, 0x77, 0x72};
+static char temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN		2
+static char watt_sensor_sig[]  = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS	3
+static char *sensor_name_templates[] = {
+	"%s%d_input",
+	"%s%d_min_input",
+	"%s%d_max_input"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			values[PEX_NUM_SENSOR_FUNCS];
+	int			multiplier;
+
+	struct sensor_device_attribute	attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct class_device	*class_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_VERSION;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       extract_value(data->rx_msg_data, 2),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].values[0] =
+			extract_value(data->rx_msg_data, 16);
+		data->sensors[i].values[1] =
+			extract_value(data->rx_msg_data, 18);
+		data->sensors[i].values[2] =
+			extract_value(data->rx_msg_data, 20);
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	int iface = PEX_INTERFACE(attr->index);
+	int sensor = PEX_SENSOR(attr->index);
+	int func = PEX_FUNC(attr->index);
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+	int multiplier = data->sensors[sensor].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n",
+		       data->sensors[sensor].values[func] * multiplier);
+}
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
+		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
+			return 1000;
+
+	return 100;
+}
+
+static int create_sensor(struct ibmpex_bmc_data *data, const char *type,
+			 int counter, int sensor, int func)
+{
+	int err;
+	char *n;
+
+	n = kmalloc(32, GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	sprintf(n, sensor_name_templates[func], type, counter);
+	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
+	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
+	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
+	data->sensors[sensor].attr[func].index =
+		PEX_INDEX(data->interface, sensor, func);
+
+	err = device_create_file(data->bmc_device,
+				 &data->sensors[sensor].attr[func].dev_attr);
+	if (err) {
+		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
+		kfree(n);
+		return err;
+	}
+
+	return 0;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, j, err;
+	char *sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err < 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = "power";
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = "temp";
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create attributes */
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
+			if (create_sensor(data, sensor_type, sensor_counter,
+					  i, j))
+				goto exit_remove;
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	kfree(data->sensors);
+	return -ENOENT;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = PEX_NET_FUNCTION;
+	data->tx_message.cmd = PEX_COMMAND;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->class_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->class_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+	
+	return;
+
+out_register:
+	hwmon_device_unregister(data->class_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i, j;
+
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	list_del(&data->list);
+	hwmon_device_unregister(data->class_dev);
+	ipmi_destroy_user(data->user);
+	if (data->sensors)
+		kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-28 16:44       ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
@ 2007-08-29  9:10         ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-29  9:10 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick,

On Tue, 28 Aug 2007 09:44:40 -0700, Darrick J. Wong wrote:
> Update the hwmon sysfs interface documentation to include a specification
> for power meters.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  Documentation/hwmon/sysfs-interface |   15 +++++++++++++++
>  1 files changed, 15 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index b3a9e1b..da546ce 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_input	Current power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_max_input	Historical maximum power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_min_input	Historical minimum power use
> +			Unit: milliWatt
> +			RO
>  

I'm not sure if we want these "historical" files. We don't have them for
the other input types, and I believe that it's not the driver's job to
compute and export these values. If anyone cares about the history of
sensed values, that's something for a user-space application to
implement. This will also be much more flexible in user-space, as it
becomes possible to decide the exact time range to consider, to
remember at which time the peak occurred, etc.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-08-29  9:10         ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-29  9:10 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick,

On Tue, 28 Aug 2007 09:44:40 -0700, Darrick J. Wong wrote:
> Update the hwmon sysfs interface documentation to include a specification
> for power meters.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  Documentation/hwmon/sysfs-interface |   15 +++++++++++++++
>  1 files changed, 15 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index b3a9e1b..da546ce 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_input	Current power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_max_input	Historical maximum power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_min_input	Historical minimum power use
> +			Unit: milliWatt
> +			RO
>  

I'm not sure if we want these "historical" files. We don't have them for
the other input types, and I believe that it's not the driver's job to
compute and export these values. If anyone cares about the history of
sensed values, that's something for a user-space application to
implement. This will also be much more flexible in user-space, as it
becomes possible to decide the exact time range to consider, to
remember at which time the peak occurred, etc.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
  2007-08-28 16:28       ` Darrick J. Wong
@ 2007-08-29  9:49         ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-29  9:49 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel,
	lm-sensors, Yani Ioannou

Hi Darrick,

On Tue, 28 Aug 2007 09:28:51 -0700, Darrick J. Wong wrote:
> On Tue, Aug 28, 2007 at 01:19:42PM +0200, Jean Delvare wrote:
> > I see that the driver relies on IPMI. Can't it be merged with the
> > out-of-tree impisensors driver then? This would give that driver some
> > momentum so that it can finally be merged, and I would like to avoid
> > having two drivers if one is enough. Note though that I don't know
> > anything about IPMI so I might as well be totally wrong ;)
> 
> The IBM PEx meters are accessible via custom IPMI commands, not the
> standard IPMI sensor interface.  I considered modifying the ipmisensors
> driver, but reached the opinion that it would clutter that driver
> unnecessarily, particularly since there are a lot of systems with IPMI
> BMCs, but most of them will not have this interface.

OK, fine with me then.

> That said, what is standing in the way of ipmisensors being merged?  I
> think I can shake out some free time to apply TLC.

I don't know exactly, check out the website for details:
  http://bmcsensors-26.sourceforge.net/

It needs at least review and testing, but maybe there are a few more
steps before merging can be considered. Adding Yani (the author) to Cc,
please work with him if you're interested.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-29  9:49         ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-29  9:49 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel,
	lm-sensors, Yani Ioannou

Hi Darrick,

On Tue, 28 Aug 2007 09:28:51 -0700, Darrick J. Wong wrote:
> On Tue, Aug 28, 2007 at 01:19:42PM +0200, Jean Delvare wrote:
> > I see that the driver relies on IPMI. Can't it be merged with the
> > out-of-tree impisensors driver then? This would give that driver some
> > momentum so that it can finally be merged, and I would like to avoid
> > having two drivers if one is enough. Note though that I don't know
> > anything about IPMI so I might as well be totally wrong ;)
> 
> The IBM PEx meters are accessible via custom IPMI commands, not the
> standard IPMI sensor interface.  I considered modifying the ipmisensors
> driver, but reached the opinion that it would clutter that driver
> unnecessarily, particularly since there are a lot of systems with IPMI
> BMCs, but most of them will not have this interface.

OK, fine with me then.

> That said, what is standing in the way of ipmisensors being merged?  I
> think I can shake out some free time to apply TLC.

I don't know exactly, check out the website for details:
  http://bmcsensors-26.sourceforge.net/

It needs at least review and testing, but maybe there are a few more
steps before merging can be considered. Adding Yani (the author) to Cc,
please work with him if you're interested.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
  2007-08-29  9:49         ` Jean Delvare
@ 2007-08-29 12:45           ` Frank Phillips
  -1 siblings, 0 replies; 72+ messages in thread
From: Frank Phillips @ 2007-08-29 12:45 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Darrick J. Wong, Henrique de Moraes Holschuh, Mark M. Hoffman,
	linux-kernel, lm-sensors, Yani Ioannou

> It needs at least review and testing, but maybe there are a few more
> steps before merging can be considered. Adding Yani (the author) to Cc,
> please work with him if you're interested.

As a user, I can tell you that it completely fills my dmesg with this sort

ipmisensors: sensor 50 (type 1) reading 60
ipmisensors: Send 0x2d 0x30 0x0
ipmisensors: received message
ipmisensors: sensor 48 (type 1) reading 28
ipmisensors: Send 0x2d 0x1b 0x0
ipmisensors: received message
ipmisensors: sensor 27 (type 2) reading 34
ipmisensors: Send 0x2d 0x1a 0x0
ipmisensors: received message
ipmisensors: sensor 26 (type 2) reading 187
ipmisensors: Send 0x2d 0x18 0x0

That's the only problem I have. It would be great to see it merged.

Frank

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v1 of IBM power meter driver
@ 2007-08-29 12:45           ` Frank Phillips
  0 siblings, 0 replies; 72+ messages in thread
From: Frank Phillips @ 2007-08-29 12:45 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Darrick J. Wong, Henrique de Moraes Holschuh, Mark M. Hoffman,
	linux-kernel, lm-sensors, Yani Ioannou

> It needs at least review and testing, but maybe there are a few more
> steps before merging can be considered. Adding Yani (the author) to Cc,
> please work with him if you're interested.

As a user, I can tell you that it completely fills my dmesg with this sort

ipmisensors: sensor 50 (type 1) reading 60
ipmisensors: Send 0x2d 0x30 0x0
ipmisensors: received message
ipmisensors: sensor 48 (type 1) reading 28
ipmisensors: Send 0x2d 0x1b 0x0
ipmisensors: received message
ipmisensors: sensor 27 (type 2) reading 34
ipmisensors: Send 0x2d 0x1a 0x0
ipmisensors: received message
ipmisensors: sensor 26 (type 2) reading 187
ipmisensors: Send 0x2d 0x18 0x0

That's the only problem I have. It would be great to see it merged.

Frank

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-29  9:10         ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
@ 2007-08-29 14:50           ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-29 14:50 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

[-- Attachment #1: Type: text/plain, Size: 408 bytes --]

On Wed, Aug 29, 2007 at 11:10:52AM +0200, Jean Delvare wrote:

> I'm not sure if we want these "historical" files. We don't have them for
> the other input types, and I believe that it's not the driver's job to
> compute and export these values. If anyone cares about the history of

In the case of ibmpex, it is the _hardware_ that computes the historical
data; the driver merely exports what it sees.

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-08-29 14:50           ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-08-29 14:50 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors


[-- Attachment #1.1: Type: text/plain, Size: 408 bytes --]

On Wed, Aug 29, 2007 at 11:10:52AM +0200, Jean Delvare wrote:

> I'm not sure if we want these "historical" files. We don't have them for
> the other input types, and I believe that it's not the driver's job to
> compute and export these values. If anyone cares about the history of

In the case of ibmpex, it is the _hardware_ that computes the historical
data; the driver merely exports what it sees.

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-29 14:50           ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
@ 2007-08-30  9:57             ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-30  9:57 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick,

On Wed, 29 Aug 2007 07:50:03 -0700, Darrick J. Wong wrote:
> On Wed, Aug 29, 2007 at 11:10:52AM +0200, Jean Delvare wrote:
> 
> > I'm not sure if we want these "historical" files. We don't have them for
> > the other input types, and I believe that it's not the driver's job to
> > compute and export these values. If anyone cares about the history of
> 
> In the case of ibmpex, it is the _hardware_ that computes the historical
> data; the driver merely exports what it sees.

OK, that's a bit different then, but I'm still not sure that there is
much value in exporting these values in sysfs, in particular if there
is no way to reset them.

I am also not happy with the names you proposed: power1_max_input and
power1_min_input are somewhat confusing IMHO, I'd suggest
power1_input_highest and power1_input_lowest to make them clearly
different from the min and max limits we have for other sensor types.
If we have them at all, of course.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-08-30  9:57             ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-08-30  9:57 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

Hi Darrick,

On Wed, 29 Aug 2007 07:50:03 -0700, Darrick J. Wong wrote:
> On Wed, Aug 29, 2007 at 11:10:52AM +0200, Jean Delvare wrote:
> 
> > I'm not sure if we want these "historical" files. We don't have them for
> > the other input types, and I believe that it's not the driver's job to
> > compute and export these values. If anyone cares about the history of
> 
> In the case of ibmpex, it is the _hardware_ that computes the historical
> data; the driver merely exports what it sees.

OK, that's a bit different then, but I'm still not sure that there is
much value in exporting these values in sysfs, in particular if there
is no way to reset them.

I am also not happy with the names you proposed: power1_max_input and
power1_min_input are somewhat confusing IMHO, I'd suggest
power1_input_highest and power1_input_lowest to make them clearly
different from the min and max limits we have for other sensor types.
If we have them at all, of course.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-28 16:44       ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
@ 2007-09-01 17:10         ` Pavel Machek
  -1 siblings, 0 replies; 72+ messages in thread
From: Pavel Machek @ 2007-09-01 17:10 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, Mark M. Hoffman,
	linux-kernel, lm-sensors

Hi!

> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index b3a9e1b..da546ce 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_input	Current power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_max_input	Historical maximum power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_min_input	Historical minimum power use
> +			Unit: milliWatt
> +			RO

Should we add power?_10sec_avg_input? IIRC thinkpads export that,
too.
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-01 17:10         ` Pavel Machek
  0 siblings, 0 replies; 72+ messages in thread
From: Pavel Machek @ 2007-09-01 17:10 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, Mark M. Hoffman,
	linux-kernel, lm-sensors

Hi!

> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index b3a9e1b..da546ce 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -304,6 +304,21 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_input	Current power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_max_input	Historical maximum power use
> +			Unit: milliWatt
> +			RO
> +
> +power[1-*]_min_input	Historical minimum power use
> +			Unit: milliWatt
> +			RO

Should we add power?_10sec_avg_input? IIRC thinkpads export that,
too.
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-01 17:10         ` [lm-sensors] [PATCH] hwmon: Add power meters to Pavel Machek
@ 2007-09-01 18:05           ` Shem Multinymous
  -1 siblings, 0 replies; 72+ messages in thread
From: Shem Multinymous @ 2007-09-01 18:05 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Darrick J. Wong, Jean Delvare, Henrique de Moraes Holschuh,
	Mark M. Hoffman, linux-kernel, lm-sensors

On 9/1/07, Pavel Machek <pavel@ucw.cz> wrote:
> > diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> > index b3a9e1b..da546ce 100644
> > --- a/Documentation/hwmon/sysfs-interface
> > +++ b/Documentation/hwmon/sysfs-interface
> > @@ -304,6 +304,21 @@ curr[1-*]_input  Current input value
> >               Unit: milliampere
> >               RO
> >
> > +*********
> > +* Power *
> > +*********
> > +
> > +power[1-*]_input     Current power use
> > +                     Unit: milliWatt
> > +                     RO
> > +
> > +power[1-*]_max_input Historical maximum power use
> > +                     Unit: milliWatt
> > +                     RO
> > +
> > +power[1-*]_min_input Historical minimum power use
> > +                     Unit: milliWatt
> > +                     RO
>
> Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> too.

Indeed, ThinkPads report both instantaneous power/current and a
rolling average (exponentially decaying, I think) over the last ~10
seconds. ACPI provides only the rolling average, and the out-of-tree
tp_smapi driver provides both.

More generally, linux/power_supply.h defines these attributes:

	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_VOLTAGE_AVG,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_CURRENT_AVG,

It would be nice if the power meter interface uses the same
conventions as the power supply interface, since the former is
essentially a subset of the latter. Userspace app that read power
meters via sysfs should work for power supplies too.

  Shem

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-01 18:05           ` Shem Multinymous
  0 siblings, 0 replies; 72+ messages in thread
From: Shem Multinymous @ 2007-09-01 18:05 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Darrick J. Wong, Jean Delvare, Henrique de Moraes Holschuh,
	Mark M. Hoffman, linux-kernel, lm-sensors

On 9/1/07, Pavel Machek <pavel@ucw.cz> wrote:
> > diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> > index b3a9e1b..da546ce 100644
> > --- a/Documentation/hwmon/sysfs-interface
> > +++ b/Documentation/hwmon/sysfs-interface
> > @@ -304,6 +304,21 @@ curr[1-*]_input  Current input value
> >               Unit: milliampere
> >               RO
> >
> > +*********
> > +* Power *
> > +*********
> > +
> > +power[1-*]_input     Current power use
> > +                     Unit: milliWatt
> > +                     RO
> > +
> > +power[1-*]_max_input Historical maximum power use
> > +                     Unit: milliWatt
> > +                     RO
> > +
> > +power[1-*]_min_input Historical minimum power use
> > +                     Unit: milliWatt
> > +                     RO
>
> Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> too.

Indeed, ThinkPads report both instantaneous power/current and a
rolling average (exponentially decaying, I think) over the last ~10
seconds. ACPI provides only the rolling average, and the out-of-tree
tp_smapi driver provides both.

More generally, linux/power_supply.h defines these attributes:

	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_VOLTAGE_AVG,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_CURRENT_AVG,

It would be nice if the power meter interface uses the same
conventions as the power supply interface, since the former is
essentially a subset of the latter. Userspace app that read power
meters via sysfs should work for power supplies too.

  Shem

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-01 18:05           ` [lm-sensors] [PATCH] hwmon: Add power meters to Shem Multinymous
@ 2007-09-01 19:44             ` Henrique de Moraes Holschuh
  -1 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-01 19:44 UTC (permalink / raw)
  To: Shem Multinymous
  Cc: Pavel Machek, linux-kernel, lm-sensors, Mark M. Hoffman, Darrick J. Wong

On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > too.

I'd say it is enough to know it is an average, you don't need to specify HOW
it is averaged.

But there is a real need to export both instantaneous and averaged values in
power supplies/power sources, so we should have a standard way to do so in
hwmon, and power_supply class should use it (or the opposite, it doesn't
matter, as long as everyone does it the same way).

> It would be nice if the power meter interface uses the same
> conventions as the power supply interface, since the former is
> essentially a subset of the latter. Userspace app that read power
> meters via sysfs should work for power supplies too.

Agreed.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters
@ 2007-09-01 19:44             ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-01 19:44 UTC (permalink / raw)
  To: Shem Multinymous
  Cc: Pavel Machek, linux-kernel, lm-sensors, Mark M. Hoffman, Darrick J. Wong

On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > too.

I'd say it is enough to know it is an average, you don't need to specify HOW
it is averaged.

But there is a real need to export both instantaneous and averaged values in
power supplies/power sources, so we should have a standard way to do so in
hwmon, and power_supply class should use it (or the opposite, it doesn't
matter, as long as everyone does it the same way).

> It would be nice if the power meter interface uses the same
> conventions as the power supply interface, since the former is
> essentially a subset of the latter. Userspace app that read power
> meters via sysfs should work for power supplies too.

Agreed.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-01 19:44             ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
@ 2007-09-02 19:38               ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-02 19:38 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sat, 1 Sep 2007 16:44:17 -0300, Henrique de Moraes Holschuh wrote:
> On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > > too.
> 
> I'd say it is enough to know it is an average, you don't need to specify HOW
> it is averaged.

Agreed.

> But there is a real need to export both instantaneous and averaged values in
> power supplies/power sources, so we should have a standard way to do so in
> hwmon, and power_supply class should use it (or the opposite, it doesn't
> matter, as long as everyone does it the same way).

I guess power[1-*]_average would be OK?

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-02 19:38               ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-02 19:38 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sat, 1 Sep 2007 16:44:17 -0300, Henrique de Moraes Holschuh wrote:
> On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > > too.
> 
> I'd say it is enough to know it is an average, you don't need to specify HOW
> it is averaged.

Agreed.

> But there is a real need to export both instantaneous and averaged values in
> power supplies/power sources, so we should have a standard way to do so in
> hwmon, and power_supply class should use it (or the opposite, it doesn't
> matter, as long as everyone does it the same way).

I guess power[1-*]_average would be OK?

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-02 19:38               ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
@ 2007-09-03  2:02                 ` Henrique de Moraes Holschuh
  -1 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-03  2:02 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sun, 02 Sep 2007, Jean Delvare wrote:
> On Sat, 1 Sep 2007 16:44:17 -0300, Henrique de Moraes Holschuh wrote:
> > On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > > > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > > > too.
> > 
> > I'd say it is enough to know it is an average, you don't need to specify HOW
> > it is averaged.
> 
> Agreed.
> 
> > But there is a real need to export both instantaneous and averaged values in
> > power supplies/power sources, so we should have a standard way to do so in
> > hwmon, and power_supply class should use it (or the opposite, it doesn't
> > matter, as long as everyone does it the same way).
> 
> I guess power[1-*]_average would be OK?

AFAIK, yes.  It is probably not 100% in sync with the power supply class,
though.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters
@ 2007-09-03  2:02                 ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-03  2:02 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sun, 02 Sep 2007, Jean Delvare wrote:
> On Sat, 1 Sep 2007 16:44:17 -0300, Henrique de Moraes Holschuh wrote:
> > On Sat, 01 Sep 2007, Shem Multinymous wrote:
> > > > Should we add power?_10sec_avg_input? IIRC thinkpads export that,
> > > > too.
> > 
> > I'd say it is enough to know it is an average, you don't need to specify HOW
> > it is averaged.
> 
> Agreed.
> 
> > But there is a real need to export both instantaneous and averaged values in
> > power supplies/power sources, so we should have a standard way to do so in
> > hwmon, and power_supply class should use it (or the opposite, it doesn't
> > matter, as long as everyone does it the same way).
> 
> I guess power[1-*]_average would be OK?

AFAIK, yes.  It is probably not 100% in sync with the power supply class,
though.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-03  2:02                 ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
@ 2007-09-03 16:06                   ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-03 16:06 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sun, 2 Sep 2007 23:02:01 -0300, Henrique de Moraes Holschuh wrote:
> On Sun, 02 Sep 2007, Jean Delvare wrote:
> > I guess power[1-*]_average would be OK?
> 
> AFAIK, yes.  It is probably not 100% in sync with the power supply class,
> though.

Is the power supply class creating sysfs files? I see a number of
attributes listed in Documentation/power_supply_class.txt, but they are
all uppercase, which doesn't seem suitable for sysfs file names. That
document also doesn't list the numbering convention when multiple
channels are present.

My main worry is that we will have to add support for power measurement
in libsensors, and I would like it to be as easy as possible. Thus
sticking to the same naming convention hwmon have been using for years
appears to be the best solution.

I see that the power supply class units are 10^-6 A and 10^-6 W, so if
we are supposed to be compatible, I guess we'll have to use this too.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-03 16:06                   ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-03 16:06 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, Darrick J. Wong, linux-kernel, Pavel Machek, lm-sensors

On Sun, 2 Sep 2007 23:02:01 -0300, Henrique de Moraes Holschuh wrote:
> On Sun, 02 Sep 2007, Jean Delvare wrote:
> > I guess power[1-*]_average would be OK?
> 
> AFAIK, yes.  It is probably not 100% in sync with the power supply class,
> though.

Is the power supply class creating sysfs files? I see a number of
attributes listed in Documentation/power_supply_class.txt, but they are
all uppercase, which doesn't seem suitable for sysfs file names. That
document also doesn't list the numbering convention when multiple
channels are present.

My main worry is that we will have to add support for power measurement
in libsensors, and I would like it to be as easy as possible. Thus
sticking to the same naming convention hwmon have been using for years
appears to be the best solution.

I see that the power supply class units are 10^-6 A and 10^-6 W, so if
we are supposed to be compatible, I guess we'll have to use this too.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-03 16:06                   ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
@ 2007-09-03 23:22                     ` Henrique de Moraes Holschuh
  -1 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-03 23:22 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Mon, 03 Sep 2007, Jean Delvare wrote:
> On Sun, 2 Sep 2007 23:02:01 -0300, Henrique de Moraes Holschuh wrote:
> > On Sun, 02 Sep 2007, Jean Delvare wrote:
> > > I guess power[1-*]_average would be OK?
> > 
> > AFAIK, yes.  It is probably not 100% in sync with the power supply class,
> > though.
> 
> Is the power supply class creating sysfs files? I see a number of
> attributes listed in Documentation/power_supply_class.txt, but they are
> all uppercase, which doesn't seem suitable for sysfs file names. That
> document also doesn't list the numbering convention when multiple
> channels are present.

Looks like power supply needs some tweaks, then.

> My main worry is that we will have to add support for power measurement
> in libsensors, and I would like it to be as easy as possible. Thus
> sticking to the same naming convention hwmon have been using for years
> appears to be the best solution.

Agreed.

> I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> we are supposed to be compatible, I guess we'll have to use this too.

There was a good reason for that, and people who deal with small portables
said that they needed 10^-4 A or somesuch, at which point it makes more
sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters
@ 2007-09-03 23:22                     ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-03 23:22 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Mon, 03 Sep 2007, Jean Delvare wrote:
> On Sun, 2 Sep 2007 23:02:01 -0300, Henrique de Moraes Holschuh wrote:
> > On Sun, 02 Sep 2007, Jean Delvare wrote:
> > > I guess power[1-*]_average would be OK?
> > 
> > AFAIK, yes.  It is probably not 100% in sync with the power supply class,
> > though.
> 
> Is the power supply class creating sysfs files? I see a number of
> attributes listed in Documentation/power_supply_class.txt, but they are
> all uppercase, which doesn't seem suitable for sysfs file names. That
> document also doesn't list the numbering convention when multiple
> channels are present.

Looks like power supply needs some tweaks, then.

> My main worry is that we will have to add support for power measurement
> in libsensors, and I would like it to be as easy as possible. Thus
> sticking to the same naming convention hwmon have been using for years
> appears to be the best solution.

Agreed.

> I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> we are supposed to be compatible, I guess we'll have to use this too.

There was a good reason for that, and people who deal with small portables
said that they needed 10^-4 A or somesuch, at which point it makes more
sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-03 23:22                     ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
@ 2007-09-06  9:34                       ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-06  9:34 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Mon, 3 Sep 2007 20:22:25 -0300, Henrique de Moraes Holschuh wrote:
> On Mon, 03 Sep 2007, Jean Delvare wrote:
> > I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> > we are supposed to be compatible, I guess we'll have to use this too.
> 
> There was a good reason for that, and people who deal with small portables
> said that they needed 10^-4 A or somesuch, at which point it makes more
> sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.

Well, if we need 10^-4 A, and we use voltages in the 1-2 V range
(that's what CPU, AGP etc. use these days), then we obviously need
10^-4 W as well.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-06  9:34                       ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-06  9:34 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Mon, 3 Sep 2007 20:22:25 -0300, Henrique de Moraes Holschuh wrote:
> On Mon, 03 Sep 2007, Jean Delvare wrote:
> > I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> > we are supposed to be compatible, I guess we'll have to use this too.
> 
> There was a good reason for that, and people who deal with small portables
> said that they needed 10^-4 A or somesuch, at which point it makes more
> sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.

Well, if we need 10^-4 A, and we use voltages in the 1-2 V range
(that's what CPU, AGP etc. use these days), then we obviously need
10^-4 W as well.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-09-06  9:34                       ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
@ 2007-09-06 16:29                         ` Henrique de Moraes Holschuh
  -1 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-06 16:29 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Thu, 06 Sep 2007, Jean Delvare wrote:
> On Mon, 3 Sep 2007 20:22:25 -0300, Henrique de Moraes Holschuh wrote:
> > On Mon, 03 Sep 2007, Jean Delvare wrote:
> > > I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> > > we are supposed to be compatible, I guess we'll have to use this too.
> > 
> > There was a good reason for that, and people who deal with small portables
> > said that they needed 10^-4 A or somesuch, at which point it makes more
> > sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.
> 
> Well, if we need 10^-4 A, and we use voltages in the 1-2 V range
> (that's what CPU, AGP etc. use these days), then we obviously need
> 10^-4 W as well.

I see, and in that case it is safer to just go to 10^-6 for both.  It is more
future-proof.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters
@ 2007-09-06 16:29                         ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 72+ messages in thread
From: Henrique de Moraes Holschuh @ 2007-09-06 16:29 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, lm-sensors, linux-kernel, Pavel Machek, Darrick J. Wong

On Thu, 06 Sep 2007, Jean Delvare wrote:
> On Mon, 3 Sep 2007 20:22:25 -0300, Henrique de Moraes Holschuh wrote:
> > On Mon, 03 Sep 2007, Jean Delvare wrote:
> > > I see that the power supply class units are 10^-6 A and 10^-6 W, so if
> > > we are supposed to be compatible, I guess we'll have to use this too.
> > 
> > There was a good reason for that, and people who deal with small portables
> > said that they needed 10^-4 A or somesuch, at which point it makes more
> > sense to just go to 10^-6 already.  I don't recall why 10^-6 W, though.
> 
> Well, if we need 10^-4 A, and we use voltages in the 1-2 V range
> (that's what CPU, AGP etc. use these days), then we obviously need
> 10^-4 W as well.

I see, and in that case it is safer to just go to 10^-6 for both.  It is more
future-proof.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] v3 of IBM power meter driver
  2007-08-28 23:25         ` [lm-sensors] " Darrick J. Wong
@ 2007-09-11 13:23           ` Mark M. Hoffman
  -1 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-09-11 13:23 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick:

* Darrick J. Wong <djwong@us.ibm.com> [2007-08-28 16:25:05 -0700]:
> Dave Hansen complained about the magic numbers, repetitive code, and
> various other minor problems with the driver code, so here's a v3 with
> the magic numbers migrated to the top of the file and #define'd,
> helper macros taking place of the bit shifting/masking activities, and
> the compression of the value/min/max sysfs code into parameterized
> functions.
> --
> ibm_pex: Driver to export IBM PowerExecutive power meter sensors.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>

I am not an IPMI expert, so I would appreciate getting an Acked-by from
someone who knows more about that subsystem.

Anyway, some comments are below.  This is nowhere near a complete review yet.

> ---
> 
>  drivers/hwmon/Kconfig  |   12 +
>  drivers/hwmon/Makefile |    1 
>  drivers/hwmon/ibmpex.c |  564 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 577 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 555f470..41ffa2e 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -275,6 +275,18 @@ config SENSORS_CORETEMP
>  	  sensor inside your CPU. Supported all are all known variants
>  	  of Intel Core family.
>  
> +config SENSORS_IBMPEX
> +	tristate "IBM PowerExecutive temperature/power sensors"
> +	depends on IPMI_SI

Open question: can we use "select" here?  As written, it took some hunting to
even get this driver to show up as an option in menuconfig.

> +	help
> +	  If you say yes here you get support for the temperature and
> +	  power sensors in various IBM System X servers that support
> +	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
> +	  x3755, and certain HS20 blades.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ibmpex.
> +
>  config SENSORS_IT87
>  	tristate "ITE IT87xx and compatibles"
>  	select HWMON_VID
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index a133981..31da6fe 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
>  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
>  obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
>  obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
> +obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
>  obj-$(CONFIG_SENSORS_IT87)	+= it87.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> new file mode 100644
> index 0000000..632f897
> --- /dev/null
> +++ b/drivers/hwmon/ibmpex.c
> @@ -0,0 +1,564 @@
> +/*
> + * A hwmon driver for the IBM PowerExecutive temperature/power sensors
> + * Copyright (C) 2007 IBM
> + *
> + * Author: Darrick J. Wong <djwong@us.ibm.com>
> + *
> + * 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 Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/ipmi.h>
> +#include <linux/module.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>

#include <linux/jiffies.h>

> +#include <linux/mutex.h>
> +
> +#define REFRESH_INTERVAL	(5 * HZ)
> +#define DRVNAME			"ibmpex"
> +
> +#define PEX_GET_VERSION		1
> +#define PEX_GET_SENSOR_COUNT	2
> +#define PEX_GET_SENSOR_NAME	3
> +#define PEX_GET_SENSOR_DATA	6
> +
> +#define PEX_NET_FUNCTION	0x3A
> +#define PEX_COMMAND		0x3C
> +
> +static inline u16 extract_value(const char *data, int offset)
> +{
> +	u16 val = *(u16*)&data[offset];
> +	return be16_to_cpu(val);

	return be16_to_cpup((u16*)&data[offset]);

> +}
> +
> +#define PEX_INTERFACE(idx)		((idx) >> 16)
> +#define PEX_SENSOR(idx)			(((idx) >> 8) & 0xFF)
> +#define PEX_FUNC(idx)			((idx) & 0xFF)
> +#define PEX_INDEX(iface, num, fn)	(((iface) << 16) | ((num) << 8) | (fn))
> +
> +#define PEX_SENSOR_TYPE_LEN	3
> +static char power_sensor_sig[] = {0x70, 0x77, 0x72};
> +static char temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
> +
> +#define PEX_MULT_LEN		2
> +static char watt_sensor_sig[]  = {0x41, 0x43};
> +

All three above should be "static u8 const"

> +#define PEX_NUM_SENSOR_FUNCS	3
> +static char *sensor_name_templates[] = {
> +	"%s%d_input",
> +	"%s%d_min_input",
> +	"%s%d_max_input"
> +};
> +

static char const * const sensor_name_templates[] = {

> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
> +static void ibmpex_register_bmc(int iface, struct device *dev);
> +static void ibmpex_bmc_gone(int iface);
> +
> +struct ibmpex_sensor_data {
> +	int			in_use;
> +	s16			values[PEX_NUM_SENSOR_FUNCS];
> +	int			multiplier;
> +
> +	struct sensor_device_attribute	attr[PEX_NUM_SENSOR_FUNCS];
> +};
> +
> +struct ibmpex_bmc_data {
> +	struct list_head	list;
> +	struct class_device	*class_dev;

My current stack of patches includes one which requires that this be changed
to 'struct device *hwmon_dev', as 'struct class_device' is going away soon.
You may rebase on my testing tree[1], or else I will just follow up with a
patch to fix this up after I eventually merge yours.

[1] http://lm-sensors.org/kernel?p=kernel/mhoffman/hwmon-2.6.git;a=shortlog;h=testing

> +	struct device		*bmc_device;
> +	struct mutex		lock;
> +	char			valid;
> +	unsigned long		last_updated;	/* In jiffies */
> +
> +	struct ipmi_addr	address;
> +	struct completion	read_complete;
> +	ipmi_user_t		user;
> +	int			interface;
> +
> +	struct kernel_ipmi_msg	tx_message;
> +	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	long			tx_msgid;
> +
> +	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	unsigned long		rx_msg_len;
> +	unsigned char		rx_result;
> +	int			rx_recv_type;
> +
> +	unsigned char		sensor_major;
> +	unsigned char		sensor_minor;
> +
> +	unsigned char		num_sensors;
> +	struct ibmpex_sensor_data	*sensors;
> +};
> +
> +struct ibmpex_driver_data {
> +	struct list_head	bmc_data;
> +	struct ipmi_smi_watcher	bmc_events;
> +	struct ipmi_user_hndl	ipmi_hndlrs;
> +};
> +
> +static struct ibmpex_driver_data driver_data = {
> +	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
> +	.bmc_events = {
> +		.owner = THIS_MODULE,
> +		.new_smi = ibmpex_register_bmc,
> +		.smi_gone = ibmpex_bmc_gone,
> +	},
> +	.ipmi_hndlrs = {
> +		.ipmi_recv_hndl = ibmpex_msg_handler,
> +	},
> +};
> +
> +static int ibmpex_send_message(struct ibmpex_bmc_data *data)
> +{
> +	int err;
> +
> +	err = ipmi_validate_addr(&data->address, sizeof(data->address));
> +	if (err)
> +		goto out;
> +
> +	data->tx_msgid++;
> +	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
> +				   &data->tx_message, data, 0, 0, 0);
> +	if (err)
> +		goto out1;
> +
> +	return 0;
> +out1:
> +	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
> +	return err;
> +out:
> +	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
> +	return err;
> +}
> +
> +static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_VERSION;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 6)
> +		return -ENOENT;
> +
> +	data->sensor_major = data->rx_msg_data[0];
> +	data->sensor_minor = data->rx_msg_data[1];
> +
> +	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
> +	       "v%d.%d %d-%02d-%02d on interface %d\n",
> +	       data->sensor_major,
> +	       data->sensor_minor,
> +	       extract_value(data->rx_msg_data, 2),
> +	       data->rx_msg_data[4],
> +	       data->rx_msg_data[5],
> +	       data->interface);
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 1)
> +		return -ENOENT;
> +
> +	return data->rx_msg_data[0];
> +}
> +
> +static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 1)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 26) {
> +		printk(KERN_ERR "Error reading sensor %d, please check.\n",
> +		       sensor);
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ibmpex_update_device(struct ibmpex_bmc_data *data)
> +{
> +	int i, err;
> +
> +	mutex_lock(&data->lock);
> +	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
> +	    data->valid)
> +		goto out;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		if (!data->sensors[i].in_use)
> +			continue;
> +		err = ibmpex_query_sensor_data(data, i);
> +		if (err)
> +			continue;
> +		data->sensors[i].values[0] =
> +			extract_value(data->rx_msg_data, 16);
> +		data->sensors[i].values[1] =
> +			extract_value(data->rx_msg_data, 18);
> +		data->sensors[i].values[2] =
> +			extract_value(data->rx_msg_data, 20);
> +	}
> +
> +	data->last_updated = jiffies;
> +	data->valid = 1;
> +
> +out:
> +	mutex_unlock(&data->lock);
> +}
> +
> +static struct ibmpex_bmc_data *get_bmc_data(int iface)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		if (p->interface == iface)
> +			return p;
> +
> +	return NULL;
> +}
> +

That function is yucky...

> +static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	return sprintf(buf, "%s\n", DRVNAME);
> +}
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
> +
> +static ssize_t ibmpex_show_sensor(struct device *dev,
> +				  struct device_attribute *devattr,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> +	int iface = PEX_INTERFACE(attr->index);
> +	int sensor = PEX_SENSOR(attr->index);
> +	int func = PEX_FUNC(attr->index);
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);

... especially given how many times you're going to call it.  Is there any
reason you can't use the driver_data field of struct device *dev for that?

E.g. i2c based hwmon drivers do this at some point during the probe:

	i2c_set_clientdata(new_client, data);

(which becomes)

	dev_set_drvdata(&new_client->dev, data);

If you could do that, then you no longer need 'iface' at all in the function
above... *that* may allow you to use the SENSOR_ATTR_2 mechanism from
hwmon-sysfs.h - much easier to read than the manual number packing for 'sensor'
and 'func'.

> +	int multiplier = data->sensors[sensor].multiplier;
> +	ibmpex_update_device(data);
> +
> +	return sprintf(buf, "%d\n",
> +		       data->sensors[sensor].values[func] * multiplier);
> +}
> +
> +static int is_power_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int is_temp_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int power_sensor_multiplier(const char *sensor_id, int len)
> +{
> +	int i;
> +
> +	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
> +		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
> +			return 1000;
> +
> +	return 100;
> +}
> +
> +static int create_sensor(struct ibmpex_bmc_data *data, const char *type,
> +			 int counter, int sensor, int func)
> +{
> +	int err;
> +	char *n;
> +
> +	n = kmalloc(32, GFP_KERNEL);
> +	if (!n)
> +		return -ENOMEM;
> +
> +	sprintf(n, sensor_name_templates[func], type, counter);
> +	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
> +	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
> +	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
> +	data->sensors[sensor].attr[func].index =
> +		PEX_INDEX(data->interface, sensor, func);
> +
> +	err = device_create_file(data->bmc_device,
> +				 &data->sensors[sensor].attr[func].dev_attr);
> +	if (err) {
> +		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
> +		kfree(n);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
> +{
> +	int i, j, err;
> +	char *sensor_type;
> +	int sensor_counter;
> +	int num_power = 0;
> +	int num_temp = 0;
> +
> +	err = ibmpex_query_sensor_count(data);
> +	if (err < 0)
> +		return -ENOENT;
> +	data->num_sensors = err;
> +

Did you mean 'if (err <= 0)' ?

> +	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
> +				GFP_KERNEL);
> +	if (!data->sensors)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		err = ibmpex_query_sensor_name(data, i);
> +		if (err)
> +			continue;
> +
> +		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
> +			sensor_type = "power";
> +			num_power++;
> +			sensor_counter = num_power;
> +			data->sensors[i].multiplier =
> +				power_sensor_multiplier(data->rx_msg_data,
> +						     data->rx_msg_len);
> +		} else if (is_temp_sensor(data->rx_msg_data,
> +					  data->rx_msg_len)) {
> +			sensor_type = "temp";
> +			num_temp++;
> +			sensor_counter = num_temp;
> +			data->sensors[i].multiplier = 1;
> +		} else
> +			continue;
> +
> +		data->sensors[i].in_use = 1;
> +
> +		/* Create attributes */
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
> +			if (create_sensor(data, sensor_type, sensor_counter,
> +					  i, j))

Why not 'err = create_sensor(...)' and propagate the actual error here?

> +				goto exit_remove;
> +	}
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_name.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	return 0;
> +
> +exit_remove:
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	kfree(data->sensors);
> +	return -ENOENT;

... and 'return err' here instead.

> +}
> +
> +static void ibmpex_register_bmc(int iface, struct device *dev)
> +{
> +	struct ibmpex_bmc_data *data;
> +	int err;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
> +		       "interface %d.\n", data->interface);
> +		return;
> +	}
> +
> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
> +	data->address.channel = IPMI_BMC_CHANNEL;
> +	data->address.data[0] = 0;
> +	data->interface = iface;
> +	data->bmc_device = dev;
> +
> +	/* Create IPMI messaging interface user */
> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
> +			       data, &data->user);
> +	if (err < 0) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
> +		       "ipmi interface %d\n",
> +		       data->interface);
> +		goto out;
> +	}
> +
> +	mutex_init(&data->lock);
> +
> +	/* Initialize message */
> +	data->tx_msgid = 0;
> +	init_completion(&data->read_complete);
> +	data->tx_message.netfn = PEX_NET_FUNCTION;
> +	data->tx_message.cmd = PEX_COMMAND;
> +	data->tx_message.data = data->tx_msg_data;
> +
> +	/* Does this BMC support PowerExecutive? */
> +	err = ibmpex_ver_check(data);
> +	if (err)
> +		goto out_user;
> +
> +	/* Register the BMC as a HWMON class device */
> +	data->class_dev = hwmon_device_register(data->bmc_device);
> +
> +	if (IS_ERR(data->class_dev)) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> +		       "class device for interface %d\n",
> +		       data->interface);
> +		kfree(data);
> +		return;
> +	}
> +
> +	/* finally add the new bmc data to the bmc data list */
> +	list_add_tail(&data->list, &driver_data.bmc_data);
> +
> +	/* Now go find all the sensors */
> +	err = ibmpex_find_sensors(data);
> +	if (err) {
> +		printk(KERN_ERR "Error %d allocating memory\n", err);
> +		goto out_register;
> +	}
> +	
> +	return;
> +
> +out_register:
> +	hwmon_device_unregister(data->class_dev);
> +out_user:
> +	ipmi_destroy_user(data->user);
> +out:
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
> +{
> +	int i, j;
> +
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	list_del(&data->list);
> +	hwmon_device_unregister(data->class_dev);
> +	ipmi_destroy_user(data->user);
> +	if (data->sensors)
> +		kfree(data->sensors);
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_gone(int iface)
> +{
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> +
> +	if (!data)
> +		return;
> +
> +	ibmpex_bmc_delete(data);
> +}
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> +{
> +	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
> +
> +	if (msg->msgid != data->tx_msgid) {
> +		printk(KERN_ERR "Received msgid (%02x) and transmitted "
> +		       "msgid (%02x) mismatch!\n",
> +		       (int)msg->msgid,
> +		       (int)data->tx_msgid);
> +		ipmi_free_recv_msg(msg);
> +		return;
> +	}
> +
> +	data->rx_recv_type = msg->recv_type;
> +	if (msg->msg.data_len > 0)
> +		data->rx_result = msg->msg.data[0];
> +	else
> +		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
> +
> +	if (msg->msg.data_len > 1) {
> +		data->rx_msg_len = msg->msg.data_len - 1;
> +		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
> +	} else
> +		data->rx_msg_len = 0;
> +
> +	ipmi_free_recv_msg(msg);
> +	complete(&data->read_complete);
> +}
> +
> +static int __init ibmpex_init(void)
> +{
> +	return ipmi_smi_watcher_register(&driver_data.bmc_events);
> +}
> +
> +static void __exit ibmpex_exit(void)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		ibmpex_bmc_delete(p);
> +}
> +
> +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
> +MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(ibmpex_init);
> +module_exit(ibmpex_exit);

Regards,

-- 
Mark M. Hoffman
mhoffman@lightlink.com


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v3 of IBM power meter driver
@ 2007-09-11 13:23           ` Mark M. Hoffman
  0 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-09-11 13:23 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick:

* Darrick J. Wong <djwong@us.ibm.com> [2007-08-28 16:25:05 -0700]:
> Dave Hansen complained about the magic numbers, repetitive code, and
> various other minor problems with the driver code, so here's a v3 with
> the magic numbers migrated to the top of the file and #define'd,
> helper macros taking place of the bit shifting/masking activities, and
> the compression of the value/min/max sysfs code into parameterized
> functions.
> --
> ibm_pex: Driver to export IBM PowerExecutive power meter sensors.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>

I am not an IPMI expert, so I would appreciate getting an Acked-by from
someone who knows more about that subsystem.

Anyway, some comments are below.  This is nowhere near a complete review yet.

> ---
> 
>  drivers/hwmon/Kconfig  |   12 +
>  drivers/hwmon/Makefile |    1 
>  drivers/hwmon/ibmpex.c |  564 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 577 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 555f470..41ffa2e 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -275,6 +275,18 @@ config SENSORS_CORETEMP
>  	  sensor inside your CPU. Supported all are all known variants
>  	  of Intel Core family.
>  
> +config SENSORS_IBMPEX
> +	tristate "IBM PowerExecutive temperature/power sensors"
> +	depends on IPMI_SI

Open question: can we use "select" here?  As written, it took some hunting to
even get this driver to show up as an option in menuconfig.

> +	help
> +	  If you say yes here you get support for the temperature and
> +	  power sensors in various IBM System X servers that support
> +	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
> +	  x3755, and certain HS20 blades.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ibmpex.
> +
>  config SENSORS_IT87
>  	tristate "ITE IT87xx and compatibles"
>  	select HWMON_VID
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index a133981..31da6fe 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
>  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
>  obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
>  obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
> +obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
>  obj-$(CONFIG_SENSORS_IT87)	+= it87.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> new file mode 100644
> index 0000000..632f897
> --- /dev/null
> +++ b/drivers/hwmon/ibmpex.c
> @@ -0,0 +1,564 @@
> +/*
> + * A hwmon driver for the IBM PowerExecutive temperature/power sensors
> + * Copyright (C) 2007 IBM
> + *
> + * Author: Darrick J. Wong <djwong@us.ibm.com>
> + *
> + * 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 Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/ipmi.h>
> +#include <linux/module.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>

#include <linux/jiffies.h>

> +#include <linux/mutex.h>
> +
> +#define REFRESH_INTERVAL	(5 * HZ)
> +#define DRVNAME			"ibmpex"
> +
> +#define PEX_GET_VERSION		1
> +#define PEX_GET_SENSOR_COUNT	2
> +#define PEX_GET_SENSOR_NAME	3
> +#define PEX_GET_SENSOR_DATA	6
> +
> +#define PEX_NET_FUNCTION	0x3A
> +#define PEX_COMMAND		0x3C
> +
> +static inline u16 extract_value(const char *data, int offset)
> +{
> +	u16 val = *(u16*)&data[offset];
> +	return be16_to_cpu(val);

	return be16_to_cpup((u16*)&data[offset]);

> +}
> +
> +#define PEX_INTERFACE(idx)		((idx) >> 16)
> +#define PEX_SENSOR(idx)			(((idx) >> 8) & 0xFF)
> +#define PEX_FUNC(idx)			((idx) & 0xFF)
> +#define PEX_INDEX(iface, num, fn)	(((iface) << 16) | ((num) << 8) | (fn))
> +
> +#define PEX_SENSOR_TYPE_LEN	3
> +static char power_sensor_sig[] = {0x70, 0x77, 0x72};
> +static char temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
> +
> +#define PEX_MULT_LEN		2
> +static char watt_sensor_sig[]  = {0x41, 0x43};
> +

All three above should be "static u8 const"

> +#define PEX_NUM_SENSOR_FUNCS	3
> +static char *sensor_name_templates[] = {
> +	"%s%d_input",
> +	"%s%d_min_input",
> +	"%s%d_max_input"
> +};
> +

static char const * const sensor_name_templates[] = {

> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
> +static void ibmpex_register_bmc(int iface, struct device *dev);
> +static void ibmpex_bmc_gone(int iface);
> +
> +struct ibmpex_sensor_data {
> +	int			in_use;
> +	s16			values[PEX_NUM_SENSOR_FUNCS];
> +	int			multiplier;
> +
> +	struct sensor_device_attribute	attr[PEX_NUM_SENSOR_FUNCS];
> +};
> +
> +struct ibmpex_bmc_data {
> +	struct list_head	list;
> +	struct class_device	*class_dev;

My current stack of patches includes one which requires that this be changed
to 'struct device *hwmon_dev', as 'struct class_device' is going away soon.
You may rebase on my testing tree[1], or else I will just follow up with a
patch to fix this up after I eventually merge yours.

[1] http://lm-sensors.org/kernel?p=kernel/mhoffman/hwmon-2.6.git;a=shortlog;h=testing

> +	struct device		*bmc_device;
> +	struct mutex		lock;
> +	char			valid;
> +	unsigned long		last_updated;	/* In jiffies */
> +
> +	struct ipmi_addr	address;
> +	struct completion	read_complete;
> +	ipmi_user_t		user;
> +	int			interface;
> +
> +	struct kernel_ipmi_msg	tx_message;
> +	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	long			tx_msgid;
> +
> +	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	unsigned long		rx_msg_len;
> +	unsigned char		rx_result;
> +	int			rx_recv_type;
> +
> +	unsigned char		sensor_major;
> +	unsigned char		sensor_minor;
> +
> +	unsigned char		num_sensors;
> +	struct ibmpex_sensor_data	*sensors;
> +};
> +
> +struct ibmpex_driver_data {
> +	struct list_head	bmc_data;
> +	struct ipmi_smi_watcher	bmc_events;
> +	struct ipmi_user_hndl	ipmi_hndlrs;
> +};
> +
> +static struct ibmpex_driver_data driver_data = {
> +	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
> +	.bmc_events = {
> +		.owner = THIS_MODULE,
> +		.new_smi = ibmpex_register_bmc,
> +		.smi_gone = ibmpex_bmc_gone,
> +	},
> +	.ipmi_hndlrs = {
> +		.ipmi_recv_hndl = ibmpex_msg_handler,
> +	},
> +};
> +
> +static int ibmpex_send_message(struct ibmpex_bmc_data *data)
> +{
> +	int err;
> +
> +	err = ipmi_validate_addr(&data->address, sizeof(data->address));
> +	if (err)
> +		goto out;
> +
> +	data->tx_msgid++;
> +	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
> +				   &data->tx_message, data, 0, 0, 0);
> +	if (err)
> +		goto out1;
> +
> +	return 0;
> +out1:
> +	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
> +	return err;
> +out:
> +	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
> +	return err;
> +}
> +
> +static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_VERSION;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 6)
> +		return -ENOENT;
> +
> +	data->sensor_major = data->rx_msg_data[0];
> +	data->sensor_minor = data->rx_msg_data[1];
> +
> +	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
> +	       "v%d.%d %d-%02d-%02d on interface %d\n",
> +	       data->sensor_major,
> +	       data->sensor_minor,
> +	       extract_value(data->rx_msg_data, 2),
> +	       data->rx_msg_data[4],
> +	       data->rx_msg_data[5],
> +	       data->interface);
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 1)
> +		return -ENOENT;
> +
> +	return data->rx_msg_data[0];
> +}
> +
> +static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 1)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 26) {
> +		printk(KERN_ERR "Error reading sensor %d, please check.\n",
> +		       sensor);
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ibmpex_update_device(struct ibmpex_bmc_data *data)
> +{
> +	int i, err;
> +
> +	mutex_lock(&data->lock);
> +	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
> +	    data->valid)
> +		goto out;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		if (!data->sensors[i].in_use)
> +			continue;
> +		err = ibmpex_query_sensor_data(data, i);
> +		if (err)
> +			continue;
> +		data->sensors[i].values[0] > +			extract_value(data->rx_msg_data, 16);
> +		data->sensors[i].values[1] > +			extract_value(data->rx_msg_data, 18);
> +		data->sensors[i].values[2] > +			extract_value(data->rx_msg_data, 20);
> +	}
> +
> +	data->last_updated = jiffies;
> +	data->valid = 1;
> +
> +out:
> +	mutex_unlock(&data->lock);
> +}
> +
> +static struct ibmpex_bmc_data *get_bmc_data(int iface)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		if (p->interface = iface)
> +			return p;
> +
> +	return NULL;
> +}
> +

That function is yucky...

> +static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	return sprintf(buf, "%s\n", DRVNAME);
> +}
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
> +
> +static ssize_t ibmpex_show_sensor(struct device *dev,
> +				  struct device_attribute *devattr,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> +	int iface = PEX_INTERFACE(attr->index);
> +	int sensor = PEX_SENSOR(attr->index);
> +	int func = PEX_FUNC(attr->index);
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);

... especially given how many times you're going to call it.  Is there any
reason you can't use the driver_data field of struct device *dev for that?

E.g. i2c based hwmon drivers do this at some point during the probe:

	i2c_set_clientdata(new_client, data);

(which becomes)

	dev_set_drvdata(&new_client->dev, data);

If you could do that, then you no longer need 'iface' at all in the function
above... *that* may allow you to use the SENSOR_ATTR_2 mechanism from
hwmon-sysfs.h - much easier to read than the manual number packing for 'sensor'
and 'func'.

> +	int multiplier = data->sensors[sensor].multiplier;
> +	ibmpex_update_device(data);
> +
> +	return sprintf(buf, "%d\n",
> +		       data->sensors[sensor].values[func] * multiplier);
> +}
> +
> +static int is_power_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int is_temp_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int power_sensor_multiplier(const char *sensor_id, int len)
> +{
> +	int i;
> +
> +	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
> +		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
> +			return 1000;
> +
> +	return 100;
> +}
> +
> +static int create_sensor(struct ibmpex_bmc_data *data, const char *type,
> +			 int counter, int sensor, int func)
> +{
> +	int err;
> +	char *n;
> +
> +	n = kmalloc(32, GFP_KERNEL);
> +	if (!n)
> +		return -ENOMEM;
> +
> +	sprintf(n, sensor_name_templates[func], type, counter);
> +	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
> +	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
> +	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
> +	data->sensors[sensor].attr[func].index > +		PEX_INDEX(data->interface, sensor, func);
> +
> +	err = device_create_file(data->bmc_device,
> +				 &data->sensors[sensor].attr[func].dev_attr);
> +	if (err) {
> +		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
> +		kfree(n);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
> +{
> +	int i, j, err;
> +	char *sensor_type;
> +	int sensor_counter;
> +	int num_power = 0;
> +	int num_temp = 0;
> +
> +	err = ibmpex_query_sensor_count(data);
> +	if (err < 0)
> +		return -ENOENT;
> +	data->num_sensors = err;
> +

Did you mean 'if (err <= 0)' ?

> +	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
> +				GFP_KERNEL);
> +	if (!data->sensors)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		err = ibmpex_query_sensor_name(data, i);
> +		if (err)
> +			continue;
> +
> +		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
> +			sensor_type = "power";
> +			num_power++;
> +			sensor_counter = num_power;
> +			data->sensors[i].multiplier > +				power_sensor_multiplier(data->rx_msg_data,
> +						     data->rx_msg_len);
> +		} else if (is_temp_sensor(data->rx_msg_data,
> +					  data->rx_msg_len)) {
> +			sensor_type = "temp";
> +			num_temp++;
> +			sensor_counter = num_temp;
> +			data->sensors[i].multiplier = 1;
> +		} else
> +			continue;
> +
> +		data->sensors[i].in_use = 1;
> +
> +		/* Create attributes */
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
> +			if (create_sensor(data, sensor_type, sensor_counter,
> +					  i, j))

Why not 'err = create_sensor(...)' and propagate the actual error here?

> +				goto exit_remove;
> +	}
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_name.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	return 0;
> +
> +exit_remove:
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	kfree(data->sensors);
> +	return -ENOENT;

... and 'return err' here instead.

> +}
> +
> +static void ibmpex_register_bmc(int iface, struct device *dev)
> +{
> +	struct ibmpex_bmc_data *data;
> +	int err;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
> +		       "interface %d.\n", data->interface);
> +		return;
> +	}
> +
> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
> +	data->address.channel = IPMI_BMC_CHANNEL;
> +	data->address.data[0] = 0;
> +	data->interface = iface;
> +	data->bmc_device = dev;
> +
> +	/* Create IPMI messaging interface user */
> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
> +			       data, &data->user);
> +	if (err < 0) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
> +		       "ipmi interface %d\n",
> +		       data->interface);
> +		goto out;
> +	}
> +
> +	mutex_init(&data->lock);
> +
> +	/* Initialize message */
> +	data->tx_msgid = 0;
> +	init_completion(&data->read_complete);
> +	data->tx_message.netfn = PEX_NET_FUNCTION;
> +	data->tx_message.cmd = PEX_COMMAND;
> +	data->tx_message.data = data->tx_msg_data;
> +
> +	/* Does this BMC support PowerExecutive? */
> +	err = ibmpex_ver_check(data);
> +	if (err)
> +		goto out_user;
> +
> +	/* Register the BMC as a HWMON class device */
> +	data->class_dev = hwmon_device_register(data->bmc_device);
> +
> +	if (IS_ERR(data->class_dev)) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> +		       "class device for interface %d\n",
> +		       data->interface);
> +		kfree(data);
> +		return;
> +	}
> +
> +	/* finally add the new bmc data to the bmc data list */
> +	list_add_tail(&data->list, &driver_data.bmc_data);
> +
> +	/* Now go find all the sensors */
> +	err = ibmpex_find_sensors(data);
> +	if (err) {
> +		printk(KERN_ERR "Error %d allocating memory\n", err);
> +		goto out_register;
> +	}
> +	
> +	return;
> +
> +out_register:
> +	hwmon_device_unregister(data->class_dev);
> +out_user:
> +	ipmi_destroy_user(data->user);
> +out:
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
> +{
> +	int i, j;
> +
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	list_del(&data->list);
> +	hwmon_device_unregister(data->class_dev);
> +	ipmi_destroy_user(data->user);
> +	if (data->sensors)
> +		kfree(data->sensors);
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_gone(int iface)
> +{
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> +
> +	if (!data)
> +		return;
> +
> +	ibmpex_bmc_delete(data);
> +}
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> +{
> +	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
> +
> +	if (msg->msgid != data->tx_msgid) {
> +		printk(KERN_ERR "Received msgid (%02x) and transmitted "
> +		       "msgid (%02x) mismatch!\n",
> +		       (int)msg->msgid,
> +		       (int)data->tx_msgid);
> +		ipmi_free_recv_msg(msg);
> +		return;
> +	}
> +
> +	data->rx_recv_type = msg->recv_type;
> +	if (msg->msg.data_len > 0)
> +		data->rx_result = msg->msg.data[0];
> +	else
> +		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
> +
> +	if (msg->msg.data_len > 1) {
> +		data->rx_msg_len = msg->msg.data_len - 1;
> +		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
> +	} else
> +		data->rx_msg_len = 0;
> +
> +	ipmi_free_recv_msg(msg);
> +	complete(&data->read_complete);
> +}
> +
> +static int __init ibmpex_init(void)
> +{
> +	return ipmi_smi_watcher_register(&driver_data.bmc_events);
> +}
> +
> +static void __exit ibmpex_exit(void)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		ibmpex_bmc_delete(p);
> +}
> +
> +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
> +MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(ibmpex_init);
> +module_exit(ibmpex_exit);

Regards,

-- 
Mark M. Hoffman
mhoffman@lightlink.com


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] v3 of IBM power meter driver
  2007-09-11 13:23           ` [lm-sensors] " Mark M. Hoffman
@ 2007-09-11 13:59             ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-11 13:59 UTC (permalink / raw)
  To: mhoffman, djwong
  Cc: Henrique de Moraes Holschuh, linux-kernel, lm-sensors, haveblue


On 9/11/2007, "Mark M. Hoffman" <mhoffman@lightlink.com> wrote:
>* Darrick J. Wong <djwong@us.ibm.com> [2007-08-28 16:25:05 -0700]:
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -275,6 +275,18 @@ config SENSORS_CORETEMP
>>  	  sensor inside your CPU. Supported all are all known variants
>>  	  of Intel Core family.
>>
>> +config SENSORS_IBMPEX
>> +	tristate "IBM PowerExecutive temperature/power sensors"
>> +	depends on IPMI_SI
>
> Open question: can we use "select" here?  As written, it took some hunting
> to even get this driver to show up as an option in menuconfig.

My 2 cents: I assume that people who have IPMI-capable hardware are aware
that they do. Thus, if you change the above to "select IPMI_SI", you
probably want to add "depends on IPMI_HANDLER" (not sure why it's not
just named CONFIG_IPMI).

--
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v3 of IBM power meter driver
@ 2007-09-11 13:59             ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-11 13:59 UTC (permalink / raw)
  To: mhoffman, djwong
  Cc: Henrique de Moraes Holschuh, linux-kernel, lm-sensors, haveblue


On 9/11/2007, "Mark M. Hoffman" <mhoffman@lightlink.com> wrote:
>* Darrick J. Wong <djwong@us.ibm.com> [2007-08-28 16:25:05 -0700]:
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -275,6 +275,18 @@ config SENSORS_CORETEMP
>>  	  sensor inside your CPU. Supported all are all known variants
>>  	  of Intel Core family.
>>
>> +config SENSORS_IBMPEX
>> +	tristate "IBM PowerExecutive temperature/power sensors"
>> +	depends on IPMI_SI
>
> Open question: can we use "select" here?  As written, it took some hunting
> to even get this driver to show up as an option in menuconfig.

My 2 cents: I assume that people who have IPMI-capable hardware are aware
that they do. Thus, if you change the above to "select IPMI_SI", you
probably want to add "depends on IPMI_HANDLER" (not sure why it's not
just named CONFIG_IPMI).

--
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface
  2007-08-30  9:57             ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
@ 2007-09-11 16:43               ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-11 16:43 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors

[-- Attachment #1: Type: text/plain, Size: 662 bytes --]

On Thu, Aug 30, 2007 at 11:57:43AM +0200, Jean Delvare wrote:
> OK, that's a bit different then, but I'm still not sure that there is
> much value in exporting these values in sysfs, in particular if there
> is no way to reset them.

It turns out there _is_ a way to reset them; the next iteration of the
driver will have it.

> I am also not happy with the names you proposed: power1_max_input and
> power1_min_input are somewhat confusing IMHO, I'd suggest
> power1_input_highest and power1_input_lowest to make them clearly
> different from the min and max limits we have for other sensor types.
> If we have them at all, of course.

Agreed and changed.

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon: Add power meters to
@ 2007-09-11 16:43               ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-11 16:43 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Henrique de Moraes Holschuh, Mark M. Hoffman, linux-kernel, lm-sensors


[-- Attachment #1.1: Type: text/plain, Size: 662 bytes --]

On Thu, Aug 30, 2007 at 11:57:43AM +0200, Jean Delvare wrote:
> OK, that's a bit different then, but I'm still not sure that there is
> much value in exporting these values in sysfs, in particular if there
> is no way to reset them.

It turns out there _is_ a way to reset them; the next iteration of the
driver will have it.

> I am also not happy with the names you proposed: power1_max_input and
> power1_min_input are somewhat confusing IMHO, I'd suggest
> power1_input_highest and power1_input_lowest to make them clearly
> different from the min and max limits we have for other sensor types.
> If we have them at all, of course.

Agreed and changed.

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH] v3 of IBM power meter driver
  2007-09-11 13:23           ` [lm-sensors] " Mark M. Hoffman
@ 2007-09-12  1:11             ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-12  1:11 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 3296 bytes --]

On Tue, Sep 11, 2007 at 09:23:35AM -0400, Mark M. Hoffman wrote:

> I am not an IPMI expert, so I would appreciate getting an Acked-by from
> someone who knows more about that subsystem.
> 
> Anyway, some comments are below.  This is nowhere near a complete review yet.

Thank you for the review!  Comments interspersed below, though for
brevity the one-liners have been fixed.

> > +config SENSORS_IBMPEX
> > +	tristate "IBM PowerExecutive temperature/power sensors"
> > +	depends on IPMI_SI
> 
> Open question: can we use "select" here?  As written, it took some hunting to
> even get this driver to show up as an option in menuconfig.

Changed, since it seems reasonable that someone looking for PEx support
might not necessarily know that it is based upon IPMI.

> > +struct ibmpex_bmc_data {
> > +	struct list_head	list;
> > +	struct class_device	*class_dev;
> 
> My current stack of patches includes one which requires that this be changed
> to 'struct device *hwmon_dev', as 'struct class_device' is going away soon.
> You may rebase on my testing tree[1], or else I will just follow up with a
> patch to fix this up after I eventually merge yours.
> 
> [1] http://lm-sensors.org/kernel?p=kernel/mhoffman/hwmon-2.6.git;a=shortlog;h=testing

Done.

> > +static ssize_t ibmpex_show_sensor(struct device *dev,
> > +				  struct device_attribute *devattr,
> > +				  char *buf)
> > +{
> > +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> > +	int iface = PEX_INTERFACE(attr->index);
> > +	int sensor = PEX_SENSOR(attr->index);
> > +	int func = PEX_FUNC(attr->index);
> > +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> 
> ... especially given how many times you're going to call it.  Is there any
> reason you can't use the driver_data field of struct device *dev for that?

I can (and did) update the code to use dev_get/set_drvdata for the
accessors.  However, the "iface" field exists as a mechanism to map
interface numbers to struct ibmpex_bmc_data/struct device data because
the callback that IPMI uses to notify clients that BMCs are going away
only passes the interface number, not the struct device itself.
Unfortunately, this means that get_bmc_data() must remain, but now it is
only used once at the end of life.

> E.g. i2c based hwmon drivers do this at some point during the probe:
> 
> 	i2c_set_clientdata(new_client, data);
> 
> (which becomes)
> 
> 	dev_set_drvdata(&new_client->dev, data);
> 
> If you could do that, then you no longer need 'iface' at all in the function
> above... *that* may allow you to use the SENSOR_ATTR_2 mechanism from
> hwmon-sysfs.h - much easier to read than the manual number packing for 'sensor'
> and 'func'.

Doesn't look too hard; I'll have a go at it and see how it does.

> > +	err = ibmpex_query_sensor_count(data);
> > +	if (err < 0)
> > +		return -ENOENT;
> > +	data->num_sensors = err;
> > +
> 
> Did you mean 'if (err <= 0)' ?

Yes.

> > +		/* Create attributes */
> > +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
> > +			if (create_sensor(data, sensor_type, sensor_counter,
> > +					  i, j))
> 
> Why not 'err = create_sensor(...)' and propagate the actual error here?

Rough draft syndrome?  'tis fixed. :)

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] v3 of IBM power meter driver
@ 2007-09-12  1:11             ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-12  1:11 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 3296 bytes --]

On Tue, Sep 11, 2007 at 09:23:35AM -0400, Mark M. Hoffman wrote:

> I am not an IPMI expert, so I would appreciate getting an Acked-by from
> someone who knows more about that subsystem.
> 
> Anyway, some comments are below.  This is nowhere near a complete review yet.

Thank you for the review!  Comments interspersed below, though for
brevity the one-liners have been fixed.

> > +config SENSORS_IBMPEX
> > +	tristate "IBM PowerExecutive temperature/power sensors"
> > +	depends on IPMI_SI
> 
> Open question: can we use "select" here?  As written, it took some hunting to
> even get this driver to show up as an option in menuconfig.

Changed, since it seems reasonable that someone looking for PEx support
might not necessarily know that it is based upon IPMI.

> > +struct ibmpex_bmc_data {
> > +	struct list_head	list;
> > +	struct class_device	*class_dev;
> 
> My current stack of patches includes one which requires that this be changed
> to 'struct device *hwmon_dev', as 'struct class_device' is going away soon.
> You may rebase on my testing tree[1], or else I will just follow up with a
> patch to fix this up after I eventually merge yours.
> 
> [1] http://lm-sensors.org/kernel?p=kernel/mhoffman/hwmon-2.6.git;a=shortlog;h=testing

Done.

> > +static ssize_t ibmpex_show_sensor(struct device *dev,
> > +				  struct device_attribute *devattr,
> > +				  char *buf)
> > +{
> > +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> > +	int iface = PEX_INTERFACE(attr->index);
> > +	int sensor = PEX_SENSOR(attr->index);
> > +	int func = PEX_FUNC(attr->index);
> > +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> 
> ... especially given how many times you're going to call it.  Is there any
> reason you can't use the driver_data field of struct device *dev for that?

I can (and did) update the code to use dev_get/set_drvdata for the
accessors.  However, the "iface" field exists as a mechanism to map
interface numbers to struct ibmpex_bmc_data/struct device data because
the callback that IPMI uses to notify clients that BMCs are going away
only passes the interface number, not the struct device itself.
Unfortunately, this means that get_bmc_data() must remain, but now it is
only used once at the end of life.

> E.g. i2c based hwmon drivers do this at some point during the probe:
> 
> 	i2c_set_clientdata(new_client, data);
> 
> (which becomes)
> 
> 	dev_set_drvdata(&new_client->dev, data);
> 
> If you could do that, then you no longer need 'iface' at all in the function
> above... *that* may allow you to use the SENSOR_ATTR_2 mechanism from
> hwmon-sysfs.h - much easier to read than the manual number packing for 'sensor'
> and 'func'.

Doesn't look too hard; I'll have a go at it and see how it does.

> > +	err = ibmpex_query_sensor_count(data);
> > +	if (err < 0)
> > +		return -ENOENT;
> > +	data->num_sensors = err;
> > +
> 
> Did you mean 'if (err <= 0)' ?

Yes.

> > +		/* Create attributes */
> > +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++)
> > +			if (create_sensor(data, sensor_type, sensor_counter,
> > +					  i, j))
> 
> Why not 'err = create_sensor(...)' and propagate the actual error here?

Rough draft syndrome?  'tis fixed. :)

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface
  2007-09-11 13:23           ` [lm-sensors] " Mark M. Hoffman
@ 2007-09-14 19:29             ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-14 19:29 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 1250 bytes --]

Update the hwmon sysfs interface documentation to include a specification
for power meters.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 Documentation/hwmon/sysfs-interface |   30 ++++++++++++++++++++++++++++++
 1 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index db7bb4a..5c98bee 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -324,6 +324,36 @@ curr[1-*]_input	Current input value
 		Unit: milliampere
 		RO
 
+*********
+* Power *
+*********
+
+power[1-*]_average		Average power use
+				Unit: microWatt
+				RO
+
+power[1-*]_average_highest	Historical average maximum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_average_lowest	Historical average minimum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input		Instantaneous power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input_highest	Historical maximum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input_lowest		Historical minimum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_high_low_reset	Reset input_highest/input_lowest.
+				WO
 
 **********
 * Alarms *

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH v2] hwmon: Update
@ 2007-09-14 19:29             ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-14 19:29 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 1250 bytes --]

Update the hwmon sysfs interface documentation to include a specification
for power meters.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 Documentation/hwmon/sysfs-interface |   30 ++++++++++++++++++++++++++++++
 1 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index db7bb4a..5c98bee 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -324,6 +324,36 @@ curr[1-*]_input	Current input value
 		Unit: milliampere
 		RO
 
+*********
+* Power *
+*********
+
+power[1-*]_average		Average power use
+				Unit: microWatt
+				RO
+
+power[1-*]_average_highest	Historical average maximum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_average_lowest	Historical average minimum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input		Instantaneous power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input_highest	Historical maximum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_input_lowest		Historical minimum power use
+				Unit: microWatt
+				RO
+
+power[1-*]_high_low_reset	Reset input_highest/input_lowest.
+				WO
 
 **********
 * Alarms *

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4] IBM power meter driver
  2007-09-11 13:23           ` [lm-sensors] " Mark M. Hoffman
@ 2007-09-14 19:33             ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-14 19:33 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 18425 bytes --]

Here's a fourth revision where I've tried to clean up the things that
people complained about, as well as shifted the sysfs file names to
match the spec that we've been drifting towards.
---
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   13 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  608 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 622 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5ca5d95..591b666 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -315,6 +315,19 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	select IPMI_SI
+	depends on IPMI_HANDLER
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5070cf7..0fcbcb4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IPMI)	+= ipmisensors.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..fe2c261
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,608 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(2 * HZ)
+#define DRVNAME			"ibmpex"
+
+#define PEX_GET_VERSION		1
+#define PEX_GET_SENSOR_COUNT	2
+#define PEX_GET_SENSOR_NAME	3
+#define PEX_RESET_HIGH_LOW	4
+#define PEX_GET_SENSOR_DATA	6
+
+#define PEX_NET_FUNCTION	0x3A
+#define PEX_COMMAND		0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+	return be16_to_cpup((u16 *)&data[offset]);
+}
+
+#define TEMP_SENSOR		1
+#define POWER_SENSOR		2
+
+#define PEX_SENSOR_TYPE_LEN	3
+static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
+static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN		2
+static u8 const watt_sensor_sig[]  = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS	3
+static char const * const power_sensor_name_templates[] = {
+	"%s%d_average",
+	"%s%d_average_lowest",
+	"%s%d_average_highest"
+};
+static char const * const temp_sensor_name_templates[] = {
+	"%s%d_input",
+	"%s%d_input_lowest",
+	"%s%d_input_highest"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			values[PEX_NUM_SENSOR_FUNCS];
+	int			multiplier;
+
+	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct device		*hwmon_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_VERSION;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       extract_value(data->rx_msg_data, 2),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].values[0] =
+			extract_value(data->rx_msg_data, 16);
+		data->sensors[i].values[1] =
+			extract_value(data->rx_msg_data, 18);
+		data->sensors[i].values[2] =
+			extract_value(data->rx_msg_data, 20);
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+	int mult = data->sensors[attr->index].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n",
+		       data->sensors[attr->index].values[attr->nr] * mult);
+}
+
+static ssize_t ibmpex_reset_high_low(struct device *dev,
+				     struct device_attribute *devattr,
+				     const char *buf,
+				     size_t count)
+{
+	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+
+	ibmpex_reset_high_low_data(data);
+
+	return count;
+}
+
+static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
+			  ibmpex_reset_high_low, 0);
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
+		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
+			return 1000000;
+
+	return 100000;
+}
+
+static int create_sensor(struct ibmpex_bmc_data *data, int type,
+			 int counter, int sensor, int func)
+{
+	int err;
+	char *n;
+
+	n = kmalloc(32, GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	if (type == TEMP_SENSOR)
+		sprintf(n, temp_sensor_name_templates[func], "temp", counter);
+	else if (type == POWER_SENSOR)
+		sprintf(n, power_sensor_name_templates[func], "power", counter);
+
+	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
+	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
+	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
+	data->sensors[sensor].attr[func].index = sensor;
+	data->sensors[sensor].attr[func].nr = func;
+
+	err = device_create_file(data->bmc_device,
+				 &data->sensors[sensor].attr[func].dev_attr);
+	if (err) {
+		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
+		kfree(n);
+		return err;
+	}
+
+	return 0;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, j, err;
+	int sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err <= 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = POWER_SENSOR;
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = TEMP_SENSOR;
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create attributes */
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			err = create_sensor(data, sensor_type, sensor_counter,
+					    i, j);
+			if (err)
+				goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_reset_high_low.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	device_remove_file(data->bmc_device,
+			   &sensor_dev_attr_reset_high_low.dev_attr);
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	kfree(data->sensors);
+	return err;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = PEX_NET_FUNCTION;
+	data->tx_message.cmd = PEX_COMMAND;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->hwmon_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->hwmon_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	dev_set_drvdata(dev, data);
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+
+	return;
+
+out_register:
+	hwmon_device_unregister(data->hwmon_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i, j;
+
+	device_remove_file(data->bmc_device,
+			   &sensor_dev_attr_reset_high_low.dev_attr);
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	list_del(&data->list);
+	dev_set_drvdata(data->bmc_device, NULL);
+	hwmon_device_unregister(data->hwmon_dev);
+	ipmi_destroy_user(data->user);
+	kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH v4] IBM power meter driver
@ 2007-09-14 19:33             ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-14 19:33 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 18425 bytes --]

Here's a fourth revision where I've tried to clean up the things that
people complained about, as well as shifted the sysfs file names to
match the spec that we've been drifting towards.
---
ibm_pex: Driver to export IBM PowerExecutive power meter sensors.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/Kconfig  |   13 +
 drivers/hwmon/Makefile |    1 
 drivers/hwmon/ibmpex.c |  608 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 622 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5ca5d95..591b666 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -315,6 +315,19 @@ config SENSORS_CORETEMP
 	  sensor inside your CPU. Supported all are all known variants
 	  of Intel Core family.
 
+config SENSORS_IBMPEX
+	tristate "IBM PowerExecutive temperature/power sensors"
+	select IPMI_SI
+	depends on IPMI_HANDLER
+	help
+	  If you say yes here you get support for the temperature and
+	  power sensors in various IBM System X servers that support
+	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
+	  x3755, and certain HS20 blades.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ibmpex.
+
 config SENSORS_IT87
 	tristate "ITE IT87xx and compatibles"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5070cf7..0fcbcb4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
+obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
 obj-$(CONFIG_SENSORS_IPMI)	+= ipmisensors.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 0000000..fe2c261
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,608 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL	(2 * HZ)
+#define DRVNAME			"ibmpex"
+
+#define PEX_GET_VERSION		1
+#define PEX_GET_SENSOR_COUNT	2
+#define PEX_GET_SENSOR_NAME	3
+#define PEX_RESET_HIGH_LOW	4
+#define PEX_GET_SENSOR_DATA	6
+
+#define PEX_NET_FUNCTION	0x3A
+#define PEX_COMMAND		0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+	return be16_to_cpup((u16 *)&data[offset]);
+}
+
+#define TEMP_SENSOR		1
+#define POWER_SENSOR		2
+
+#define PEX_SENSOR_TYPE_LEN	3
+static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
+static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN		2
+static u8 const watt_sensor_sig[]  = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS	3
+static char const * const power_sensor_name_templates[] = {
+	"%s%d_average",
+	"%s%d_average_lowest",
+	"%s%d_average_highest"
+};
+static char const * const temp_sensor_name_templates[] = {
+	"%s%d_input",
+	"%s%d_input_lowest",
+	"%s%d_input_highest"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+	int			in_use;
+	s16			values[PEX_NUM_SENSOR_FUNCS];
+	int			multiplier;
+
+	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+	struct list_head	list;
+	struct device		*hwmon_dev;
+	struct device		*bmc_device;
+	struct mutex		lock;
+	char			valid;
+	unsigned long		last_updated;	/* In jiffies */
+
+	struct ipmi_addr	address;
+	struct completion	read_complete;
+	ipmi_user_t		user;
+	int			interface;
+
+	struct kernel_ipmi_msg	tx_message;
+	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
+	long			tx_msgid;
+
+	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
+	unsigned long		rx_msg_len;
+	unsigned char		rx_result;
+	int			rx_recv_type;
+
+	unsigned char		sensor_major;
+	unsigned char		sensor_minor;
+
+	unsigned char		num_sensors;
+	struct ibmpex_sensor_data	*sensors;
+};
+
+struct ibmpex_driver_data {
+	struct list_head	bmc_data;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ibmpex_register_bmc,
+		.smi_gone = ibmpex_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ibmpex_msg_handler,
+	},
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+	int err;
+
+	err = ipmi_validate_addr(&data->address, sizeof(data->address));
+	if (err)
+		goto out;
+
+	data->tx_msgid++;
+	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+				   &data->tx_message, data, 0, 0, 0);
+	if (err)
+		goto out1;
+
+	return 0;
+out1:
+	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+	return err;
+out:
+	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+	return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_VERSION;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 6)
+		return -ENOENT;
+
+	data->sensor_major = data->rx_msg_data[0];
+	data->sensor_minor = data->rx_msg_data[1];
+
+	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
+	       "v%d.%d %d-%02d-%02d on interface %d\n",
+	       data->sensor_major,
+	       data->sensor_minor,
+	       extract_value(data->rx_msg_data, 2),
+	       data->rx_msg_data[4],
+	       data->rx_msg_data[5],
+	       data->interface);
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 1)
+		return -ENOENT;
+
+	return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 1)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+	data->tx_msg_data[1] = sensor;
+	data->tx_message.data_len = 2;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len < 26) {
+		printk(KERN_ERR "Error reading sensor %d, please check.\n",
+		       sensor);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
+{
+	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
+	data->tx_message.data_len = 1;
+	ibmpex_send_message(data);
+
+	wait_for_completion(&data->read_complete);
+
+	return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+	int i, err;
+
+	mutex_lock(&data->lock);
+	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+	    data->valid)
+		goto out;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		if (!data->sensors[i].in_use)
+			continue;
+		err = ibmpex_query_sensor_data(data, i);
+		if (err)
+			continue;
+		data->sensors[i].values[0] =
+			extract_value(data->rx_msg_data, 16);
+		data->sensors[i].values[1] =
+			extract_value(data->rx_msg_data, 18);
+		data->sensors[i].values[2] =
+			extract_value(data->rx_msg_data, 20);
+	}
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+
+out:
+	mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		if (p->interface == iface)
+			return p;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+	int mult = data->sensors[attr->index].multiplier;
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%d\n",
+		       data->sensors[attr->index].values[attr->nr] * mult);
+}
+
+static ssize_t ibmpex_reset_high_low(struct device *dev,
+				     struct device_attribute *devattr,
+				     const char *buf,
+				     size_t count)
+{
+	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+
+	ibmpex_reset_high_low_data(data);
+
+	return count;
+}
+
+static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
+			  ibmpex_reset_high_low, 0);
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+	if (len < PEX_SENSOR_TYPE_LEN)
+		return 0;
+
+	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
+		return 1;
+	return 0;
+}
+
+static int power_sensor_multiplier(const char *sensor_id, int len)
+{
+	int i;
+
+	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
+		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
+			return 1000000;
+
+	return 100000;
+}
+
+static int create_sensor(struct ibmpex_bmc_data *data, int type,
+			 int counter, int sensor, int func)
+{
+	int err;
+	char *n;
+
+	n = kmalloc(32, GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	if (type == TEMP_SENSOR)
+		sprintf(n, temp_sensor_name_templates[func], "temp", counter);
+	else if (type == POWER_SENSOR)
+		sprintf(n, power_sensor_name_templates[func], "power", counter);
+
+	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
+	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
+	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
+	data->sensors[sensor].attr[func].index = sensor;
+	data->sensors[sensor].attr[func].nr = func;
+
+	err = device_create_file(data->bmc_device,
+				 &data->sensors[sensor].attr[func].dev_attr);
+	if (err) {
+		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
+		kfree(n);
+		return err;
+	}
+
+	return 0;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+	int i, j, err;
+	int sensor_type;
+	int sensor_counter;
+	int num_power = 0;
+	int num_temp = 0;
+
+	err = ibmpex_query_sensor_count(data);
+	if (err <= 0)
+		return -ENOENT;
+	data->num_sensors = err;
+
+	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
+				GFP_KERNEL);
+	if (!data->sensors)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_sensors; i++) {
+		err = ibmpex_query_sensor_name(data, i);
+		if (err)
+			continue;
+
+		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+			sensor_type = POWER_SENSOR;
+			num_power++;
+			sensor_counter = num_power;
+			data->sensors[i].multiplier =
+				power_sensor_multiplier(data->rx_msg_data,
+						     data->rx_msg_len);
+		} else if (is_temp_sensor(data->rx_msg_data,
+					  data->rx_msg_len)) {
+			sensor_type = TEMP_SENSOR;
+			num_temp++;
+			sensor_counter = num_temp;
+			data->sensors[i].multiplier = 1;
+		} else
+			continue;
+
+		data->sensors[i].in_use = 1;
+
+		/* Create attributes */
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			err = create_sensor(data, sensor_type, sensor_counter,
+					    i, j);
+			if (err)
+				goto exit_remove;
+		}
+	}
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_reset_high_low.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(data->bmc_device,
+			&sensor_dev_attr_name.dev_attr);
+	if (err)
+		goto exit_remove;
+
+	return 0;
+
+exit_remove:
+	device_remove_file(data->bmc_device,
+			   &sensor_dev_attr_reset_high_low.dev_attr);
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	kfree(data->sensors);
+	return err;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+	struct ibmpex_bmc_data *data;
+	int err;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
+		       "interface %d.\n", data->interface);
+		return;
+	}
+
+	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	data->address.channel = IPMI_BMC_CHANNEL;
+	data->address.data[0] = 0;
+	data->interface = iface;
+	data->bmc_device = dev;
+
+	/* Create IPMI messaging interface user */
+	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+			       data, &data->user);
+	if (err < 0) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
+		       "ipmi interface %d\n",
+		       data->interface);
+		goto out;
+	}
+
+	mutex_init(&data->lock);
+
+	/* Initialize message */
+	data->tx_msgid = 0;
+	init_completion(&data->read_complete);
+	data->tx_message.netfn = PEX_NET_FUNCTION;
+	data->tx_message.cmd = PEX_COMMAND;
+	data->tx_message.data = data->tx_msg_data;
+
+	/* Does this BMC support PowerExecutive? */
+	err = ibmpex_ver_check(data);
+	if (err)
+		goto out_user;
+
+	/* Register the BMC as a HWMON class device */
+	data->hwmon_dev = hwmon_device_register(data->bmc_device);
+
+	if (IS_ERR(data->hwmon_dev)) {
+		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
+		       "class device for interface %d\n",
+		       data->interface);
+		kfree(data);
+		return;
+	}
+
+	/* finally add the new bmc data to the bmc data list */
+	dev_set_drvdata(dev, data);
+	list_add_tail(&data->list, &driver_data.bmc_data);
+
+	/* Now go find all the sensors */
+	err = ibmpex_find_sensors(data);
+	if (err) {
+		printk(KERN_ERR "Error %d allocating memory\n", err);
+		goto out_register;
+	}
+
+	return;
+
+out_register:
+	hwmon_device_unregister(data->hwmon_dev);
+out_user:
+	ipmi_destroy_user(data->user);
+out:
+	kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+	int i, j;
+
+	device_remove_file(data->bmc_device,
+			   &sensor_dev_attr_reset_high_low.dev_attr);
+	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+	for (i = 0; i < data->num_sensors; i++)
+		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+			if (!data->sensors[i].attr[j].dev_attr.attr.name)
+				continue;
+			device_remove_file(data->bmc_device,
+				&data->sensors[i].attr[j].dev_attr);
+			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+		}
+
+	list_del(&data->list);
+	dev_set_drvdata(data->bmc_device, NULL);
+	hwmon_device_unregister(data->hwmon_dev);
+	ipmi_destroy_user(data->user);
+	kfree(data->sensors);
+	kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+	struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+	if (!data)
+		return;
+
+	ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+	if (msg->msgid != data->tx_msgid) {
+		printk(KERN_ERR "Received msgid (%02x) and transmitted "
+		       "msgid (%02x) mismatch!\n",
+		       (int)msg->msgid,
+		       (int)data->tx_msgid);
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+
+	data->rx_recv_type = msg->recv_type;
+	if (msg->msg.data_len > 0)
+		data->rx_result = msg->msg.data[0];
+	else
+		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+	if (msg->msg.data_len > 1) {
+		data->rx_msg_len = msg->msg.data_len - 1;
+		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+	} else
+		data->rx_msg_len = 0;
+
+	ipmi_free_recv_msg(msg);
+	complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+	return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+	struct ibmpex_bmc_data *p, *next;
+
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+		ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface
  2007-09-14 19:29             ` [lm-sensors] [PATCH v2] hwmon: Update Darrick J. Wong
@ 2007-09-17 17:28               ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-17 17:28 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick,

On Fri, 14 Sep 2007 12:29:24 -0700, Darrick J. Wong wrote:
> Update the hwmon sysfs interface documentation to include a specification
> for power meters.

Thanks for the update. I have some more comments. And sorry if it looks
like nitpicking, but this is a standard interface we're defining so we
better make sure that we get it right.

First of all, please think of a better subject line for this patch.
"Update Documentation/hwmon/sysfs-interface" is too vague when your
patch is rather specific.

> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  Documentation/hwmon/sysfs-interface |   30 ++++++++++++++++++++++++++++++
>  1 files changed, 30 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index db7bb4a..5c98bee 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -324,6 +324,36 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_average		Average power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_average_highest	Historical average maximum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_average_lowest	Historical average minimum power use
> +				Unit: microWatt
> +				RO

How useful are historical extremes of an average?

> +
> +power[1-*]_input		Instantaneous power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_input_highest	Historical maximum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_input_lowest		Historical minimum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> +				WO

I don't much like this name. It sounds like a name crafted for the
specific feature of a given chip, while we try to use generic names for
the standard interface. I would rather go for power[1-*]_reset_history
if we have a single file for resetting all the extremes of a given
channel, or power[1-*]_input_lowest_reset and
power[1-*]_input_highest_reset if we go for a per-value reset.

Alternatively, we could simply make the power[1-*]_input_lowest and
power[1-*]_input_highest files writable, and "cat power1_input >
power1_input_lowest" (or any write?) would reset the history.

>  
>  **********
>  * Alarms *

Thanks,
-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Update
@ 2007-09-17 17:28               ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-17 17:28 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick,

On Fri, 14 Sep 2007 12:29:24 -0700, Darrick J. Wong wrote:
> Update the hwmon sysfs interface documentation to include a specification
> for power meters.

Thanks for the update. I have some more comments. And sorry if it looks
like nitpicking, but this is a standard interface we're defining so we
better make sure that we get it right.

First of all, please think of a better subject line for this patch.
"Update Documentation/hwmon/sysfs-interface" is too vague when your
patch is rather specific.

> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  Documentation/hwmon/sysfs-interface |   30 ++++++++++++++++++++++++++++++
>  1 files changed, 30 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
> index db7bb4a..5c98bee 100644
> --- a/Documentation/hwmon/sysfs-interface
> +++ b/Documentation/hwmon/sysfs-interface
> @@ -324,6 +324,36 @@ curr[1-*]_input	Current input value
>  		Unit: milliampere
>  		RO
>  
> +*********
> +* Power *
> +*********
> +
> +power[1-*]_average		Average power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_average_highest	Historical average maximum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_average_lowest	Historical average minimum power use
> +				Unit: microWatt
> +				RO

How useful are historical extremes of an average?

> +
> +power[1-*]_input		Instantaneous power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_input_highest	Historical maximum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_input_lowest		Historical minimum power use
> +				Unit: microWatt
> +				RO
> +
> +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> +				WO

I don't much like this name. It sounds like a name crafted for the
specific feature of a given chip, while we try to use generic names for
the standard interface. I would rather go for power[1-*]_reset_history
if we have a single file for resetting all the extremes of a given
channel, or power[1-*]_input_lowest_reset and
power[1-*]_input_highest_reset if we go for a per-value reset.

Alternatively, we could simply make the power[1-*]_input_lowest and
power[1-*]_input_highest files writable, and "cat power1_input >
power1_input_lowest" (or any write?) would reset the history.

>  
>  **********
>  * Alarms *

Thanks,
-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface
  2007-09-17 17:28               ` [lm-sensors] [PATCH v2] hwmon: Update Jean Delvare
@ 2007-09-17 18:43                 ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-17 18:43 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 2204 bytes --]

On Mon, Sep 17, 2007 at 07:28:08PM +0200, Jean Delvare wrote:
> Thanks for the update. I have some more comments. And sorry if it looks
> like nitpicking, but this is a standard interface we're defining so we
> better make sure that we get it right.

Agreed. :)
> 
> First of all, please think of a better subject line for this patch.
> "Update Documentation/hwmon/sysfs-interface" is too vague when your
> patch is rather specific.

Will change to:
"Add power meter spec to Documentation/hwmon/sysfs-interface"

> > +power[1-*]_average_lowest	Historical average minimum power use
> > +				Unit: microWatt
> > +				RO
> 
> How useful are historical extremes of an average?

I don't know if anybody ever really requires this data, but the device
I'm talking to records the extremes in hardware, so I put it in the driver.

> > +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> > +				WO
> 
> I don't much like this name. It sounds like a name crafted for the
> specific feature of a given chip, while we try to use generic names for

Guilty as charged.  Since ibmpex is the only on-board power meter hardware
to which I have access, the interface proposal includes the various odd
pieces that the hardware provides in addition to the meters.  I
could take the historical extreme and reset bits out of the proposal and
put them in something like Documentation/hwmon/ibmpex.txt as extra
chip-specific features if people prefer that.

> the standard interface. I would rather go for power[1-*]_reset_history

Me too.

> if we have a single file for resetting all the extremes of a given
> channel, or power[1-*]_input_lowest_reset and
> power[1-*]_input_highest_reset if we go for a per-value reset.

The ibmpex hardware only knows how to reset all of them.

> Alternatively, we could simply make the power[1-*]_input_lowest and
> power[1-*]_input_highest files writable, and "cat power1_input >
> power1_input_lowest" (or any write?) would reset the history.

I'd prefer to leave it explicitly called out as a separate sysfs knob,
unless there are established precedents for resetting a sensor by
writing something to its sysfs file.

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Update
@ 2007-09-17 18:43                 ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-09-17 18:43 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 2204 bytes --]

On Mon, Sep 17, 2007 at 07:28:08PM +0200, Jean Delvare wrote:
> Thanks for the update. I have some more comments. And sorry if it looks
> like nitpicking, but this is a standard interface we're defining so we
> better make sure that we get it right.

Agreed. :)
> 
> First of all, please think of a better subject line for this patch.
> "Update Documentation/hwmon/sysfs-interface" is too vague when your
> patch is rather specific.

Will change to:
"Add power meter spec to Documentation/hwmon/sysfs-interface"

> > +power[1-*]_average_lowest	Historical average minimum power use
> > +				Unit: microWatt
> > +				RO
> 
> How useful are historical extremes of an average?

I don't know if anybody ever really requires this data, but the device
I'm talking to records the extremes in hardware, so I put it in the driver.

> > +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> > +				WO
> 
> I don't much like this name. It sounds like a name crafted for the
> specific feature of a given chip, while we try to use generic names for

Guilty as charged.  Since ibmpex is the only on-board power meter hardware
to which I have access, the interface proposal includes the various odd
pieces that the hardware provides in addition to the meters.  I
could take the historical extreme and reset bits out of the proposal and
put them in something like Documentation/hwmon/ibmpex.txt as extra
chip-specific features if people prefer that.

> the standard interface. I would rather go for power[1-*]_reset_history

Me too.

> if we have a single file for resetting all the extremes of a given
> channel, or power[1-*]_input_lowest_reset and
> power[1-*]_input_highest_reset if we go for a per-value reset.

The ibmpex hardware only knows how to reset all of them.

> Alternatively, we could simply make the power[1-*]_input_lowest and
> power[1-*]_input_highest files writable, and "cat power1_input >
> power1_input_lowest" (or any write?) would reset the history.

I'd prefer to leave it explicitly called out as a separate sysfs knob,
unless there are established precedents for resetting a sensor by
writing something to its sysfs file.

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface
  2007-09-17 18:43                 ` [lm-sensors] [PATCH v2] hwmon: Update Darrick J. Wong
@ 2007-09-21  8:43                   ` Jean Delvare
  -1 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-21  8:43 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick,

On Mon, 17 Sep 2007 11:43:11 -0700, Darrick J. Wong wrote:
> On Mon, Sep 17, 2007 at 07:28:08PM +0200, Jean Delvare wrote:
> > First of all, please think of a better subject line for this patch.
> > "Update Documentation/hwmon/sysfs-interface" is too vague when your
> > patch is rather specific.
> 
> Will change to:
> "Add power meter spec to Documentation/hwmon/sysfs-interface"

Sounds good, thanks.

> > > +power[1-*]_average_lowest	Historical average minimum power use
> > > +				Unit: microWatt
> > > +				RO
> > 
> > How useful are historical extremes of an average?
> 
> I don't know if anybody ever really requires this data, but the device
> I'm talking to records the extremes in hardware, so I put it in the driver.

OK, fine with me then.

> > > +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> > > +				WO
> > 
> > I don't much like this name. It sounds like a name crafted for the
> > specific feature of a given chip, while we try to use generic names for
> 
> Guilty as charged.  Since ibmpex is the only on-board power meter hardware
> to which I have access, the interface proposal includes the various odd
> pieces that the hardware provides in addition to the meters.  I
> could take the historical extreme and reset bits out of the proposal and
> put them in something like Documentation/hwmon/ibmpex.txt as extra
> chip-specific features if people prefer that.

I'm fine having them in the spec as long as the interface is made
generic enough.

> > the standard interface. I would rather go for power[1-*]_reset_history
> 
> Me too.
> 
> > if we have a single file for resetting all the extremes of a given
> > channel, or power[1-*]_input_lowest_reset and
> > power[1-*]_input_highest_reset if we go for a per-value reset.
> 
> The ibmpex hardware only knows how to reset all of them.

This doesn't mean we do not want separate files. The same problem
exists for many other hardware monitoring features. For example most
thermal sensor chips have one high temperature limit per temperature
channel. However some of them have a single limit register for several
channels! The approach we have taken so far was to have individual
files, and it's up to the driver to keep them in sync.

Another example is alarm files. Some chips have a single alarm for
out-of-range voltage measurements. Others have separate alarms for low
voltage and high voltage limit crossing. In that case, we decided to
have either a single file (in0_alarm) or separate files (in0_min_alarm
and in0_max_alarm) depending on what the chip supports. Admittedly this
is not very consistent with what we did for the shared temperature
limits.

> > Alternatively, we could simply make the power[1-*]_input_lowest and
> > power[1-*]_input_highest files writable, and "cat power1_input >
> > power1_input_lowest" (or any write?) would reset the history.
> 
> I'd prefer to leave it explicitly called out as a separate sysfs knob,
> unless there are established precedents for resetting a sensor by
> writing something to its sysfs file.

No, there's no such established precedent. This was only a suggestion
from me as it seems to make some sense. If you don't like the idea,
just ignore it.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Update
@ 2007-09-21  8:43                   ` Jean Delvare
  0 siblings, 0 replies; 72+ messages in thread
From: Jean Delvare @ 2007-09-21  8:43 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Mark M. Hoffman, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick,

On Mon, 17 Sep 2007 11:43:11 -0700, Darrick J. Wong wrote:
> On Mon, Sep 17, 2007 at 07:28:08PM +0200, Jean Delvare wrote:
> > First of all, please think of a better subject line for this patch.
> > "Update Documentation/hwmon/sysfs-interface" is too vague when your
> > patch is rather specific.
> 
> Will change to:
> "Add power meter spec to Documentation/hwmon/sysfs-interface"

Sounds good, thanks.

> > > +power[1-*]_average_lowest	Historical average minimum power use
> > > +				Unit: microWatt
> > > +				RO
> > 
> > How useful are historical extremes of an average?
> 
> I don't know if anybody ever really requires this data, but the device
> I'm talking to records the extremes in hardware, so I put it in the driver.

OK, fine with me then.

> > > +power[1-*]_high_low_reset	Reset input_highest/input_lowest.
> > > +				WO
> > 
> > I don't much like this name. It sounds like a name crafted for the
> > specific feature of a given chip, while we try to use generic names for
> 
> Guilty as charged.  Since ibmpex is the only on-board power meter hardware
> to which I have access, the interface proposal includes the various odd
> pieces that the hardware provides in addition to the meters.  I
> could take the historical extreme and reset bits out of the proposal and
> put them in something like Documentation/hwmon/ibmpex.txt as extra
> chip-specific features if people prefer that.

I'm fine having them in the spec as long as the interface is made
generic enough.

> > the standard interface. I would rather go for power[1-*]_reset_history
> 
> Me too.
> 
> > if we have a single file for resetting all the extremes of a given
> > channel, or power[1-*]_input_lowest_reset and
> > power[1-*]_input_highest_reset if we go for a per-value reset.
> 
> The ibmpex hardware only knows how to reset all of them.

This doesn't mean we do not want separate files. The same problem
exists for many other hardware monitoring features. For example most
thermal sensor chips have one high temperature limit per temperature
channel. However some of them have a single limit register for several
channels! The approach we have taken so far was to have individual
files, and it's up to the driver to keep them in sync.

Another example is alarm files. Some chips have a single alarm for
out-of-range voltage measurements. Others have separate alarms for low
voltage and high voltage limit crossing. In that case, we decided to
have either a single file (in0_alarm) or separate files (in0_min_alarm
and in0_max_alarm) depending on what the chip supports. Admittedly this
is not very consistent with what we did for the shared temperature
limits.

> > Alternatively, we could simply make the power[1-*]_input_lowest and
> > power[1-*]_input_highest files writable, and "cat power1_input >
> > power1_input_lowest" (or any write?) would reset the history.
> 
> I'd prefer to leave it explicitly called out as a separate sysfs knob,
> unless there are established precedents for resetting a sensor by
> writing something to its sysfs file.

No, there's no such established precedent. This was only a suggestion
from me as it seems to make some sense. If you don't like the idea,
just ignore it.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v4] IBM power meter driver
  2007-09-14 19:33             ` [lm-sensors] " Darrick J. Wong
@ 2007-10-09 12:00               ` Mark M. Hoffman
  -1 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-10-09 12:00 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick:

* Darrick J. Wong <djwong@us.ibm.com> [2007-09-14 12:33:46 -0700]:
> Here's a fourth revision where I've tried to clean up the things that
> people complained about, as well as shifted the sysfs file names to
> match the spec that we've been drifting towards.

Sorry it took me so long to get back to this.  I've applied this to my hwmon
testing branch, but I would still like to get a review from an IPMI expert.

OBTW: please try to send patches which apply cleanly to some tree to which I
have access.  E.g.  from the Makefile - preceding and trailing context are not
present in any git tree that I know.

> ---
> ibm_pex: Driver to export IBM PowerExecutive power meter sensors.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  drivers/hwmon/Kconfig  |   13 +
>  drivers/hwmon/Makefile |    1 
>  drivers/hwmon/ibmpex.c |  608 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 622 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5ca5d95..591b666 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -315,6 +315,19 @@ config SENSORS_CORETEMP
>  	  sensor inside your CPU. Supported all are all known variants
>  	  of Intel Core family.
>  
> +config SENSORS_IBMPEX
> +	tristate "IBM PowerExecutive temperature/power sensors"
> +	select IPMI_SI
> +	depends on IPMI_HANDLER
> +	help
> +	  If you say yes here you get support for the temperature and
> +	  power sensors in various IBM System X servers that support
> +	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
> +	  x3755, and certain HS20 blades.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ibmpex.
> +
>  config SENSORS_IT87
>  	tristate "ITE IT87xx and compatibles"
>  	select HWMON_VID
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 5070cf7..0fcbcb4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
>  obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
>  obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
>  obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
> +obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
>  obj-$(CONFIG_SENSORS_IPMI)	+= ipmisensors.o
>  obj-$(CONFIG_SENSORS_IT87)	+= it87.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> new file mode 100644
> index 0000000..fe2c261
> --- /dev/null
> +++ b/drivers/hwmon/ibmpex.c
> @@ -0,0 +1,608 @@
> +/*
> + * A hwmon driver for the IBM PowerExecutive temperature/power sensors
> + * Copyright (C) 2007 IBM
> + *
> + * Author: Darrick J. Wong <djwong@us.ibm.com>
> + *
> + * 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 Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/ipmi.h>
> +#include <linux/module.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/jiffies.h>
> +#include <linux/mutex.h>
> +
> +#define REFRESH_INTERVAL	(2 * HZ)
> +#define DRVNAME			"ibmpex"
> +
> +#define PEX_GET_VERSION		1
> +#define PEX_GET_SENSOR_COUNT	2
> +#define PEX_GET_SENSOR_NAME	3
> +#define PEX_RESET_HIGH_LOW	4
> +#define PEX_GET_SENSOR_DATA	6
> +
> +#define PEX_NET_FUNCTION	0x3A
> +#define PEX_COMMAND		0x3C
> +
> +static inline u16 extract_value(const char *data, int offset)
> +{
> +	return be16_to_cpup((u16 *)&data[offset]);
> +}
> +
> +#define TEMP_SENSOR		1
> +#define POWER_SENSOR		2
> +
> +#define PEX_SENSOR_TYPE_LEN	3
> +static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
> +static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
> +
> +#define PEX_MULT_LEN		2
> +static u8 const watt_sensor_sig[]  = {0x41, 0x43};
> +
> +#define PEX_NUM_SENSOR_FUNCS	3
> +static char const * const power_sensor_name_templates[] = {
> +	"%s%d_average",
> +	"%s%d_average_lowest",
> +	"%s%d_average_highest"
> +};
> +static char const * const temp_sensor_name_templates[] = {
> +	"%s%d_input",
> +	"%s%d_input_lowest",
> +	"%s%d_input_highest"
> +};
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
> +static void ibmpex_register_bmc(int iface, struct device *dev);
> +static void ibmpex_bmc_gone(int iface);
> +
> +struct ibmpex_sensor_data {
> +	int			in_use;
> +	s16			values[PEX_NUM_SENSOR_FUNCS];
> +	int			multiplier;
> +
> +	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];
> +};
> +
> +struct ibmpex_bmc_data {
> +	struct list_head	list;
> +	struct device		*hwmon_dev;
> +	struct device		*bmc_device;
> +	struct mutex		lock;
> +	char			valid;
> +	unsigned long		last_updated;	/* In jiffies */
> +
> +	struct ipmi_addr	address;
> +	struct completion	read_complete;
> +	ipmi_user_t		user;
> +	int			interface;
> +
> +	struct kernel_ipmi_msg	tx_message;
> +	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	long			tx_msgid;
> +
> +	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	unsigned long		rx_msg_len;
> +	unsigned char		rx_result;
> +	int			rx_recv_type;
> +
> +	unsigned char		sensor_major;
> +	unsigned char		sensor_minor;
> +
> +	unsigned char		num_sensors;
> +	struct ibmpex_sensor_data	*sensors;
> +};
> +
> +struct ibmpex_driver_data {
> +	struct list_head	bmc_data;
> +	struct ipmi_smi_watcher	bmc_events;
> +	struct ipmi_user_hndl	ipmi_hndlrs;
> +};
> +
> +static struct ibmpex_driver_data driver_data = {
> +	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
> +	.bmc_events = {
> +		.owner = THIS_MODULE,
> +		.new_smi = ibmpex_register_bmc,
> +		.smi_gone = ibmpex_bmc_gone,
> +	},
> +	.ipmi_hndlrs = {
> +		.ipmi_recv_hndl = ibmpex_msg_handler,
> +	},
> +};
> +
> +static int ibmpex_send_message(struct ibmpex_bmc_data *data)
> +{
> +	int err;
> +
> +	err = ipmi_validate_addr(&data->address, sizeof(data->address));
> +	if (err)
> +		goto out;
> +
> +	data->tx_msgid++;
> +	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
> +				   &data->tx_message, data, 0, 0, 0);
> +	if (err)
> +		goto out1;
> +
> +	return 0;
> +out1:
> +	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
> +	return err;
> +out:
> +	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
> +	return err;
> +}
> +
> +static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_VERSION;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 6)
> +		return -ENOENT;
> +
> +	data->sensor_major = data->rx_msg_data[0];
> +	data->sensor_minor = data->rx_msg_data[1];
> +
> +	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
> +	       "v%d.%d %d-%02d-%02d on interface %d\n",
> +	       data->sensor_major,
> +	       data->sensor_minor,
> +	       extract_value(data->rx_msg_data, 2),
> +	       data->rx_msg_data[4],
> +	       data->rx_msg_data[5],
> +	       data->interface);
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 1)
> +		return -ENOENT;
> +
> +	return data->rx_msg_data[0];
> +}
> +
> +static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 1)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 26) {
> +		printk(KERN_ERR "Error reading sensor %d, please check.\n",
> +		       sensor);
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	return 0;
> +}
> +
> +static void ibmpex_update_device(struct ibmpex_bmc_data *data)
> +{
> +	int i, err;
> +
> +	mutex_lock(&data->lock);
> +	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
> +	    data->valid)
> +		goto out;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		if (!data->sensors[i].in_use)
> +			continue;
> +		err = ibmpex_query_sensor_data(data, i);
> +		if (err)
> +			continue;
> +		data->sensors[i].values[0] =
> +			extract_value(data->rx_msg_data, 16);
> +		data->sensors[i].values[1] =
> +			extract_value(data->rx_msg_data, 18);
> +		data->sensors[i].values[2] =
> +			extract_value(data->rx_msg_data, 20);
> +	}
> +
> +	data->last_updated = jiffies;
> +	data->valid = 1;
> +
> +out:
> +	mutex_unlock(&data->lock);
> +}
> +
> +static struct ibmpex_bmc_data *get_bmc_data(int iface)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		if (p->interface == iface)
> +			return p;
> +
> +	return NULL;
> +}
> +
> +static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	return sprintf(buf, "%s\n", DRVNAME);
> +}
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
> +
> +static ssize_t ibmpex_show_sensor(struct device *dev,
> +				  struct device_attribute *devattr,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
> +	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
> +	int mult = data->sensors[attr->index].multiplier;
> +	ibmpex_update_device(data);
> +
> +	return sprintf(buf, "%d\n",
> +		       data->sensors[attr->index].values[attr->nr] * mult);
> +}
> +
> +static ssize_t ibmpex_reset_high_low(struct device *dev,
> +				     struct device_attribute *devattr,
> +				     const char *buf,
> +				     size_t count)
> +{
> +	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
> +
> +	ibmpex_reset_high_low_data(data);
> +
> +	return count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
> +			  ibmpex_reset_high_low, 0);
> +
> +static int is_power_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int is_temp_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int power_sensor_multiplier(const char *sensor_id, int len)
> +{
> +	int i;
> +
> +	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
> +		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
> +			return 1000000;
> +
> +	return 100000;
> +}
> +
> +static int create_sensor(struct ibmpex_bmc_data *data, int type,
> +			 int counter, int sensor, int func)
> +{
> +	int err;
> +	char *n;
> +
> +	n = kmalloc(32, GFP_KERNEL);
> +	if (!n)
> +		return -ENOMEM;
> +
> +	if (type == TEMP_SENSOR)
> +		sprintf(n, temp_sensor_name_templates[func], "temp", counter);
> +	else if (type == POWER_SENSOR)
> +		sprintf(n, power_sensor_name_templates[func], "power", counter);
> +
> +	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
> +	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
> +	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
> +	data->sensors[sensor].attr[func].index = sensor;
> +	data->sensors[sensor].attr[func].nr = func;
> +
> +	err = device_create_file(data->bmc_device,
> +				 &data->sensors[sensor].attr[func].dev_attr);
> +	if (err) {
> +		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
> +		kfree(n);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
> +{
> +	int i, j, err;
> +	int sensor_type;
> +	int sensor_counter;
> +	int num_power = 0;
> +	int num_temp = 0;
> +
> +	err = ibmpex_query_sensor_count(data);
> +	if (err <= 0)
> +		return -ENOENT;
> +	data->num_sensors = err;
> +
> +	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
> +				GFP_KERNEL);
> +	if (!data->sensors)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		err = ibmpex_query_sensor_name(data, i);
> +		if (err)
> +			continue;
> +
> +		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
> +			sensor_type = POWER_SENSOR;
> +			num_power++;
> +			sensor_counter = num_power;
> +			data->sensors[i].multiplier =
> +				power_sensor_multiplier(data->rx_msg_data,
> +						     data->rx_msg_len);
> +		} else if (is_temp_sensor(data->rx_msg_data,
> +					  data->rx_msg_len)) {
> +			sensor_type = TEMP_SENSOR;
> +			num_temp++;
> +			sensor_counter = num_temp;
> +			data->sensors[i].multiplier = 1;
> +		} else
> +			continue;
> +
> +		data->sensors[i].in_use = 1;
> +
> +		/* Create attributes */
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			err = create_sensor(data, sensor_type, sensor_counter,
> +					    i, j);
> +			if (err)
> +				goto exit_remove;
> +		}
> +	}
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_reset_high_low.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_name.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(data->bmc_device,
> +			   &sensor_dev_attr_reset_high_low.dev_attr);
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	kfree(data->sensors);
> +	return err;
> +}
> +
> +static void ibmpex_register_bmc(int iface, struct device *dev)
> +{
> +	struct ibmpex_bmc_data *data;
> +	int err;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
> +		       "interface %d.\n", data->interface);
> +		return;
> +	}
> +
> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
> +	data->address.channel = IPMI_BMC_CHANNEL;
> +	data->address.data[0] = 0;
> +	data->interface = iface;
> +	data->bmc_device = dev;
> +
> +	/* Create IPMI messaging interface user */
> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
> +			       data, &data->user);
> +	if (err < 0) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
> +		       "ipmi interface %d\n",
> +		       data->interface);
> +		goto out;
> +	}
> +
> +	mutex_init(&data->lock);
> +
> +	/* Initialize message */
> +	data->tx_msgid = 0;
> +	init_completion(&data->read_complete);
> +	data->tx_message.netfn = PEX_NET_FUNCTION;
> +	data->tx_message.cmd = PEX_COMMAND;
> +	data->tx_message.data = data->tx_msg_data;
> +
> +	/* Does this BMC support PowerExecutive? */
> +	err = ibmpex_ver_check(data);
> +	if (err)
> +		goto out_user;
> +
> +	/* Register the BMC as a HWMON class device */
> +	data->hwmon_dev = hwmon_device_register(data->bmc_device);
> +
> +	if (IS_ERR(data->hwmon_dev)) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> +		       "class device for interface %d\n",
> +		       data->interface);
> +		kfree(data);
> +		return;
> +	}
> +
> +	/* finally add the new bmc data to the bmc data list */
> +	dev_set_drvdata(dev, data);
> +	list_add_tail(&data->list, &driver_data.bmc_data);
> +
> +	/* Now go find all the sensors */
> +	err = ibmpex_find_sensors(data);
> +	if (err) {
> +		printk(KERN_ERR "Error %d allocating memory\n", err);
> +		goto out_register;
> +	}
> +
> +	return;
> +
> +out_register:
> +	hwmon_device_unregister(data->hwmon_dev);
> +out_user:
> +	ipmi_destroy_user(data->user);
> +out:
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
> +{
> +	int i, j;
> +
> +	device_remove_file(data->bmc_device,
> +			   &sensor_dev_attr_reset_high_low.dev_attr);
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	list_del(&data->list);
> +	dev_set_drvdata(data->bmc_device, NULL);
> +	hwmon_device_unregister(data->hwmon_dev);
> +	ipmi_destroy_user(data->user);
> +	kfree(data->sensors);
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_gone(int iface)
> +{
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> +
> +	if (!data)
> +		return;
> +
> +	ibmpex_bmc_delete(data);
> +}
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> +{
> +	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
> +
> +	if (msg->msgid != data->tx_msgid) {
> +		printk(KERN_ERR "Received msgid (%02x) and transmitted "
> +		       "msgid (%02x) mismatch!\n",
> +		       (int)msg->msgid,
> +		       (int)data->tx_msgid);
> +		ipmi_free_recv_msg(msg);
> +		return;
> +	}
> +
> +	data->rx_recv_type = msg->recv_type;
> +	if (msg->msg.data_len > 0)
> +		data->rx_result = msg->msg.data[0];
> +	else
> +		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
> +
> +	if (msg->msg.data_len > 1) {
> +		data->rx_msg_len = msg->msg.data_len - 1;
> +		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
> +	} else
> +		data->rx_msg_len = 0;
> +
> +	ipmi_free_recv_msg(msg);
> +	complete(&data->read_complete);
> +}
> +
> +static int __init ibmpex_init(void)
> +{
> +	return ipmi_smi_watcher_register(&driver_data.bmc_events);
> +}
> +
> +static void __exit ibmpex_exit(void)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		ibmpex_bmc_delete(p);
> +}
> +
> +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
> +MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(ibmpex_init);
> +module_exit(ibmpex_exit);

Regards,

-- 
Mark M. Hoffman
mhoffman@lightlink.com


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v4] IBM power meter driver
@ 2007-10-09 12:00               ` Mark M. Hoffman
  0 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-10-09 12:00 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

Hi Darrick:

* Darrick J. Wong <djwong@us.ibm.com> [2007-09-14 12:33:46 -0700]:
> Here's a fourth revision where I've tried to clean up the things that
> people complained about, as well as shifted the sysfs file names to
> match the spec that we've been drifting towards.

Sorry it took me so long to get back to this.  I've applied this to my hwmon
testing branch, but I would still like to get a review from an IPMI expert.

OBTW: please try to send patches which apply cleanly to some tree to which I
have access.  E.g.  from the Makefile - preceding and trailing context are not
present in any git tree that I know.

> ---
> ibm_pex: Driver to export IBM PowerExecutive power meter sensors.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  drivers/hwmon/Kconfig  |   13 +
>  drivers/hwmon/Makefile |    1 
>  drivers/hwmon/ibmpex.c |  608 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 622 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5ca5d95..591b666 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -315,6 +315,19 @@ config SENSORS_CORETEMP
>  	  sensor inside your CPU. Supported all are all known variants
>  	  of Intel Core family.
>  
> +config SENSORS_IBMPEX
> +	tristate "IBM PowerExecutive temperature/power sensors"
> +	select IPMI_SI
> +	depends on IPMI_HANDLER
> +	help
> +	  If you say yes here you get support for the temperature and
> +	  power sensors in various IBM System X servers that support
> +	  PowerExecutive.  So far this includes the x3550, x3650, x3655,
> +	  x3755, and certain HS20 blades.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ibmpex.
> +
>  config SENSORS_IT87
>  	tristate "ITE IT87xx and compatibles"
>  	select HWMON_VID
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 5070cf7..0fcbcb4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
>  obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
>  obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
>  obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
> +obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
>  obj-$(CONFIG_SENSORS_IPMI)	+= ipmisensors.o
>  obj-$(CONFIG_SENSORS_IT87)	+= it87.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> new file mode 100644
> index 0000000..fe2c261
> --- /dev/null
> +++ b/drivers/hwmon/ibmpex.c
> @@ -0,0 +1,608 @@
> +/*
> + * A hwmon driver for the IBM PowerExecutive temperature/power sensors
> + * Copyright (C) 2007 IBM
> + *
> + * Author: Darrick J. Wong <djwong@us.ibm.com>
> + *
> + * 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 Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/ipmi.h>
> +#include <linux/module.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/jiffies.h>
> +#include <linux/mutex.h>
> +
> +#define REFRESH_INTERVAL	(2 * HZ)
> +#define DRVNAME			"ibmpex"
> +
> +#define PEX_GET_VERSION		1
> +#define PEX_GET_SENSOR_COUNT	2
> +#define PEX_GET_SENSOR_NAME	3
> +#define PEX_RESET_HIGH_LOW	4
> +#define PEX_GET_SENSOR_DATA	6
> +
> +#define PEX_NET_FUNCTION	0x3A
> +#define PEX_COMMAND		0x3C
> +
> +static inline u16 extract_value(const char *data, int offset)
> +{
> +	return be16_to_cpup((u16 *)&data[offset]);
> +}
> +
> +#define TEMP_SENSOR		1
> +#define POWER_SENSOR		2
> +
> +#define PEX_SENSOR_TYPE_LEN	3
> +static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
> +static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
> +
> +#define PEX_MULT_LEN		2
> +static u8 const watt_sensor_sig[]  = {0x41, 0x43};
> +
> +#define PEX_NUM_SENSOR_FUNCS	3
> +static char const * const power_sensor_name_templates[] = {
> +	"%s%d_average",
> +	"%s%d_average_lowest",
> +	"%s%d_average_highest"
> +};
> +static char const * const temp_sensor_name_templates[] = {
> +	"%s%d_input",
> +	"%s%d_input_lowest",
> +	"%s%d_input_highest"
> +};
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
> +static void ibmpex_register_bmc(int iface, struct device *dev);
> +static void ibmpex_bmc_gone(int iface);
> +
> +struct ibmpex_sensor_data {
> +	int			in_use;
> +	s16			values[PEX_NUM_SENSOR_FUNCS];
> +	int			multiplier;
> +
> +	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];
> +};
> +
> +struct ibmpex_bmc_data {
> +	struct list_head	list;
> +	struct device		*hwmon_dev;
> +	struct device		*bmc_device;
> +	struct mutex		lock;
> +	char			valid;
> +	unsigned long		last_updated;	/* In jiffies */
> +
> +	struct ipmi_addr	address;
> +	struct completion	read_complete;
> +	ipmi_user_t		user;
> +	int			interface;
> +
> +	struct kernel_ipmi_msg	tx_message;
> +	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	long			tx_msgid;
> +
> +	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];
> +	unsigned long		rx_msg_len;
> +	unsigned char		rx_result;
> +	int			rx_recv_type;
> +
> +	unsigned char		sensor_major;
> +	unsigned char		sensor_minor;
> +
> +	unsigned char		num_sensors;
> +	struct ibmpex_sensor_data	*sensors;
> +};
> +
> +struct ibmpex_driver_data {
> +	struct list_head	bmc_data;
> +	struct ipmi_smi_watcher	bmc_events;
> +	struct ipmi_user_hndl	ipmi_hndlrs;
> +};
> +
> +static struct ibmpex_driver_data driver_data = {
> +	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
> +	.bmc_events = {
> +		.owner = THIS_MODULE,
> +		.new_smi = ibmpex_register_bmc,
> +		.smi_gone = ibmpex_bmc_gone,
> +	},
> +	.ipmi_hndlrs = {
> +		.ipmi_recv_hndl = ibmpex_msg_handler,
> +	},
> +};
> +
> +static int ibmpex_send_message(struct ibmpex_bmc_data *data)
> +{
> +	int err;
> +
> +	err = ipmi_validate_addr(&data->address, sizeof(data->address));
> +	if (err)
> +		goto out;
> +
> +	data->tx_msgid++;
> +	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
> +				   &data->tx_message, data, 0, 0, 0);
> +	if (err)
> +		goto out1;
> +
> +	return 0;
> +out1:
> +	printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
> +	return err;
> +out:
> +	printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
> +	return err;
> +}
> +
> +static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_VERSION;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 6)
> +		return -ENOENT;
> +
> +	data->sensor_major = data->rx_msg_data[0];
> +	data->sensor_minor = data->rx_msg_data[1];
> +
> +	printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
> +	       "v%d.%d %d-%02d-%02d on interface %d\n",
> +	       data->sensor_major,
> +	       data->sensor_minor,
> +	       extract_value(data->rx_msg_data, 2),
> +	       data->rx_msg_data[4],
> +	       data->rx_msg_data[5],
> +	       data->interface);
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len != 1)
> +		return -ENOENT;
> +
> +	return data->rx_msg_data[0];
> +}
> +
> +static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 1)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
> +{
> +	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
> +	data->tx_msg_data[1] = sensor;
> +	data->tx_message.data_len = 2;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	if (data->rx_result || data->rx_msg_len < 26) {
> +		printk(KERN_ERR "Error reading sensor %d, please check.\n",
> +		       sensor);
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
> +{
> +	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
> +	data->tx_message.data_len = 1;
> +	ibmpex_send_message(data);
> +
> +	wait_for_completion(&data->read_complete);
> +
> +	return 0;
> +}
> +
> +static void ibmpex_update_device(struct ibmpex_bmc_data *data)
> +{
> +	int i, err;
> +
> +	mutex_lock(&data->lock);
> +	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
> +	    data->valid)
> +		goto out;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		if (!data->sensors[i].in_use)
> +			continue;
> +		err = ibmpex_query_sensor_data(data, i);
> +		if (err)
> +			continue;
> +		data->sensors[i].values[0] > +			extract_value(data->rx_msg_data, 16);
> +		data->sensors[i].values[1] > +			extract_value(data->rx_msg_data, 18);
> +		data->sensors[i].values[2] > +			extract_value(data->rx_msg_data, 20);
> +	}
> +
> +	data->last_updated = jiffies;
> +	data->valid = 1;
> +
> +out:
> +	mutex_unlock(&data->lock);
> +}
> +
> +static struct ibmpex_bmc_data *get_bmc_data(int iface)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		if (p->interface = iface)
> +			return p;
> +
> +	return NULL;
> +}
> +
> +static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
> +			 char *buf)
> +{
> +	return sprintf(buf, "%s\n", DRVNAME);
> +}
> +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
> +
> +static ssize_t ibmpex_show_sensor(struct device *dev,
> +				  struct device_attribute *devattr,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
> +	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
> +	int mult = data->sensors[attr->index].multiplier;
> +	ibmpex_update_device(data);
> +
> +	return sprintf(buf, "%d\n",
> +		       data->sensors[attr->index].values[attr->nr] * mult);
> +}
> +
> +static ssize_t ibmpex_reset_high_low(struct device *dev,
> +				     struct device_attribute *devattr,
> +				     const char *buf,
> +				     size_t count)
> +{
> +	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
> +
> +	ibmpex_reset_high_low_data(data);
> +
> +	return count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
> +			  ibmpex_reset_high_low, 0);
> +
> +static int is_power_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int is_temp_sensor(const char *sensor_id, int len)
> +{
> +	if (len < PEX_SENSOR_TYPE_LEN)
> +		return 0;
> +
> +	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
> +		return 1;
> +	return 0;
> +}
> +
> +static int power_sensor_multiplier(const char *sensor_id, int len)
> +{
> +	int i;
> +
> +	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
> +		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
> +			return 1000000;
> +
> +	return 100000;
> +}
> +
> +static int create_sensor(struct ibmpex_bmc_data *data, int type,
> +			 int counter, int sensor, int func)
> +{
> +	int err;
> +	char *n;
> +
> +	n = kmalloc(32, GFP_KERNEL);
> +	if (!n)
> +		return -ENOMEM;
> +
> +	if (type = TEMP_SENSOR)
> +		sprintf(n, temp_sensor_name_templates[func], "temp", counter);
> +	else if (type = POWER_SENSOR)
> +		sprintf(n, power_sensor_name_templates[func], "power", counter);
> +
> +	data->sensors[sensor].attr[func].dev_attr.attr.name = n;
> +	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
> +	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
> +	data->sensors[sensor].attr[func].index = sensor;
> +	data->sensors[sensor].attr[func].nr = func;
> +
> +	err = device_create_file(data->bmc_device,
> +				 &data->sensors[sensor].attr[func].dev_attr);
> +	if (err) {
> +		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
> +		kfree(n);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
> +{
> +	int i, j, err;
> +	int sensor_type;
> +	int sensor_counter;
> +	int num_power = 0;
> +	int num_temp = 0;
> +
> +	err = ibmpex_query_sensor_count(data);
> +	if (err <= 0)
> +		return -ENOENT;
> +	data->num_sensors = err;
> +
> +	data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
> +				GFP_KERNEL);
> +	if (!data->sensors)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < data->num_sensors; i++) {
> +		err = ibmpex_query_sensor_name(data, i);
> +		if (err)
> +			continue;
> +
> +		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
> +			sensor_type = POWER_SENSOR;
> +			num_power++;
> +			sensor_counter = num_power;
> +			data->sensors[i].multiplier > +				power_sensor_multiplier(data->rx_msg_data,
> +						     data->rx_msg_len);
> +		} else if (is_temp_sensor(data->rx_msg_data,
> +					  data->rx_msg_len)) {
> +			sensor_type = TEMP_SENSOR;
> +			num_temp++;
> +			sensor_counter = num_temp;
> +			data->sensors[i].multiplier = 1;
> +		} else
> +			continue;
> +
> +		data->sensors[i].in_use = 1;
> +
> +		/* Create attributes */
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			err = create_sensor(data, sensor_type, sensor_counter,
> +					    i, j);
> +			if (err)
> +				goto exit_remove;
> +		}
> +	}
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_reset_high_low.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(data->bmc_device,
> +			&sensor_dev_attr_name.dev_attr);
> +	if (err)
> +		goto exit_remove;
> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(data->bmc_device,
> +			   &sensor_dev_attr_reset_high_low.dev_attr);
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	kfree(data->sensors);
> +	return err;
> +}
> +
> +static void ibmpex_register_bmc(int iface, struct device *dev)
> +{
> +	struct ibmpex_bmc_data *data;
> +	int err;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
> +		       "interface %d.\n", data->interface);
> +		return;
> +	}
> +
> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
> +	data->address.channel = IPMI_BMC_CHANNEL;
> +	data->address.data[0] = 0;
> +	data->interface = iface;
> +	data->bmc_device = dev;
> +
> +	/* Create IPMI messaging interface user */
> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
> +			       data, &data->user);
> +	if (err < 0) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
> +		       "ipmi interface %d\n",
> +		       data->interface);
> +		goto out;
> +	}
> +
> +	mutex_init(&data->lock);
> +
> +	/* Initialize message */
> +	data->tx_msgid = 0;
> +	init_completion(&data->read_complete);
> +	data->tx_message.netfn = PEX_NET_FUNCTION;
> +	data->tx_message.cmd = PEX_COMMAND;
> +	data->tx_message.data = data->tx_msg_data;
> +
> +	/* Does this BMC support PowerExecutive? */
> +	err = ibmpex_ver_check(data);
> +	if (err)
> +		goto out_user;
> +
> +	/* Register the BMC as a HWMON class device */
> +	data->hwmon_dev = hwmon_device_register(data->bmc_device);
> +
> +	if (IS_ERR(data->hwmon_dev)) {
> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> +		       "class device for interface %d\n",
> +		       data->interface);
> +		kfree(data);
> +		return;
> +	}
> +
> +	/* finally add the new bmc data to the bmc data list */
> +	dev_set_drvdata(dev, data);
> +	list_add_tail(&data->list, &driver_data.bmc_data);
> +
> +	/* Now go find all the sensors */
> +	err = ibmpex_find_sensors(data);
> +	if (err) {
> +		printk(KERN_ERR "Error %d allocating memory\n", err);
> +		goto out_register;
> +	}
> +
> +	return;
> +
> +out_register:
> +	hwmon_device_unregister(data->hwmon_dev);
> +out_user:
> +	ipmi_destroy_user(data->user);
> +out:
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
> +{
> +	int i, j;
> +
> +	device_remove_file(data->bmc_device,
> +			   &sensor_dev_attr_reset_high_low.dev_attr);
> +	device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
> +	for (i = 0; i < data->num_sensors; i++)
> +		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
> +			if (!data->sensors[i].attr[j].dev_attr.attr.name)
> +				continue;
> +			device_remove_file(data->bmc_device,
> +				&data->sensors[i].attr[j].dev_attr);
> +			kfree(data->sensors[i].attr[j].dev_attr.attr.name);
> +		}
> +
> +	list_del(&data->list);
> +	dev_set_drvdata(data->bmc_device, NULL);
> +	hwmon_device_unregister(data->hwmon_dev);
> +	ipmi_destroy_user(data->user);
> +	kfree(data->sensors);
> +	kfree(data);
> +}
> +
> +static void ibmpex_bmc_gone(int iface)
> +{
> +	struct ibmpex_bmc_data *data = get_bmc_data(iface);
> +
> +	if (!data)
> +		return;
> +
> +	ibmpex_bmc_delete(data);
> +}
> +
> +static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> +{
> +	struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
> +
> +	if (msg->msgid != data->tx_msgid) {
> +		printk(KERN_ERR "Received msgid (%02x) and transmitted "
> +		       "msgid (%02x) mismatch!\n",
> +		       (int)msg->msgid,
> +		       (int)data->tx_msgid);
> +		ipmi_free_recv_msg(msg);
> +		return;
> +	}
> +
> +	data->rx_recv_type = msg->recv_type;
> +	if (msg->msg.data_len > 0)
> +		data->rx_result = msg->msg.data[0];
> +	else
> +		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
> +
> +	if (msg->msg.data_len > 1) {
> +		data->rx_msg_len = msg->msg.data_len - 1;
> +		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
> +	} else
> +		data->rx_msg_len = 0;
> +
> +	ipmi_free_recv_msg(msg);
> +	complete(&data->read_complete);
> +}
> +
> +static int __init ibmpex_init(void)
> +{
> +	return ipmi_smi_watcher_register(&driver_data.bmc_events);
> +}
> +
> +static void __exit ibmpex_exit(void)
> +{
> +	struct ibmpex_bmc_data *p, *next;
> +
> +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> +	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
> +		ibmpex_bmc_delete(p);
> +}
> +
> +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
> +MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(ibmpex_init);
> +module_exit(ibmpex_exit);

Regards,

-- 
Mark M. Hoffman
mhoffman@lightlink.com


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v4] IBM power meter driver
  2007-10-09 12:00               ` [lm-sensors] " Mark M. Hoffman
@ 2007-10-09 16:44                 ` Roel Kluin
  -1 siblings, 0 replies; 72+ messages in thread
From: Roel Kluin @ 2007-10-09 16:44 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Darrick J. Wong, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Mark M. Hoffman wrote:
>> +static void ibmpex_register_bmc(int iface, struct device *dev)
>> +{
>> +	struct ibmpex_bmc_data *data;
>> +	int err;
>> +
>> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
>> +	if (!data) {
>> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
>> +		       "interface %d.\n", data->interface);
>> +		return;
>> +	}
>> +
>> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
>> +	data->address.channel = IPMI_BMC_CHANNEL;
>> +	data->address.data[0] = 0;
>> +	data->interface = iface;
>> +	data->bmc_device = dev;
>> +
>> +	/* Create IPMI messaging interface user */
>> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
>> +			       data, &data->user);
>> +	if (err < 0) {
>> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
>> +		       "ipmi interface %d\n",
>> +		       data->interface);
>> +		goto out;
>> +	}
>> +
>> +	mutex_init(&data->lock);
>> +
>> +	/* Initialize message */
>> +	data->tx_msgid = 0;
>> +	init_completion(&data->read_complete);
>> +	data->tx_message.netfn = PEX_NET_FUNCTION;
>> +	data->tx_message.cmd = PEX_COMMAND;
>> +	data->tx_message.data = data->tx_msg_data;
>> +
>> +	/* Does this BMC support PowerExecutive? */
>> +	err = ibmpex_ver_check(data);
>> +	if (err)
>> +		goto out_user;
>> +
>> +	/* Register the BMC as a HWMON class device */
>> +	data->hwmon_dev = hwmon_device_register(data->bmc_device);
>> +
>> +	if (IS_ERR(data->hwmon_dev)) {
>> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
>> +		       "class device for interface %d\n",
>> +		       data->interface);
>> +		kfree(data);
>> +		return;

don't you want to goto out_user here instead?

>> +	}
>> +
>> +	/* finally add the new bmc data to the bmc data list */
>> +	dev_set_drvdata(dev, data);
>> +	list_add_tail(&data->list, &driver_data.bmc_data);
>> +
>> +	/* Now go find all the sensors */
>> +	err = ibmpex_find_sensors(data);
>> +	if (err) {
>> +		printk(KERN_ERR "Error %d allocating memory\n", err);
>> +		goto out_register;
>> +	}
>> +
>> +	return;
>> +
>> +out_register:
>> +	hwmon_device_unregister(data->hwmon_dev);
>> +out_user:
>> +	ipmi_destroy_user(data->user);
>> +out:
>> +	kfree(data);
>> +}



^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v4] IBM power meter driver
@ 2007-10-09 16:44                 ` Roel Kluin
  0 siblings, 0 replies; 72+ messages in thread
From: Roel Kluin @ 2007-10-09 16:44 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Darrick J. Wong, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Mark M. Hoffman wrote:
>> +static void ibmpex_register_bmc(int iface, struct device *dev)
>> +{
>> +	struct ibmpex_bmc_data *data;
>> +	int err;
>> +
>> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
>> +	if (!data) {
>> +		printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
>> +		       "interface %d.\n", data->interface);
>> +		return;
>> +	}
>> +
>> +	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
>> +	data->address.channel = IPMI_BMC_CHANNEL;
>> +	data->address.data[0] = 0;
>> +	data->interface = iface;
>> +	data->bmc_device = dev;
>> +
>> +	/* Create IPMI messaging interface user */
>> +	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
>> +			       data, &data->user);
>> +	if (err < 0) {
>> +		printk(KERN_ERR DRVNAME ": Error, unable to register user with "
>> +		       "ipmi interface %d\n",
>> +		       data->interface);
>> +		goto out;
>> +	}
>> +
>> +	mutex_init(&data->lock);
>> +
>> +	/* Initialize message */
>> +	data->tx_msgid = 0;
>> +	init_completion(&data->read_complete);
>> +	data->tx_message.netfn = PEX_NET_FUNCTION;
>> +	data->tx_message.cmd = PEX_COMMAND;
>> +	data->tx_message.data = data->tx_msg_data;
>> +
>> +	/* Does this BMC support PowerExecutive? */
>> +	err = ibmpex_ver_check(data);
>> +	if (err)
>> +		goto out_user;
>> +
>> +	/* Register the BMC as a HWMON class device */
>> +	data->hwmon_dev = hwmon_device_register(data->bmc_device);
>> +
>> +	if (IS_ERR(data->hwmon_dev)) {
>> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
>> +		       "class device for interface %d\n",
>> +		       data->interface);
>> +		kfree(data);
>> +		return;

don't you want to goto out_user here instead?

>> +	}
>> +
>> +	/* finally add the new bmc data to the bmc data list */
>> +	dev_set_drvdata(dev, data);
>> +	list_add_tail(&data->list, &driver_data.bmc_data);
>> +
>> +	/* Now go find all the sensors */
>> +	err = ibmpex_find_sensors(data);
>> +	if (err) {
>> +		printk(KERN_ERR "Error %d allocating memory\n", err);
>> +		goto out_register;
>> +	}
>> +
>> +	return;
>> +
>> +out_register:
>> +	hwmon_device_unregister(data->hwmon_dev);
>> +out_user:
>> +	ipmi_destroy_user(data->user);
>> +out:
>> +	kfree(data);
>> +}



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v4] IBM power meter driver
  2007-10-09 16:44                 ` [lm-sensors] " Roel Kluin
@ 2007-10-09 20:40                   ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-09 20:40 UTC (permalink / raw)
  To: Roel Kluin
  Cc: Mark M. Hoffman, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

On Tue, Oct 09, 2007 at 06:44:07PM +0200, Roel Kluin wrote:

> >> +	if (IS_ERR(data->hwmon_dev)) {
> >> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> >> +		       "class device for interface %d\n",
> >> +		       data->interface);
> >> +		kfree(data);
> >> +		return;
> 
> don't you want to goto out_user here instead?

Err, yes, thank you for catching that.

--D

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v4] IBM power meter driver
@ 2007-10-09 20:40                   ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-09 20:40 UTC (permalink / raw)
  To: Roel Kluin
  Cc: Mark M. Hoffman, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

On Tue, Oct 09, 2007 at 06:44:07PM +0200, Roel Kluin wrote:

> >> +	if (IS_ERR(data->hwmon_dev)) {
> >> +		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
> >> +		       "class device for interface %d\n",
> >> +		       data->interface);
> >> +		kfree(data);
> >> +		return;
> 
> don't you want to goto out_user here instead?

Err, yes, thank you for catching that.

--D

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH] ibmpex: Release IPMI user if hwmon registration fails
  2007-10-09 16:44                 ` [lm-sensors] " Roel Kluin
@ 2007-10-09 22:08                   ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-09 22:08 UTC (permalink / raw)
  To: Roel Kluin
  Cc: Mark M. Hoffman, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Roel Kluin <12o3l@tiscali.nl> found a minor defect in the init code if
hwmon device registration fails.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/ibmpex.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index fe2c261..c462824 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -498,8 +498,7 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
 		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
 		       "class device for interface %d\n",
 		       data->interface);
-		kfree(data);
-		return;
+		goto out_user;
 	}
 
 	/* finally add the new bmc data to the bmc data list */

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [lm-sensors] [PATCH] ibmpex: Release IPMI user if hwmon
@ 2007-10-09 22:08                   ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-09 22:08 UTC (permalink / raw)
  To: Roel Kluin
  Cc: Mark M. Hoffman, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Roel Kluin <12o3l@tiscali.nl> found a minor defect in the init code if
hwmon device registration fails.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
---

 drivers/hwmon/ibmpex.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index fe2c261..c462824 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -498,8 +498,7 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
 		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
 		       "class device for interface %d\n",
 		       data->interface);
-		kfree(data);
-		return;
+		goto out_user;
 	}
 
 	/* finally add the new bmc data to the bmc data list */

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH] ibmpex: Release IPMI user if hwmon registration fails
  2007-10-09 22:08                   ` [lm-sensors] [PATCH] ibmpex: Release IPMI user if hwmon Darrick J. Wong
@ 2007-10-11 11:45                     ` Mark M. Hoffman
  -1 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-10-11 11:45 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Roel Kluin, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Hi:

* Darrick J. Wong <djwong@us.ibm.com> [2007-10-09 15:08:24 -0700]:
> Roel Kluin <12o3l@tiscali.nl> found a minor defect in the init code if
> hwmon device registration fails.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  drivers/hwmon/ibmpex.c |    3 +--
>  1 files changed, 1 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> index fe2c261..c462824 100644
> --- a/drivers/hwmon/ibmpex.c
> +++ b/drivers/hwmon/ibmpex.c
> @@ -498,8 +498,7 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
>  		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
>  		       "class device for interface %d\n",
>  		       data->interface);
> -		kfree(data);
> -		return;
> +		goto out_user;
>  	}
>  
>  	/* finally add the new bmc data to the bmc data list */

Applied to hwmon-2.6.git/testing, thanks.

-- 
Mark M. Hoffman
mhoffman@lightlink.com


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH] ibmpex: Release IPMI user if hwmon
@ 2007-10-11 11:45                     ` Mark M. Hoffman
  0 siblings, 0 replies; 72+ messages in thread
From: Mark M. Hoffman @ 2007-10-11 11:45 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Roel Kluin, Jean Delvare, Henrique de Moraes Holschuh,
	linux-kernel, lm-sensors, haveblue

Hi:

* Darrick J. Wong <djwong@us.ibm.com> [2007-10-09 15:08:24 -0700]:
> Roel Kluin <12o3l@tiscali.nl> found a minor defect in the init code if
> hwmon device registration fails.
> 
> Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
> ---
> 
>  drivers/hwmon/ibmpex.c |    3 +--
>  1 files changed, 1 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
> index fe2c261..c462824 100644
> --- a/drivers/hwmon/ibmpex.c
> +++ b/drivers/hwmon/ibmpex.c
> @@ -498,8 +498,7 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
>  		printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
>  		       "class device for interface %d\n",
>  		       data->interface);
> -		kfree(data);
> -		return;
> +		goto out_user;
>  	}
>  
>  	/* finally add the new bmc data to the bmc data list */

Applied to hwmon-2.6.git/testing, thanks.

-- 
Mark M. Hoffman
mhoffman@lightlink.com


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v4] IBM power meter driver
  2007-10-09 12:00               ` [lm-sensors] " Mark M. Hoffman
@ 2007-10-13  0:29                 ` Darrick J. Wong
  -1 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-13  0:29 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue

[-- Attachment #1: Type: text/plain, Size: 939 bytes --]

On Tue, Oct 09, 2007 at 08:00:44AM -0400, Mark M. Hoffman wrote:

> Sorry it took me so long to get back to this.  I've applied this to my hwmon
> testing branch, but I would still like to get a review from an IPMI expert.

I'm working on finding somebody to do that.

> OBTW: please try to send patches which apply cleanly to some tree to which I
> have access.  E.g.  from the Makefile - preceding and trailing context are not
> present in any git tree that I know.

Oops, sorry about that.

The i5k_amb driver referenced in the Makefile diff is the same one that
I posted to lm-sensors a few weeks ago, though there hasn't been much
discussion after it was pointed out that someone had already written a
driver to achieve the same purpose.  Unfortunately, nothing's happened
to either driver; any thoughts?  I'm thinking that we ought to get one
of them into shape for upstream, though I don't particularly care which
one it is...

--D

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [lm-sensors] [PATCH v4] IBM power meter driver
@ 2007-10-13  0:29                 ` Darrick J. Wong
  0 siblings, 0 replies; 72+ messages in thread
From: Darrick J. Wong @ 2007-10-13  0:29 UTC (permalink / raw)
  To: Mark M. Hoffman
  Cc: Jean Delvare, Henrique de Moraes Holschuh, linux-kernel,
	lm-sensors, haveblue


[-- Attachment #1.1: Type: text/plain, Size: 939 bytes --]

On Tue, Oct 09, 2007 at 08:00:44AM -0400, Mark M. Hoffman wrote:

> Sorry it took me so long to get back to this.  I've applied this to my hwmon
> testing branch, but I would still like to get a review from an IPMI expert.

I'm working on finding somebody to do that.

> OBTW: please try to send patches which apply cleanly to some tree to which I
> have access.  E.g.  from the Makefile - preceding and trailing context are not
> present in any git tree that I know.

Oops, sorry about that.

The i5k_amb driver referenced in the Makefile diff is the same one that
I posted to lm-sensors a few weeks ago, though there hasn't been much
discussion after it was pointed out that someone had already written a
driver to achieve the same purpose.  Unfortunately, nothing's happened
to either driver; any thoughts?  I'm thinking that we ought to get one
of them into shape for upstream, though I don't particularly care which
one it is...

--D

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 72+ messages in thread

end of thread, other threads:[~2007-10-13  0:31 UTC | newest]

Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-08-27 21:14 [PATCH] v1 of IBM power meter driver Darrick J. Wong
2007-08-27 21:14 ` [lm-sensors] " Darrick J. Wong
2007-08-28  1:50 ` Henrique de Moraes Holschuh
2007-08-28  1:50   ` [lm-sensors] " Henrique de Moraes Holschuh
2007-08-28 11:19   ` Jean Delvare
2007-08-28 11:19     ` Jean Delvare
2007-08-28 16:28     ` Darrick J. Wong
2007-08-28 16:28       ` Darrick J. Wong
2007-08-29  9:49       ` Jean Delvare
2007-08-29  9:49         ` Jean Delvare
2007-08-29 12:45         ` Frank Phillips
2007-08-29 12:45           ` Frank Phillips
2007-08-28 16:44     ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Darrick J. Wong
2007-08-28 16:44       ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
2007-08-29  9:10       ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Jean Delvare
2007-08-29  9:10         ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
2007-08-29 14:50         ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Darrick J. Wong
2007-08-29 14:50           ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
2007-08-30  9:57           ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Jean Delvare
2007-08-30  9:57             ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
2007-09-11 16:43             ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Darrick J. Wong
2007-09-11 16:43               ` [lm-sensors] [PATCH] hwmon: Add power meters to Darrick J. Wong
2007-09-01 17:10       ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Pavel Machek
2007-09-01 17:10         ` [lm-sensors] [PATCH] hwmon: Add power meters to Pavel Machek
2007-09-01 18:05         ` [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Shem Multinymous
2007-09-01 18:05           ` [lm-sensors] [PATCH] hwmon: Add power meters to Shem Multinymous
2007-09-01 19:44           ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Henrique de Moraes Holschuh
2007-09-01 19:44             ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
2007-09-02 19:38             ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Jean Delvare
2007-09-02 19:38               ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
2007-09-03  2:02               ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Henrique de Moraes Holschuh
2007-09-03  2:02                 ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
2007-09-03 16:06                 ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Jean Delvare
2007-09-03 16:06                   ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
2007-09-03 23:22                   ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Henrique de Moraes Holschuh
2007-09-03 23:22                     ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
2007-09-06  9:34                     ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Jean Delvare
2007-09-06  9:34                       ` [lm-sensors] [PATCH] hwmon: Add power meters to Jean Delvare
2007-09-06 16:29                       ` [lm-sensors] [PATCH] hwmon: Add power meters to Documentation/hwmon/sysfs-interface Henrique de Moraes Holschuh
2007-09-06 16:29                         ` [lm-sensors] [PATCH] hwmon: Add power meters Henrique de Moraes Holschuh
2007-08-28 16:49     ` [PATCH] v2 of IBM power meter driver Darrick J. Wong
2007-08-28 16:49       ` [lm-sensors] " Darrick J. Wong
2007-08-28 23:25       ` [PATCH] v3 " Darrick J. Wong
2007-08-28 23:25         ` [lm-sensors] " Darrick J. Wong
2007-09-11 13:23         ` Mark M. Hoffman
2007-09-11 13:23           ` [lm-sensors] " Mark M. Hoffman
2007-09-11 13:59           ` Jean Delvare
2007-09-11 13:59             ` [lm-sensors] " Jean Delvare
2007-09-12  1:11           ` Darrick J. Wong
2007-09-12  1:11             ` [lm-sensors] " Darrick J. Wong
2007-09-14 19:29           ` [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface Darrick J. Wong
2007-09-14 19:29             ` [lm-sensors] [PATCH v2] hwmon: Update Darrick J. Wong
2007-09-17 17:28             ` [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface Jean Delvare
2007-09-17 17:28               ` [lm-sensors] [PATCH v2] hwmon: Update Jean Delvare
2007-09-17 18:43               ` [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface Darrick J. Wong
2007-09-17 18:43                 ` [lm-sensors] [PATCH v2] hwmon: Update Darrick J. Wong
2007-09-21  8:43                 ` [PATCH v2] hwmon: Update Documentation/hwmon/sysfs-interface Jean Delvare
2007-09-21  8:43                   ` [lm-sensors] [PATCH v2] hwmon: Update Jean Delvare
2007-09-14 19:33           ` [PATCH v4] IBM power meter driver Darrick J. Wong
2007-09-14 19:33             ` [lm-sensors] " Darrick J. Wong
2007-10-09 12:00             ` Mark M. Hoffman
2007-10-09 12:00               ` [lm-sensors] " Mark M. Hoffman
2007-10-09 16:44               ` Roel Kluin
2007-10-09 16:44                 ` [lm-sensors] " Roel Kluin
2007-10-09 20:40                 ` Darrick J. Wong
2007-10-09 20:40                   ` [lm-sensors] " Darrick J. Wong
2007-10-09 22:08                 ` [PATCH] ibmpex: Release IPMI user if hwmon registration fails Darrick J. Wong
2007-10-09 22:08                   ` [lm-sensors] [PATCH] ibmpex: Release IPMI user if hwmon Darrick J. Wong
2007-10-11 11:45                   ` [PATCH] ibmpex: Release IPMI user if hwmon registration fails Mark M. Hoffman
2007-10-11 11:45                     ` [lm-sensors] [PATCH] ibmpex: Release IPMI user if hwmon Mark M. Hoffman
2007-10-13  0:29               ` [PATCH v4] IBM power meter driver Darrick J. Wong
2007-10-13  0:29                 ` [lm-sensors] " Darrick J. Wong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.