aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIra W. Snyder <iws@ovro.caltech.edu>2010-08-14 15:08:49 -0400
committerJean Delvare <khali@linux-fr.org>2010-08-14 15:08:49 -0400
commit5950ec8d3e47a08ec0b678a0e0ba5d1b9b62dd8e (patch)
tree8cac7d3dadf50be14b2ac0441e61ecd8935f27e0
parentb9783dcebe952bf73449fe70a19ee4814adc81a0 (diff)
hwmon: (ltc4245) Expose all GPIO pins as analog voltages
Add support for exposing all GPIO pins as analog voltages. Though this is not an ideal use of the chip, some hardware engineers may decide that the LTC4245 meets their design requirements when studying the datasheet. The GPIO pins are sampled in round-robin fashion, meaning that a slow reader will see stale data. A userspace application can detect this, because it will get -EAGAIN when reading from a sysfs file which contains stale data. Users can choose to use this feature on a per-chip basis by using either platform data or the OF device tree (where applicable). Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu> Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r--Documentation/hwmon/ltc424524
-rw-r--r--drivers/hwmon/ltc4245.c177
-rw-r--r--include/linux/i2c/ltc4245.h21
3 files changed, 211 insertions, 11 deletions
diff --git a/Documentation/hwmon/ltc4245 b/Documentation/hwmon/ltc4245
index 86b5880d8502..b478b0864965 100644
--- a/Documentation/hwmon/ltc4245
+++ b/Documentation/hwmon/ltc4245
@@ -72,9 +72,31 @@ in6_min_alarm 5v output undervoltage alarm
72in7_min_alarm 3v output undervoltage alarm 72in7_min_alarm 3v output undervoltage alarm
73in8_min_alarm Vee (-12v) output undervoltage alarm 73in8_min_alarm Vee (-12v) output undervoltage alarm
74 74
75in9_input GPIO voltage data 75in9_input GPIO voltage data (see note 1)
76in10_input GPIO voltage data (see note 1)
77in11_input GPIO voltage data (see note 1)
76 78
77power1_input 12v power usage (mW) 79power1_input 12v power usage (mW)
78power2_input 5v power usage (mW) 80power2_input 5v power usage (mW)
79power3_input 3v power usage (mW) 81power3_input 3v power usage (mW)
80power4_input Vee (-12v) power usage (mW) 82power4_input Vee (-12v) power usage (mW)
83
84
85Note 1
86------
87
88If you have NOT configured the driver to sample all GPIO pins as analog
89voltages, then the in10_input and in11_input sysfs attributes will not be
90created. The driver will sample the GPIO pin that is currently connected to the
91ADC as an analog voltage, and report the value in in9_input.
92
93If you have configured the driver to sample all GPIO pins as analog voltages,
94then they will be sampled in round-robin fashion. If userspace reads too
95slowly, -EAGAIN will be returned when you read the sysfs attribute containing
96the sensor reading.
97
98The LTC4245 chip can be configured to sample all GPIO pins with two methods:
991) platform data -- see include/linux/i2c/ltc4245.h
1002) OF device tree -- add the "ltc4245,use-extra-gpios" property to each chip
101
102The default mode of operation is to sample a single GPIO pin.
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 21d201befc2c..659308329308 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -21,6 +21,7 @@
21#include <linux/i2c.h> 21#include <linux/i2c.h>
22#include <linux/hwmon.h> 22#include <linux/hwmon.h>
23#include <linux/hwmon-sysfs.h> 23#include <linux/hwmon-sysfs.h>
24#include <linux/i2c/ltc4245.h>
24 25
25/* Here are names of the chip's registers (a.k.a. commands) */ 26/* Here are names of the chip's registers (a.k.a. commands) */
26enum ltc4245_cmd { 27enum ltc4245_cmd {
@@ -60,8 +61,72 @@ struct ltc4245_data {
60 61
61 /* Voltage registers */ 62 /* Voltage registers */
62 u8 vregs[0x0d]; 63 u8 vregs[0x0d];
64
65 /* GPIO ADC registers */
66 bool use_extra_gpios;
67 int gpios[3];
63}; 68};
64 69
70/*
71 * Update the readings from the GPIO pins. If the driver has been configured to
72 * sample all GPIO's as analog voltages, a round-robin sampling method is used.
73 * Otherwise, only the configured GPIO pin is sampled.
74 *
75 * LOCKING: must hold data->update_lock
76 */
77static void ltc4245_update_gpios(struct device *dev)
78{
79 struct i2c_client *client = to_i2c_client(dev);
80 struct ltc4245_data *data = i2c_get_clientdata(client);
81 u8 gpio_curr, gpio_next, gpio_reg;
82 int i;
83
84 /* no extra gpio support, we're basically done */
85 if (!data->use_extra_gpios) {
86 data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10];
87 return;
88 }
89
90 /*
91 * If the last reading was too long ago, then we mark all old GPIO
92 * readings as stale by setting them to -EAGAIN
93 */
94 if (time_after(jiffies, data->last_updated + 5 * HZ)) {
95 dev_dbg(&client->dev, "Marking GPIOs invalid\n");
96 for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
97 data->gpios[i] = -EAGAIN;
98 }
99
100 /*
101 * Get the current GPIO pin
102 *
103 * The datasheet calls these GPIO[1-3], but we'll calculate the zero
104 * based array index instead, and call them GPIO[0-2]. This is much
105 * easier to think about.
106 */
107 gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6;
108 if (gpio_curr > 0)
109 gpio_curr -= 1;
110
111 /* Read the GPIO voltage from the GPIOADC register */
112 data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10];
113
114 /* Find the next GPIO pin to read */
115 gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios);
116
117 /*
118 * Calculate the correct setting for the GPIO register so it will
119 * sample the next GPIO pin
120 */
121 gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6);
122
123 /* Update the GPIO register */
124 i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg);
125
126 /* Update saved data */
127 data->cregs[LTC4245_GPIO] = gpio_reg;
128}
129
65static struct ltc4245_data *ltc4245_update_device(struct device *dev) 130static struct ltc4245_data *ltc4245_update_device(struct device *dev)
66{ 131{
67 struct i2c_client *client = to_i2c_client(dev); 132 struct i2c_client *client = to_i2c_client(dev);
@@ -93,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
93 data->vregs[i] = val; 158 data->vregs[i] = val;
94 } 159 }
95 160
161 /* Update GPIO readings */
162 ltc4245_update_gpios(dev);
163
96 data->last_updated = jiffies; 164 data->last_updated = jiffies;
97 data->valid = 1; 165 data->valid = 1;
98 } 166 }
@@ -233,6 +301,22 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
233 return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); 301 return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
234} 302}
235 303
304static ssize_t ltc4245_show_gpio(struct device *dev,
305 struct device_attribute *da,
306 char *buf)
307{
308 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
309 struct ltc4245_data *data = ltc4245_update_device(dev);
310 int val = data->gpios[attr->index];
311
312 /* handle stale GPIO's */
313 if (val < 0)
314 return val;
315
316 /* Convert to millivolts and print */
317 return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
318}
319
236/* These macros are used below in constructing device attribute objects 320/* These macros are used below in constructing device attribute objects
237 * for use with sysfs_create_group() to make a sysfs device file 321 * for use with sysfs_create_group() to make a sysfs device file
238 * for each register. 322 * for each register.
@@ -254,6 +338,10 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
254 static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ 338 static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
255 ltc4245_show_alarm, NULL, (mask), reg) 339 ltc4245_show_alarm, NULL, (mask), reg)
256 340
341#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
342 static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
343 ltc4245_show_gpio, NULL, gpio_num)
344
257/* Construct a sensor_device_attribute structure for each register */ 345/* Construct a sensor_device_attribute structure for each register */
258 346
259/* Input voltages */ 347/* Input voltages */
@@ -293,7 +381,9 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
293LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); 381LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
294 382
295/* GPIO voltages */ 383/* GPIO voltages */
296LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC); 384LTC4245_GPIO_VOLTAGE(in9_input, 0);
385LTC4245_GPIO_VOLTAGE(in10_input, 1);
386LTC4245_GPIO_VOLTAGE(in11_input, 2);
297 387
298/* Power Consumption (virtual) */ 388/* Power Consumption (virtual) */
299LTC4245_POWER(power1_input, LTC4245_12VSENSE); 389LTC4245_POWER(power1_input, LTC4245_12VSENSE);
@@ -304,7 +394,7 @@ LTC4245_POWER(power4_input, LTC4245_VEESENSE);
304/* Finally, construct an array of pointers to members of the above objects, 394/* Finally, construct an array of pointers to members of the above objects,
305 * as required for sysfs_create_group() 395 * as required for sysfs_create_group()
306 */ 396 */
307static struct attribute *ltc4245_attributes[] = { 397static struct attribute *ltc4245_std_attributes[] = {
308 &sensor_dev_attr_in1_input.dev_attr.attr, 398 &sensor_dev_attr_in1_input.dev_attr.attr,
309 &sensor_dev_attr_in2_input.dev_attr.attr, 399 &sensor_dev_attr_in2_input.dev_attr.attr,
310 &sensor_dev_attr_in3_input.dev_attr.attr, 400 &sensor_dev_attr_in3_input.dev_attr.attr,
@@ -345,10 +435,77 @@ static struct attribute *ltc4245_attributes[] = {
345 NULL, 435 NULL,
346}; 436};
347 437
348static const struct attribute_group ltc4245_group = { 438static struct attribute *ltc4245_gpio_attributes[] = {
349 .attrs = ltc4245_attributes, 439 &sensor_dev_attr_in10_input.dev_attr.attr,
440 &sensor_dev_attr_in11_input.dev_attr.attr,
441 NULL,
442};
443
444static const struct attribute_group ltc4245_std_group = {
445 .attrs = ltc4245_std_attributes,
446};
447
448static const struct attribute_group ltc4245_gpio_group = {
449 .attrs = ltc4245_gpio_attributes,
350}; 450};
351 451
452static int ltc4245_sysfs_create_groups(struct i2c_client *client)
453{
454 struct ltc4245_data *data = i2c_get_clientdata(client);
455 struct device *dev = &client->dev;
456 int ret;
457
458 /* register the standard sysfs attributes */
459 ret = sysfs_create_group(&dev->kobj, &ltc4245_std_group);
460 if (ret) {
461 dev_err(dev, "unable to register standard attributes\n");
462 return ret;
463 }
464
465 /* if we're using the extra gpio support, register it's attributes */
466 if (data->use_extra_gpios) {
467 ret = sysfs_create_group(&dev->kobj, &ltc4245_gpio_group);
468 if (ret) {
469 dev_err(dev, "unable to register gpio attributes\n");
470 sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
471 return ret;
472 }
473 }
474
475 return 0;
476}
477
478static void ltc4245_sysfs_remove_groups(struct i2c_client *client)
479{
480 struct ltc4245_data *data = i2c_get_clientdata(client);
481 struct device *dev = &client->dev;
482
483 if (data->use_extra_gpios)
484 sysfs_remove_group(&dev->kobj, &ltc4245_gpio_group);
485
486 sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
487}
488
489static bool ltc4245_use_extra_gpios(struct i2c_client *client)
490{
491 struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
492#ifdef CONFIG_OF
493 struct device_node *np = client->dev.of_node;
494#endif
495
496 /* prefer platform data */
497 if (pdata)
498 return pdata->use_extra_gpios;
499
500#ifdef CONFIG_OF
501 /* fallback on OF */
502 if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
503 return true;
504#endif
505
506 return false;
507}
508
352static int ltc4245_probe(struct i2c_client *client, 509static int ltc4245_probe(struct i2c_client *client,
353 const struct i2c_device_id *id) 510 const struct i2c_device_id *id)
354{ 511{
@@ -367,15 +524,16 @@ static int ltc4245_probe(struct i2c_client *client,
367 524
368 i2c_set_clientdata(client, data); 525 i2c_set_clientdata(client, data);
369 mutex_init(&data->update_lock); 526 mutex_init(&data->update_lock);
527 data->use_extra_gpios = ltc4245_use_extra_gpios(client);
370 528
371 /* Initialize the LTC4245 chip */ 529 /* Initialize the LTC4245 chip */
372 i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); 530 i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
373 i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); 531 i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
374 532
375 /* Register sysfs hooks */ 533 /* Register sysfs hooks */
376 ret = sysfs_create_group(&client->dev.kobj, &ltc4245_group); 534 ret = ltc4245_sysfs_create_groups(client);
377 if (ret) 535 if (ret)
378 goto out_sysfs_create_group; 536 goto out_sysfs_create_groups;
379 537
380 data->hwmon_dev = hwmon_device_register(&client->dev); 538 data->hwmon_dev = hwmon_device_register(&client->dev);
381 if (IS_ERR(data->hwmon_dev)) { 539 if (IS_ERR(data->hwmon_dev)) {
@@ -386,8 +544,8 @@ static int ltc4245_probe(struct i2c_client *client,
386 return 0; 544 return 0;
387 545
388out_hwmon_device_register: 546out_hwmon_device_register:
389 sysfs_remove_group(&client->dev.kobj, &ltc4245_group); 547 ltc4245_sysfs_remove_groups(client);
390out_sysfs_create_group: 548out_sysfs_create_groups:
391 kfree(data); 549 kfree(data);
392out_kzalloc: 550out_kzalloc:
393 return ret; 551 return ret;
@@ -398,8 +556,7 @@ static int ltc4245_remove(struct i2c_client *client)
398 struct ltc4245_data *data = i2c_get_clientdata(client); 556 struct ltc4245_data *data = i2c_get_clientdata(client);
399 557
400 hwmon_device_unregister(data->hwmon_dev); 558 hwmon_device_unregister(data->hwmon_dev);
401 sysfs_remove_group(&client->dev.kobj, &ltc4245_group); 559 ltc4245_sysfs_remove_groups(client);
402
403 kfree(data); 560 kfree(data);
404 561
405 return 0; 562 return 0;
diff --git a/include/linux/i2c/ltc4245.h b/include/linux/i2c/ltc4245.h
new file mode 100644
index 000000000000..56bda4be0016
--- /dev/null
+++ b/include/linux/i2c/ltc4245.h
@@ -0,0 +1,21 @@
1/*
2 * Platform Data for LTC4245 hardware monitor chip
3 *
4 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#ifndef LINUX_LTC4245_H
13#define LINUX_LTC4245_H
14
15#include <linux/types.h>
16
17struct ltc4245_platform_data {
18 bool use_extra_gpios;
19};
20
21#endif /* LINUX_LTC4245_H */