[PATCH 2/2] ibmpex: Add energy consumption meters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Amend the ibmpex driver to report system energy consumption data.

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

 drivers/hwmon/ibmpex.c |  135 +++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 127 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index 9c9cdb0..8cc4262 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -35,9 +35,24 @@
 #define PEX_RESET_HIGH_LOW	4
 #define PEX_GET_SENSOR_DATA	6
 
+#define IBMPEX_TYPE_1		1
 #define PEX_NET_FUNCTION	0x3A
 #define PEX_COMMAND		0x3C
 
+#define IBMPEX_TYPE_2		2
+#define AEM_NET_FUNCTION	0x2E
+#define AEM_RUN_METHOD_COMMAND	0x81
+#define AEM_IANA_NUMBER_0	0x02
+#define AEM_READ_ELEMENT_REG	0x82
+
+#define AEM_ENERGY_REGISTER	0x01
+#define NUM_ENERGY_REGS		1
+
+static inline u64 extract_value64(const char *data, int offset)
+{
+	return be64_to_cpup((u64 *)&data[offset]);
+}
+
 static inline u16 extract_value(const char *data, int offset)
 {
 	return be16_to_cpup((u16 *)&data[offset]);
@@ -104,6 +119,9 @@ struct ibmpex_bmc_data {
 
 	unsigned char		num_sensors;
 	struct ibmpex_sensor_data	*sensors;
+
+	u64			energy[NUM_ENERGY_REGS];
+	struct sensor_device_attribute	energy_attr[NUM_ENERGY_REGS];
 };
 
 struct ibmpex_driver_data {
@@ -124,10 +142,23 @@ static struct ibmpex_driver_data driver_data = {
 	},
 };
 
-static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+static int ibmpex_send_message(struct ibmpex_bmc_data *data, int type)
 {
 	int err;
 
+	switch (type) {
+	case IBMPEX_TYPE_1:
+		data->tx_message.netfn = PEX_NET_FUNCTION;
+		data->tx_message.cmd = PEX_COMMAND;
+		break;
+	case IBMPEX_TYPE_2:
+		data->tx_message.netfn = AEM_NET_FUNCTION;
+		data->tx_message.cmd = AEM_RUN_METHOD_COMMAND;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	err = ipmi_validate_addr(&data->address, sizeof(data->address));
 	if (err)
 		goto out;
@@ -151,7 +182,7 @@ 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);
+	ibmpex_send_message(data, IBMPEX_TYPE_1);
 
 	wait_for_completion(&data->read_complete);
 
@@ -177,7 +208,7 @@ 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);
+	ibmpex_send_message(data, IBMPEX_TYPE_1);
 
 	wait_for_completion(&data->read_complete);
 
@@ -192,7 +223,7 @@ 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);
+	ibmpex_send_message(data, IBMPEX_TYPE_1);
 
 	wait_for_completion(&data->read_complete);
 
@@ -207,7 +238,7 @@ 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);
+	ibmpex_send_message(data, IBMPEX_TYPE_1);
 
 	wait_for_completion(&data->read_complete);
 
@@ -220,11 +251,36 @@ static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
 	return 0;
 }
 
+static int ibmpex_query_energy_data(struct ibmpex_bmc_data *data, int sensor)
+{
+	data->tx_msg_data[0] = AEM_IANA_NUMBER_0;
+	data->tx_msg_data[1] = 0x00;
+	data->tx_msg_data[2] = 0x00;
+	data->tx_msg_data[3] = 0x00;
+	data->tx_msg_data[4] = AEM_ENERGY_REGISTER;
+	data->tx_msg_data[5] = AEM_READ_ELEMENT_REG;
+	data->tx_msg_data[6] = sensor;
+	data->tx_msg_data[7] = 0x08;
+	data->tx_message.data_len = 8;
+
+	ibmpex_send_message(data, IBMPEX_TYPE_2);
+
+	wait_for_completion(&data->read_complete);
+
+	if (data->rx_result || data->rx_msg_len != 11) {
+		dev_err(data->bmc_device, "Error reading sensor %d.\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);
+	ibmpex_send_message(data, IBMPEX_TYPE_1);
 
 	wait_for_completion(&data->read_complete);
 
@@ -240,6 +296,13 @@ static void ibmpex_update_device(struct ibmpex_bmc_data *data)
 	    data->valid)
 		goto out;
 
+	for (i = 0; i < NUM_ENERGY_REGS; i++) {
+		err = ibmpex_query_energy_data(data, i);
+		if (err)
+			continue;
+		data->energy[i] = extract_value64(data->rx_msg_data, 3);
+	}
+
 	for (i = 0; i < data->num_sensors; i++) {
 		if (!data->sensors[i].in_use)
 			continue;
@@ -279,6 +342,17 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
 }
 static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
 
+static ssize_t ibmpex_show_energy(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+	ibmpex_update_device(data);
+
+	return sprintf(buf, "%llu000\n", data->energy[attr->index]);
+}
+
 static ssize_t ibmpex_show_sensor(struct device *dev,
 				  struct device_attribute *devattr,
 				  char *buf)
@@ -338,6 +412,33 @@ static int power_sensor_multiplier(const char *sensor_id, int len)
 	return 100000;
 }
 
+static int create_energy_sensor(struct ibmpex_bmc_data *data, int sensor)
+{
+	int err;
+	char *n;
+
+	n = kmalloc(32, GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	sprintf(n, "energy%d_input", sensor);
+
+	data->energy_attr[sensor].dev_attr.attr.name = n;
+	data->energy_attr[sensor].dev_attr.attr.mode = S_IRUGO;
+	data->energy_attr[sensor].dev_attr.show = ibmpex_show_energy;
+	data->energy_attr[sensor].index = sensor;
+
+	err = device_create_file(data->bmc_device,
+				 &data->energy_attr[sensor].dev_attr);
+	if (err) {
+		data->energy_attr[sensor].dev_attr.attr.name = NULL;
+		kfree(n);
+		return err;
+	}
+
+	return 0;
+}
+
 static int create_sensor(struct ibmpex_bmc_data *data, int type,
 			 int counter, int sensor, int func)
 {
@@ -420,6 +521,12 @@ static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
 		}
 	}
 
+	for (i = 0; i < NUM_ENERGY_REGS; i++) {
+		err = create_energy_sensor(data, i);
+		if (err)
+			goto exit_remove;
+	}
+
 	err = device_create_file(data->bmc_device,
 			&sensor_dev_attr_reset_high_low.dev_attr);
 	if (err)
@@ -436,6 +543,13 @@ 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 < NUM_ENERGY_REGS; i++) {
+		if (!data->energy_attr[i].dev_attr.attr.name)
+			continue;
+		device_remove_file(data->bmc_device,
+			&data->energy_attr[i].dev_attr);
+		kfree(data->energy_attr[i].dev_attr.attr.name);
+	}
 	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)
@@ -480,8 +594,6 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
 	/* 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? */
@@ -527,6 +639,13 @@ static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
 	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 < NUM_ENERGY_REGS; i++) {
+		if (!data->energy_attr[i].dev_attr.attr.name)
+			continue;
+		device_remove_file(data->bmc_device,
+			&data->energy_attr[i].dev_attr);
+		kfree(data->energy_attr[i].dev_attr.attr.name);
+	}
 	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)




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux