aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@us.ibm.com>2007-09-14 15:33:46 -0400
committerMark M. Hoffman <mhoffman@lightlink.com>2007-10-09 22:56:32 -0400
commit57c7c3a0fdea95eddcaeba31e7ca7dfc917682ab (patch)
treed315854afedf0fbb5b6d83849ed2c8c27c153b7f /drivers/hwmon
parentc940336b4403540c498fceb102c7142799252129 (diff)
hwmon: IBM power meter driver
Add a driver to export IBM PowerExecutive power meter sensors. Signed-off-by: Darrick J. Wong <djwong@us.ibm.com> Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig13
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ibmpex.c608
3 files changed, 622 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 52511742b335..b8854b94807f 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -295,6 +295,19 @@ config SENSORS_CORETEMP
295 sensor inside your CPU. Supported all are all known variants 295 sensor inside your CPU. Supported all are all known variants
296 of Intel Core family. 296 of Intel Core family.
297 297
298config SENSORS_IBMPEX
299 tristate "IBM PowerExecutive temperature/power sensors"
300 select IPMI_SI
301 depends on IPMI_HANDLER
302 help
303 If you say yes here you get support for the temperature and
304 power sensors in various IBM System X servers that support
305 PowerExecutive. So far this includes the x3550, x3650, x3655,
306 x3755, and certain HS20 blades.
307
308 This driver can also be built as a module. If so, the module
309 will be called ibmpex.
310
298config SENSORS_IT87 311config SENSORS_IT87
299 tristate "ITE IT87xx and compatibles" 312 tristate "ITE IT87xx and compatibles"
300 select HWMON_VID 313 select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 19da12ec140c..2f592e2e01b3 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
37obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o 37obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
38obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o 38obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
39obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o 39obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
40obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
40obj-$(CONFIG_SENSORS_IT87) += it87.o 41obj-$(CONFIG_SENSORS_IT87) += it87.o
41obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 42obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
42obj-$(CONFIG_SENSORS_LM63) += lm63.o 43obj-$(CONFIG_SENSORS_LM63) += lm63.o
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 000000000000..fe2c2616c6b1
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,608 @@
1/*
2 * A hwmon driver for the IBM PowerExecutive temperature/power sensors
3 * Copyright (C) 2007 IBM
4 *
5 * Author: Darrick J. Wong <djwong@us.ibm.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/ipmi.h>
23#include <linux/module.h>
24#include <linux/hwmon.h>
25#include <linux/hwmon-sysfs.h>
26#include <linux/jiffies.h>
27#include <linux/mutex.h>
28
29#define REFRESH_INTERVAL (2 * HZ)
30#define DRVNAME "ibmpex"
31
32#define PEX_GET_VERSION 1
33#define PEX_GET_SENSOR_COUNT 2
34#define PEX_GET_SENSOR_NAME 3
35#define PEX_RESET_HIGH_LOW 4
36#define PEX_GET_SENSOR_DATA 6
37
38#define PEX_NET_FUNCTION 0x3A
39#define PEX_COMMAND 0x3C
40
41static inline u16 extract_value(const char *data, int offset)
42{
43 return be16_to_cpup((u16 *)&data[offset]);
44}
45
46#define TEMP_SENSOR 1
47#define POWER_SENSOR 2
48
49#define PEX_SENSOR_TYPE_LEN 3
50static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
51static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D};
52
53#define PEX_MULT_LEN 2
54static u8 const watt_sensor_sig[] = {0x41, 0x43};
55
56#define PEX_NUM_SENSOR_FUNCS 3
57static char const * const power_sensor_name_templates[] = {
58 "%s%d_average",
59 "%s%d_average_lowest",
60 "%s%d_average_highest"
61};
62static char const * const temp_sensor_name_templates[] = {
63 "%s%d_input",
64 "%s%d_input_lowest",
65 "%s%d_input_highest"
66};
67
68static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
69static void ibmpex_register_bmc(int iface, struct device *dev);
70static void ibmpex_bmc_gone(int iface);
71
72struct ibmpex_sensor_data {
73 int in_use;
74 s16 values[PEX_NUM_SENSOR_FUNCS];
75 int multiplier;
76
77 struct sensor_device_attribute_2 attr[PEX_NUM_SENSOR_FUNCS];
78};
79
80struct ibmpex_bmc_data {
81 struct list_head list;
82 struct device *hwmon_dev;
83 struct device *bmc_device;
84 struct mutex lock;
85 char valid;
86 unsigned long last_updated; /* In jiffies */
87
88 struct ipmi_addr address;
89 struct completion read_complete;
90 ipmi_user_t user;
91 int interface;
92
93 struct kernel_ipmi_msg tx_message;
94 unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH];
95 long tx_msgid;
96
97 unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH];
98 unsigned long rx_msg_len;
99 unsigned char rx_result;
100 int rx_recv_type;
101
102 unsigned char sensor_major;
103 unsigned char sensor_minor;
104
105 unsigned char num_sensors;
106 struct ibmpex_sensor_data *sensors;
107};
108
109struct ibmpex_driver_data {
110 struct list_head bmc_data;
111 struct ipmi_smi_watcher bmc_events;
112 struct ipmi_user_hndl ipmi_hndlrs;
113};
114
115static struct ibmpex_driver_data driver_data = {
116 .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
117 .bmc_events = {
118 .owner = THIS_MODULE,
119 .new_smi = ibmpex_register_bmc,
120 .smi_gone = ibmpex_bmc_gone,
121 },
122 .ipmi_hndlrs = {
123 .ipmi_recv_hndl = ibmpex_msg_handler,
124 },
125};
126
127static int ibmpex_send_message(struct ibmpex_bmc_data *data)
128{
129 int err;
130
131 err = ipmi_validate_addr(&data->address, sizeof(data->address));
132 if (err)
133 goto out;
134
135 data->tx_msgid++;
136 err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
137 &data->tx_message, data, 0, 0, 0);
138 if (err)
139 goto out1;
140
141 return 0;
142out1:
143 printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
144 return err;
145out:
146 printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
147 return err;
148}
149
150static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
151{
152 data->tx_msg_data[0] = PEX_GET_VERSION;
153 data->tx_message.data_len = 1;
154 ibmpex_send_message(data);
155
156 wait_for_completion(&data->read_complete);
157
158 if (data->rx_result || data->rx_msg_len != 6)
159 return -ENOENT;
160
161 data->sensor_major = data->rx_msg_data[0];
162 data->sensor_minor = data->rx_msg_data[1];
163
164 printk(KERN_INFO DRVNAME ": Found BMC with sensor interface "
165 "v%d.%d %d-%02d-%02d on interface %d\n",
166 data->sensor_major,
167 data->sensor_minor,
168 extract_value(data->rx_msg_data, 2),
169 data->rx_msg_data[4],
170 data->rx_msg_data[5],
171 data->interface);
172
173 return 0;
174}
175
176static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
177{
178 data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
179 data->tx_message.data_len = 1;
180 ibmpex_send_message(data);
181
182 wait_for_completion(&data->read_complete);
183
184 if (data->rx_result || data->rx_msg_len != 1)
185 return -ENOENT;
186
187 return data->rx_msg_data[0];
188}
189
190static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
191{
192 data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
193 data->tx_msg_data[1] = sensor;
194 data->tx_message.data_len = 2;
195 ibmpex_send_message(data);
196
197 wait_for_completion(&data->read_complete);
198
199 if (data->rx_result || data->rx_msg_len < 1)
200 return -ENOENT;
201
202 return 0;
203}
204
205static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
206{
207 data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
208 data->tx_msg_data[1] = sensor;
209 data->tx_message.data_len = 2;
210 ibmpex_send_message(data);
211
212 wait_for_completion(&data->read_complete);
213
214 if (data->rx_result || data->rx_msg_len < 26) {
215 printk(KERN_ERR "Error reading sensor %d, please check.\n",
216 sensor);
217 return -ENOENT;
218 }
219
220 return 0;
221}
222
223static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
224{
225 data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
226 data->tx_message.data_len = 1;
227 ibmpex_send_message(data);
228
229 wait_for_completion(&data->read_complete);
230
231 return 0;
232}
233
234static void ibmpex_update_device(struct ibmpex_bmc_data *data)
235{
236 int i, err;
237
238 mutex_lock(&data->lock);
239 if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
240 data->valid)
241 goto out;
242
243 for (i = 0; i < data->num_sensors; i++) {
244 if (!data->sensors[i].in_use)
245 continue;
246 err = ibmpex_query_sensor_data(data, i);
247 if (err)
248 continue;
249 data->sensors[i].values[0] =
250 extract_value(data->rx_msg_data, 16);
251 data->sensors[i].values[1] =
252 extract_value(data->rx_msg_data, 18);
253 data->sensors[i].values[2] =
254 extract_value(data->rx_msg_data, 20);
255 }
256
257 data->last_updated = jiffies;
258 data->valid = 1;
259
260out:
261 mutex_unlock(&data->lock);
262}
263
264static struct ibmpex_bmc_data *get_bmc_data(int iface)
265{
266 struct ibmpex_bmc_data *p, *next;
267
268 list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
269 if (p->interface == iface)
270 return p;
271
272 return NULL;
273}
274
275static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
276 char *buf)
277{
278 return sprintf(buf, "%s\n", DRVNAME);
279}
280static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
281
282static ssize_t ibmpex_show_sensor(struct device *dev,
283 struct device_attribute *devattr,
284 char *buf)
285{
286 struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
287 struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
288 int mult = data->sensors[attr->index].multiplier;
289 ibmpex_update_device(data);
290
291 return sprintf(buf, "%d\n",
292 data->sensors[attr->index].values[attr->nr] * mult);
293}
294
295static ssize_t ibmpex_reset_high_low(struct device *dev,
296 struct device_attribute *devattr,
297 const char *buf,
298 size_t count)
299{
300 struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
301
302 ibmpex_reset_high_low_data(data);
303
304 return count;
305}
306
307static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
308 ibmpex_reset_high_low, 0);
309
310static int is_power_sensor(const char *sensor_id, int len)
311{
312 if (len < PEX_SENSOR_TYPE_LEN)
313 return 0;
314
315 if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
316 return 1;
317 return 0;
318}
319
320static int is_temp_sensor(const char *sensor_id, int len)
321{
322 if (len < PEX_SENSOR_TYPE_LEN)
323 return 0;
324
325 if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
326 return 1;
327 return 0;
328}
329
330static int power_sensor_multiplier(const char *sensor_id, int len)
331{
332 int i;
333
334 for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
335 if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
336 return 1000000;
337
338 return 100000;
339}
340
341static int create_sensor(struct ibmpex_bmc_data *data, int type,
342 int counter, int sensor, int func)
343{
344 int err;
345 char *n;
346
347 n = kmalloc(32, GFP_KERNEL);
348 if (!n)
349 return -ENOMEM;
350
351 if (type == TEMP_SENSOR)
352 sprintf(n, temp_sensor_name_templates[func], "temp", counter);
353 else if (type == POWER_SENSOR)
354 sprintf(n, power_sensor_name_templates[func], "power", counter);
355
356 data->sensors[sensor].attr[func].dev_attr.attr.name = n;
357 data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;
358 data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
359 data->sensors[sensor].attr[func].index = sensor;
360 data->sensors[sensor].attr[func].nr = func;
361
362 err = device_create_file(data->bmc_device,
363 &data->sensors[sensor].attr[func].dev_attr);
364 if (err) {
365 data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
366 kfree(n);
367 return err;
368 }
369
370 return 0;
371}
372
373static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
374{
375 int i, j, err;
376 int sensor_type;
377 int sensor_counter;
378 int num_power = 0;
379 int num_temp = 0;
380
381 err = ibmpex_query_sensor_count(data);
382 if (err <= 0)
383 return -ENOENT;
384 data->num_sensors = err;
385
386 data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors),
387 GFP_KERNEL);
388 if (!data->sensors)
389 return -ENOMEM;
390
391 for (i = 0; i < data->num_sensors; i++) {
392 err = ibmpex_query_sensor_name(data, i);
393 if (err)
394 continue;
395
396 if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
397 sensor_type = POWER_SENSOR;
398 num_power++;
399 sensor_counter = num_power;
400 data->sensors[i].multiplier =
401 power_sensor_multiplier(data->rx_msg_data,
402 data->rx_msg_len);
403 } else if (is_temp_sensor(data->rx_msg_data,
404 data->rx_msg_len)) {
405 sensor_type = TEMP_SENSOR;
406 num_temp++;
407 sensor_counter = num_temp;
408 data->sensors[i].multiplier = 1;
409 } else
410 continue;
411
412 data->sensors[i].in_use = 1;
413
414 /* Create attributes */
415 for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
416 err = create_sensor(data, sensor_type, sensor_counter,
417 i, j);
418 if (err)
419 goto exit_remove;
420 }
421 }
422
423 err = device_create_file(data->bmc_device,
424 &sensor_dev_attr_reset_high_low.dev_attr);
425 if (err)
426 goto exit_remove;
427
428 err = device_create_file(data->bmc_device,
429 &sensor_dev_attr_name.dev_attr);
430 if (err)
431 goto exit_remove;
432
433 return 0;
434
435exit_remove:
436 device_remove_file(data->bmc_device,
437 &sensor_dev_attr_reset_high_low.dev_attr);
438 device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
439 for (i = 0; i < data->num_sensors; i++)
440 for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
441 if (!data->sensors[i].attr[j].dev_attr.attr.name)
442 continue;
443 device_remove_file(data->bmc_device,
444 &data->sensors[i].attr[j].dev_attr);
445 kfree(data->sensors[i].attr[j].dev_attr.attr.name);
446 }
447
448 kfree(data->sensors);
449 return err;
450}
451
452static void ibmpex_register_bmc(int iface, struct device *dev)
453{
454 struct ibmpex_bmc_data *data;
455 int err;
456
457 data = kzalloc(sizeof(*data), GFP_KERNEL);
458 if (!data) {
459 printk(KERN_ERR DRVNAME ": Insufficient memory for BMC "
460 "interface %d.\n", data->interface);
461 return;
462 }
463
464 data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
465 data->address.channel = IPMI_BMC_CHANNEL;
466 data->address.data[0] = 0;
467 data->interface = iface;
468 data->bmc_device = dev;
469
470 /* Create IPMI messaging interface user */
471 err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
472 data, &data->user);
473 if (err < 0) {
474 printk(KERN_ERR DRVNAME ": Error, unable to register user with "
475 "ipmi interface %d\n",
476 data->interface);
477 goto out;
478 }
479
480 mutex_init(&data->lock);
481
482 /* Initialize message */
483 data->tx_msgid = 0;
484 init_completion(&data->read_complete);
485 data->tx_message.netfn = PEX_NET_FUNCTION;
486 data->tx_message.cmd = PEX_COMMAND;
487 data->tx_message.data = data->tx_msg_data;
488
489 /* Does this BMC support PowerExecutive? */
490 err = ibmpex_ver_check(data);
491 if (err)
492 goto out_user;
493
494 /* Register the BMC as a HWMON class device */
495 data->hwmon_dev = hwmon_device_register(data->bmc_device);
496
497 if (IS_ERR(data->hwmon_dev)) {
498 printk(KERN_ERR DRVNAME ": Error, unable to register hwmon "
499 "class device for interface %d\n",
500 data->interface);
501 kfree(data);
502 return;
503 }
504
505 /* finally add the new bmc data to the bmc data list */
506 dev_set_drvdata(dev, data);
507 list_add_tail(&data->list, &driver_data.bmc_data);
508
509 /* Now go find all the sensors */
510 err = ibmpex_find_sensors(data);
511 if (err) {
512 printk(KERN_ERR "Error %d allocating memory\n", err);
513 goto out_register;
514 }
515
516 return;
517
518out_register:
519 hwmon_device_unregister(data->hwmon_dev);
520out_user:
521 ipmi_destroy_user(data->user);
522out:
523 kfree(data);
524}
525
526static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
527{
528 int i, j;
529
530 device_remove_file(data->bmc_device,
531 &sensor_dev_attr_reset_high_low.dev_attr);
532 device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
533 for (i = 0; i < data->num_sensors; i++)
534 for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
535 if (!data->sensors[i].attr[j].dev_attr.attr.name)
536 continue;
537 device_remove_file(data->bmc_device,
538 &data->sensors[i].attr[j].dev_attr);
539 kfree(data->sensors[i].attr[j].dev_attr.attr.name);
540 }
541
542 list_del(&data->list);
543 dev_set_drvdata(data->bmc_device, NULL);
544 hwmon_device_unregister(data->hwmon_dev);
545 ipmi_destroy_user(data->user);
546 kfree(data->sensors);
547 kfree(data);
548}
549
550static void ibmpex_bmc_gone(int iface)
551{
552 struct ibmpex_bmc_data *data = get_bmc_data(iface);
553
554 if (!data)
555 return;
556
557 ibmpex_bmc_delete(data);
558}
559
560static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
561{
562 struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
563
564 if (msg->msgid != data->tx_msgid) {
565 printk(KERN_ERR "Received msgid (%02x) and transmitted "
566 "msgid (%02x) mismatch!\n",
567 (int)msg->msgid,
568 (int)data->tx_msgid);
569 ipmi_free_recv_msg(msg);
570 return;
571 }
572
573 data->rx_recv_type = msg->recv_type;
574 if (msg->msg.data_len > 0)
575 data->rx_result = msg->msg.data[0];
576 else
577 data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
578
579 if (msg->msg.data_len > 1) {
580 data->rx_msg_len = msg->msg.data_len - 1;
581 memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
582 } else
583 data->rx_msg_len = 0;
584
585 ipmi_free_recv_msg(msg);
586 complete(&data->read_complete);
587}
588
589static int __init ibmpex_init(void)
590{
591 return ipmi_smi_watcher_register(&driver_data.bmc_events);
592}
593
594static void __exit ibmpex_exit(void)
595{
596 struct ibmpex_bmc_data *p, *next;
597
598 ipmi_smi_watcher_unregister(&driver_data.bmc_events);
599 list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
600 ibmpex_bmc_delete(p);
601}
602
603MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
604MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
605MODULE_LICENSE("GPL");
606
607module_init(ibmpex_init);
608module_exit(ibmpex_exit);