Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ira W. Snyder and committed by
Jean Delvare
5950ec8d b9783dce

+211 -11
+23 -1
Documentation/hwmon/ltc4245
··· 72 72 in7_min_alarm 3v output undervoltage alarm 73 73 in8_min_alarm Vee (-12v) output undervoltage alarm 74 74 75 - in9_input GPIO voltage data 75 + in9_input GPIO voltage data (see note 1) 76 + in10_input GPIO voltage data (see note 1) 77 + in11_input GPIO voltage data (see note 1) 76 78 77 79 power1_input 12v power usage (mW) 78 80 power2_input 5v power usage (mW) 79 81 power3_input 3v power usage (mW) 80 82 power4_input Vee (-12v) power usage (mW) 83 + 84 + 85 + Note 1 86 + ------ 87 + 88 + If you have NOT configured the driver to sample all GPIO pins as analog 89 + voltages, then the in10_input and in11_input sysfs attributes will not be 90 + created. The driver will sample the GPIO pin that is currently connected to the 91 + ADC as an analog voltage, and report the value in in9_input. 92 + 93 + If you have configured the driver to sample all GPIO pins as analog voltages, 94 + then they will be sampled in round-robin fashion. If userspace reads too 95 + slowly, -EAGAIN will be returned when you read the sysfs attribute containing 96 + the sensor reading. 97 + 98 + The LTC4245 chip can be configured to sample all GPIO pins with two methods: 99 + 1) platform data -- see include/linux/i2c/ltc4245.h 100 + 2) OF device tree -- add the "ltc4245,use-extra-gpios" property to each chip 101 + 102 + The default mode of operation is to sample a single GPIO pin.
+167 -10
drivers/hwmon/ltc4245.c
··· 21 21 #include <linux/i2c.h> 22 22 #include <linux/hwmon.h> 23 23 #include <linux/hwmon-sysfs.h> 24 + #include <linux/i2c/ltc4245.h> 24 25 25 26 /* Here are names of the chip's registers (a.k.a. commands) */ 26 27 enum ltc4245_cmd { ··· 61 60 62 61 /* Voltage registers */ 63 62 u8 vregs[0x0d]; 63 + 64 + /* GPIO ADC registers */ 65 + bool use_extra_gpios; 66 + int gpios[3]; 64 67 }; 68 + 69 + /* 70 + * Update the readings from the GPIO pins. If the driver has been configured to 71 + * sample all GPIO's as analog voltages, a round-robin sampling method is used. 72 + * Otherwise, only the configured GPIO pin is sampled. 73 + * 74 + * LOCKING: must hold data->update_lock 75 + */ 76 + static void ltc4245_update_gpios(struct device *dev) 77 + { 78 + struct i2c_client *client = to_i2c_client(dev); 79 + struct ltc4245_data *data = i2c_get_clientdata(client); 80 + u8 gpio_curr, gpio_next, gpio_reg; 81 + int i; 82 + 83 + /* no extra gpio support, we're basically done */ 84 + if (!data->use_extra_gpios) { 85 + data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10]; 86 + return; 87 + } 88 + 89 + /* 90 + * If the last reading was too long ago, then we mark all old GPIO 91 + * readings as stale by setting them to -EAGAIN 92 + */ 93 + if (time_after(jiffies, data->last_updated + 5 * HZ)) { 94 + dev_dbg(&client->dev, "Marking GPIOs invalid\n"); 95 + for (i = 0; i < ARRAY_SIZE(data->gpios); i++) 96 + data->gpios[i] = -EAGAIN; 97 + } 98 + 99 + /* 100 + * Get the current GPIO pin 101 + * 102 + * The datasheet calls these GPIO[1-3], but we'll calculate the zero 103 + * based array index instead, and call them GPIO[0-2]. This is much 104 + * easier to think about. 105 + */ 106 + gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6; 107 + if (gpio_curr > 0) 108 + gpio_curr -= 1; 109 + 110 + /* Read the GPIO voltage from the GPIOADC register */ 111 + data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10]; 112 + 113 + /* Find the next GPIO pin to read */ 114 + gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios); 115 + 116 + /* 117 + * Calculate the correct setting for the GPIO register so it will 118 + * sample the next GPIO pin 119 + */ 120 + gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6); 121 + 122 + /* Update the GPIO register */ 123 + i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg); 124 + 125 + /* Update saved data */ 126 + data->cregs[LTC4245_GPIO] = gpio_reg; 127 + } 65 128 66 129 static struct ltc4245_data *ltc4245_update_device(struct device *dev) 67 130 { ··· 157 92 else 158 93 data->vregs[i] = val; 159 94 } 95 + 96 + /* Update GPIO readings */ 97 + ltc4245_update_gpios(dev); 160 98 161 99 data->last_updated = jiffies; 162 100 data->valid = 1; ··· 301 233 return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); 302 234 } 303 235 236 + static ssize_t ltc4245_show_gpio(struct device *dev, 237 + struct device_attribute *da, 238 + char *buf) 239 + { 240 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 241 + struct ltc4245_data *data = ltc4245_update_device(dev); 242 + int val = data->gpios[attr->index]; 243 + 244 + /* handle stale GPIO's */ 245 + if (val < 0) 246 + return val; 247 + 248 + /* Convert to millivolts and print */ 249 + return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); 250 + } 251 + 304 252 /* These macros are used below in constructing device attribute objects 305 253 * for use with sysfs_create_group() to make a sysfs device file 306 254 * for each register. ··· 337 253 #define LTC4245_ALARM(name, mask, reg) \ 338 254 static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ 339 255 ltc4245_show_alarm, NULL, (mask), reg) 256 + 257 + #define LTC4245_GPIO_VOLTAGE(name, gpio_num) \ 258 + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ 259 + ltc4245_show_gpio, NULL, gpio_num) 340 260 341 261 /* Construct a sensor_device_attribute structure for each register */ 342 262 ··· 381 293 LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); 382 294 383 295 /* GPIO voltages */ 384 - LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC); 296 + LTC4245_GPIO_VOLTAGE(in9_input, 0); 297 + LTC4245_GPIO_VOLTAGE(in10_input, 1); 298 + LTC4245_GPIO_VOLTAGE(in11_input, 2); 385 299 386 300 /* Power Consumption (virtual) */ 387 301 LTC4245_POWER(power1_input, LTC4245_12VSENSE); ··· 394 304 /* Finally, construct an array of pointers to members of the above objects, 395 305 * as required for sysfs_create_group() 396 306 */ 397 - static struct attribute *ltc4245_attributes[] = { 307 + static struct attribute *ltc4245_std_attributes[] = { 398 308 &sensor_dev_attr_in1_input.dev_attr.attr, 399 309 &sensor_dev_attr_in2_input.dev_attr.attr, 400 310 &sensor_dev_attr_in3_input.dev_attr.attr, ··· 435 345 NULL, 436 346 }; 437 347 438 - static const struct attribute_group ltc4245_group = { 439 - .attrs = ltc4245_attributes, 348 + static struct attribute *ltc4245_gpio_attributes[] = { 349 + &sensor_dev_attr_in10_input.dev_attr.attr, 350 + &sensor_dev_attr_in11_input.dev_attr.attr, 351 + NULL, 440 352 }; 353 + 354 + static const struct attribute_group ltc4245_std_group = { 355 + .attrs = ltc4245_std_attributes, 356 + }; 357 + 358 + static const struct attribute_group ltc4245_gpio_group = { 359 + .attrs = ltc4245_gpio_attributes, 360 + }; 361 + 362 + static int ltc4245_sysfs_create_groups(struct i2c_client *client) 363 + { 364 + struct ltc4245_data *data = i2c_get_clientdata(client); 365 + struct device *dev = &client->dev; 366 + int ret; 367 + 368 + /* register the standard sysfs attributes */ 369 + ret = sysfs_create_group(&dev->kobj, &ltc4245_std_group); 370 + if (ret) { 371 + dev_err(dev, "unable to register standard attributes\n"); 372 + return ret; 373 + } 374 + 375 + /* if we're using the extra gpio support, register it's attributes */ 376 + if (data->use_extra_gpios) { 377 + ret = sysfs_create_group(&dev->kobj, &ltc4245_gpio_group); 378 + if (ret) { 379 + dev_err(dev, "unable to register gpio attributes\n"); 380 + sysfs_remove_group(&dev->kobj, &ltc4245_std_group); 381 + return ret; 382 + } 383 + } 384 + 385 + return 0; 386 + } 387 + 388 + static void ltc4245_sysfs_remove_groups(struct i2c_client *client) 389 + { 390 + struct ltc4245_data *data = i2c_get_clientdata(client); 391 + struct device *dev = &client->dev; 392 + 393 + if (data->use_extra_gpios) 394 + sysfs_remove_group(&dev->kobj, &ltc4245_gpio_group); 395 + 396 + sysfs_remove_group(&dev->kobj, &ltc4245_std_group); 397 + } 398 + 399 + static bool ltc4245_use_extra_gpios(struct i2c_client *client) 400 + { 401 + struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); 402 + #ifdef CONFIG_OF 403 + struct device_node *np = client->dev.of_node; 404 + #endif 405 + 406 + /* prefer platform data */ 407 + if (pdata) 408 + return pdata->use_extra_gpios; 409 + 410 + #ifdef CONFIG_OF 411 + /* fallback on OF */ 412 + if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) 413 + return true; 414 + #endif 415 + 416 + return false; 417 + } 441 418 442 419 static int ltc4245_probe(struct i2c_client *client, 443 420 const struct i2c_device_id *id) ··· 524 367 525 368 i2c_set_clientdata(client, data); 526 369 mutex_init(&data->update_lock); 370 + data->use_extra_gpios = ltc4245_use_extra_gpios(client); 527 371 528 372 /* Initialize the LTC4245 chip */ 529 373 i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); 530 374 i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); 531 375 532 376 /* Register sysfs hooks */ 533 - ret = sysfs_create_group(&client->dev.kobj, &ltc4245_group); 377 + ret = ltc4245_sysfs_create_groups(client); 534 378 if (ret) 535 - goto out_sysfs_create_group; 379 + goto out_sysfs_create_groups; 536 380 537 381 data->hwmon_dev = hwmon_device_register(&client->dev); 538 382 if (IS_ERR(data->hwmon_dev)) { ··· 544 386 return 0; 545 387 546 388 out_hwmon_device_register: 547 - sysfs_remove_group(&client->dev.kobj, &ltc4245_group); 548 - out_sysfs_create_group: 389 + ltc4245_sysfs_remove_groups(client); 390 + out_sysfs_create_groups: 549 391 kfree(data); 550 392 out_kzalloc: 551 393 return ret; ··· 556 398 struct ltc4245_data *data = i2c_get_clientdata(client); 557 399 558 400 hwmon_device_unregister(data->hwmon_dev); 559 - sysfs_remove_group(&client->dev.kobj, &ltc4245_group); 560 - 401 + ltc4245_sysfs_remove_groups(client); 561 402 kfree(data); 562 403 563 404 return 0;
+21
include/linux/i2c/ltc4245.h
··· 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 + 17 + struct ltc4245_platform_data { 18 + bool use_extra_gpios; 19 + }; 20 + 21 + #endif /* LINUX_LTC4245_H */