aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <guenter.roeck@ericsson.com>2010-06-25 14:59:54 -0400
committerGuenter Roeck <guenter.roeck@ericsson.com>2010-10-25 17:11:20 -0400
commite5f5c99a39375ce533aacfdfb269978070121e1c (patch)
tree0abf39b045d772cf11bc401bcb1a36985ad3d99a
parent2ee321440e3a594dcdd9981e68e5e302447047a2 (diff)
hwmon: LTC4261 Hardware monitoring driver
This driver adds support for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller. Reviewed-by: Ira W. Snyder <iws@ovro.caltech.edu> Reviewed-by: Tom Grennan <tom.grennan@ericsson.com> Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
-rw-r--r--Documentation/hwmon/ltc426163
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ltc4261.c315
5 files changed, 397 insertions, 0 deletions
diff --git a/Documentation/hwmon/ltc4261 b/Documentation/hwmon/ltc4261
new file mode 100644
index 000000000000..eba2e2c4b94d
--- /dev/null
+++ b/Documentation/hwmon/ltc4261
@@ -0,0 +1,63 @@
1Kernel driver ltc4261
2=====================
3
4Supported chips:
5 * Linear Technology LTC4261
6 Prefix: 'ltc4261'
7 Addresses scanned: -
8 Datasheet:
9 http://cds.linear.com/docs/Datasheet/42612fb.pdf
10
11Author: Guenter Roeck <guenter.roeck@ericsson.com>
12
13
14Description
15-----------
16
17The LTC4261/LTC4261-2 negative voltage Hot Swap controllers allow a board
18to be safely inserted and removed from a live backplane.
19
20
21Usage Notes
22-----------
23
24This driver does not probe for LTC4261 devices, since there is no register
25which can be safely used to identify the chip. You will have to instantiate
26the devices explicitly.
27
28Example: the following will load the driver for an LTC4261 at address 0x10
29on I2C bus #1:
30$ modprobe ltc4261
31$ echo ltc4261 0x10 > /sys/bus/i2c/devices/i2c-1/new_device
32
33
34Sysfs entries
35-------------
36
37Voltage readings provided by this driver are reported as obtained from the ADC
38registers. If a set of voltage divider resistors is installed, calculate the
39real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the
40value of the divider resistor against the measured voltage and R2 is the value
41of the divider resistor against Ground.
42
43Current reading provided by this driver is reported as obtained from the ADC
44Current Sense register. The reported value assumes that a 1 mOhm sense resistor
45is installed. If a different sense resistor is installed, calculate the real
46current by dividing the reported value by the sense resistor value in mOhm.
47
48The chip has two voltage sensors, but only one set of voltage alarm status bits.
49In many many designs, those alarms are associated with the ADIN2 sensor, due to
50the proximity of the ADIN2 pin to the OV pin. ADIN2 is, however, not available
51on all chip variants. To ensure that the alarm condition is reported to the user,
52report it with both voltage sensors.
53
54in1_input ADIN2 voltage (mV)
55in1_min_alarm ADIN/ADIN2 Undervoltage alarm
56in1_max_alarm ADIN/ADIN2 Overvoltage alarm
57
58in2_input ADIN voltage (mV)
59in2_min_alarm ADIN/ADIN2 Undervoltage alarm
60in2_max_alarm ADIN/ADIN2 Overvoltage alarm
61
62curr1_input SENSE current (mA)
63curr1_alarm SENSE overcurrent alarm
diff --git a/MAINTAINERS b/MAINTAINERS
index 541451050b36..146b8a068a4e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3765,6 +3765,13 @@ L: linux-scsi@vger.kernel.org
3765S: Maintained 3765S: Maintained
3766F: drivers/scsi/sym53c8xx_2/ 3766F: drivers/scsi/sym53c8xx_2/
3767 3767
3768LTC4261 HARDWARE MONITOR DRIVER
3769M: Guenter Roeck <linux@roeck-us.net>
3770L: lm-sensors@lm-sensors.org
3771S: Maintained
3772F: Documentation/hwmon/ltc4261
3773F: drivers/hwmon/ltc4261.c
3774
3768LTP (Linux Test Project) 3775LTP (Linux Test Project)
3769M: Rishikesh K Rajak <risrajak@linux.vnet.ibm.com> 3776M: Rishikesh K Rajak <risrajak@linux.vnet.ibm.com>
3770M: Garrett Cooper <yanegomi@gmail.com> 3777M: Garrett Cooper <yanegomi@gmail.com>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e382da3122b7..ec4295fd22f6 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -654,6 +654,17 @@ config SENSORS_LTC4245
654 This driver can also be built as a module. If so, the module will 654 This driver can also be built as a module. If so, the module will
655 be called ltc4245. 655 be called ltc4245.
656 656
657config SENSORS_LTC4261
658 tristate "Linear Technology LTC4261"
659 depends on I2C && EXPERIMENTAL
660 default n
661 help
662 If you say yes here you get support for Linear Technology LTC4261
663 Negative Voltage Hot Swap Controller I2C interface.
664
665 This driver can also be built as a module. If so, the module will
666 be called ltc4261.
667
657config SENSORS_LM95241 668config SENSORS_LM95241
658 tristate "National Semiconductor LM95241 sensor chip" 669 tristate "National Semiconductor LM95241 sensor chip"
659 depends on I2C 670 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index ec9cb735c898..fc38e7c131f8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o
79obj-$(CONFIG_SENSORS_LM95241) += lm95241.o 79obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
80obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o 80obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
81obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o 81obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
82obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
82obj-$(CONFIG_SENSORS_MAX1111) += max1111.o 83obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
83obj-$(CONFIG_SENSORS_MAX1619) += max1619.o 84obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
84obj-$(CONFIG_SENSORS_MAX6650) += max6650.o 85obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c
new file mode 100644
index 000000000000..267626178678
--- /dev/null
+++ b/drivers/hwmon/ltc4261.c
@@ -0,0 +1,315 @@
1/*
2 * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller
3 *
4 * Copyright (C) 2010 Ericsson AB.
5 *
6 * Derived from:
7 *
8 * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
9 * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
10 *
11 * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/err.h>
32#include <linux/slab.h>
33#include <linux/i2c.h>
34#include <linux/hwmon.h>
35#include <linux/hwmon-sysfs.h>
36
37/* chip registers */
38#define LTC4261_STATUS 0x00 /* readonly */
39#define LTC4261_FAULT 0x01
40#define LTC4261_ALERT 0x02
41#define LTC4261_CONTROL 0x03
42#define LTC4261_SENSE_H 0x04
43#define LTC4261_SENSE_L 0x05
44#define LTC4261_ADIN2_H 0x06
45#define LTC4261_ADIN2_L 0x07
46#define LTC4261_ADIN_H 0x08
47#define LTC4261_ADIN_L 0x09
48
49/*
50 * Fault register bits
51 */
52#define FAULT_OV (1<<0)
53#define FAULT_UV (1<<1)
54#define FAULT_OC (1<<2)
55
56struct ltc4261_data {
57 struct device *hwmon_dev;
58
59 struct mutex update_lock;
60 bool valid;
61 unsigned long last_updated; /* in jiffies */
62
63 /* Registers */
64 u8 regs[10];
65};
66
67static struct ltc4261_data *ltc4261_update_device(struct device *dev)
68{
69 struct i2c_client *client = to_i2c_client(dev);
70 struct ltc4261_data *data = i2c_get_clientdata(client);
71 struct ltc4261_data *ret = data;
72
73 mutex_lock(&data->update_lock);
74
75 if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) {
76 int i;
77
78 /* Read registers -- 0x00 to 0x09 */
79 for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
80 int val;
81
82 val = i2c_smbus_read_byte_data(client, i);
83 if (unlikely(val < 0)) {
84 dev_dbg(dev,
85 "Failed to read ADC value: error %d",
86 val);
87 ret = ERR_PTR(val);
88 goto abort;
89 }
90 data->regs[i] = val;
91 }
92 data->last_updated = jiffies;
93 data->valid = 1;
94 }
95abort:
96 mutex_unlock(&data->update_lock);
97 return ret;
98}
99
100/* Return the voltage from the given register in mV or mA */
101static int ltc4261_get_value(struct ltc4261_data *data, u8 reg)
102{
103 u32 val;
104
105 val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6);
106
107 switch (reg) {
108 case LTC4261_ADIN_H:
109 case LTC4261_ADIN2_H:
110 /* 2.5mV resolution. Convert to mV. */
111 val = val * 25 / 10;
112 break;
113 case LTC4261_SENSE_H:
114 /*
115 * 62.5uV resolution. Convert to current as measured with
116 * an 1 mOhm sense resistor, in mA. If a different sense
117 * resistor is installed, calculate the actual current by
118 * dividing the reported current by the sense resistor value
119 * in mOhm.
120 */
121 val = val * 625 / 10;
122 break;
123 default:
124 /* If we get here, the developer messed up */
125 WARN_ON_ONCE(1);
126 val = 0;
127 break;
128 }
129
130 return val;
131}
132
133static ssize_t ltc4261_show_value(struct device *dev,
134 struct device_attribute *da, char *buf)
135{
136 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
137 struct ltc4261_data *data = ltc4261_update_device(dev);
138 int value;
139
140 if (IS_ERR(data))
141 return PTR_ERR(data);
142
143 value = ltc4261_get_value(data, attr->index);
144 return snprintf(buf, PAGE_SIZE, "%d\n", value);
145}
146
147static ssize_t ltc4261_show_bool(struct device *dev,
148 struct device_attribute *da, char *buf)
149{
150 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
151 struct i2c_client *client = to_i2c_client(dev);
152 struct ltc4261_data *data = ltc4261_update_device(dev);
153 u8 fault;
154
155 if (IS_ERR(data))
156 return PTR_ERR(data);
157
158 fault = data->regs[LTC4261_FAULT] & attr->index;
159 if (fault) /* Clear reported faults in chip register */
160 i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault);
161
162 return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
163}
164
165/*
166 * These macros are used below in constructing device attribute objects
167 * for use with sysfs_create_group() to make a sysfs device file
168 * for each register.
169 */
170
171#define LTC4261_VALUE(name, ltc4261_cmd_idx) \
172 static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
173 ltc4261_show_value, NULL, ltc4261_cmd_idx)
174
175#define LTC4261_BOOL(name, mask) \
176 static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
177 ltc4261_show_bool, NULL, (mask))
178
179/*
180 * Input voltages.
181 */
182LTC4261_VALUE(in1_input, LTC4261_ADIN_H);
183LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
184
185/*
186 * Voltage alarms. The chip has only one set of voltage alarm status bits,
187 * triggered by input voltage alarms. In many designs, those alarms are
188 * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin
189 * to the OV pin. ADIN2 is, however, not available on all chip variants.
190 * To ensure that the alarm condition is reported to the user, report it
191 * with both voltage sensors.
192 */
193LTC4261_BOOL(in1_min_alarm, FAULT_UV);
194LTC4261_BOOL(in1_max_alarm, FAULT_OV);
195LTC4261_BOOL(in2_min_alarm, FAULT_UV);
196LTC4261_BOOL(in2_max_alarm, FAULT_OV);
197
198/* Currents (via sense resistor) */
199LTC4261_VALUE(curr1_input, LTC4261_SENSE_H);
200
201/* Overcurrent alarm */
202LTC4261_BOOL(curr1_max_alarm, FAULT_OC);
203
204static struct attribute *ltc4261_attributes[] = {
205 &sensor_dev_attr_in1_input.dev_attr.attr,
206 &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
207 &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
208 &sensor_dev_attr_in2_input.dev_attr.attr,
209 &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
210 &sensor_dev_attr_in2_max_alarm.dev_attr.attr,
211
212 &sensor_dev_attr_curr1_input.dev_attr.attr,
213 &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
214
215 NULL,
216};
217
218static const struct attribute_group ltc4261_group = {
219 .attrs = ltc4261_attributes,
220};
221
222static int ltc4261_probe(struct i2c_client *client,
223 const struct i2c_device_id *id)
224{
225 struct i2c_adapter *adapter = client->adapter;
226 struct ltc4261_data *data;
227 int ret;
228
229 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
230 return -ENODEV;
231
232 if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) {
233 dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n",
234 adapter->id, client->addr, LTC4261_STATUS);
235 return -ENODEV;
236 }
237
238 data = kzalloc(sizeof(*data), GFP_KERNEL);
239 if (!data) {
240 ret = -ENOMEM;
241 goto out_kzalloc;
242 }
243
244 i2c_set_clientdata(client, data);
245 mutex_init(&data->update_lock);
246
247 /* Clear faults */
248 i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00);
249
250 /* Register sysfs hooks */
251 ret = sysfs_create_group(&client->dev.kobj, &ltc4261_group);
252 if (ret)
253 goto out_sysfs_create_group;
254
255 data->hwmon_dev = hwmon_device_register(&client->dev);
256 if (IS_ERR(data->hwmon_dev)) {
257 ret = PTR_ERR(data->hwmon_dev);
258 goto out_hwmon_device_register;
259 }
260
261 return 0;
262
263out_hwmon_device_register:
264 sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
265out_sysfs_create_group:
266 kfree(data);
267out_kzalloc:
268 return ret;
269}
270
271static int ltc4261_remove(struct i2c_client *client)
272{
273 struct ltc4261_data *data = i2c_get_clientdata(client);
274
275 hwmon_device_unregister(data->hwmon_dev);
276 sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
277
278 kfree(data);
279
280 return 0;
281}
282
283static const struct i2c_device_id ltc4261_id[] = {
284 {"ltc4261", 0},
285 {}
286};
287
288MODULE_DEVICE_TABLE(i2c, ltc4261_id);
289
290/* This is the driver that will be inserted */
291static struct i2c_driver ltc4261_driver = {
292 .driver = {
293 .name = "ltc4261",
294 },
295 .probe = ltc4261_probe,
296 .remove = ltc4261_remove,
297 .id_table = ltc4261_id,
298};
299
300static int __init ltc4261_init(void)
301{
302 return i2c_add_driver(&ltc4261_driver);
303}
304
305static void __exit ltc4261_exit(void)
306{
307 i2c_del_driver(&ltc4261_driver);
308}
309
310MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
311MODULE_DESCRIPTION("LTC4261 driver");
312MODULE_LICENSE("GPL");
313
314module_init(ltc4261_init);
315module_exit(ltc4261_exit);