aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2014-01-14 00:11:56 -0500
committerGuenter Roeck <linux@roeck-us.net>2014-03-03 11:01:03 -0500
commit79ffe8594f23475d83112cd5bd68805f91e49170 (patch)
treedeaddeb4be642b6a58f16577c2705668b3eca08c /drivers/hwmon
parent4b49cca36ee9bb8e043cb76e23b1c2864e3bbc86 (diff)
hwmon: Driver for Linear Technologies LTC4222
Driver Linear Technologies LTC4222 Dual Hot Swap Controller The driver currently only supports voltage monitoring, not voltage control. Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig12
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ltc4222.c237
3 files changed, 250 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5ce43d8dfa98..ebf1131d05ea 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -798,6 +798,18 @@ config SENSORS_LTC4215
798 This driver can also be built as a module. If so, the module will 798 This driver can also be built as a module. If so, the module will
799 be called ltc4215. 799 be called ltc4215.
800 800
801config SENSORS_LTC4222
802 tristate "Linear Technology LTC4222"
803 depends on I2C
804 select REGMAP_I2C
805 default n
806 help
807 If you say yes here you get support for Linear Technology LTC4222
808 Dual Hot Swap Controller I2C interface.
809
810 This driver can also be built as a module. If so, the module will
811 be called ltc4222.
812
801config SENSORS_LTC4245 813config SENSORS_LTC4245
802 tristate "Linear Technology LTC4245" 814 tristate "Linear Technology LTC4245"
803 depends on I2C 815 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index ec7cde06eb52..f2a4048fe6ca 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
97obj-$(CONFIG_SENSORS_LM95245) += lm95245.o 97obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
98obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o 98obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
99obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o 99obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
100obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
100obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o 101obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
101obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o 102obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
102obj-$(CONFIG_SENSORS_MAX1111) += max1111.o 103obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c
new file mode 100644
index 000000000000..07c25653659f
--- /dev/null
+++ b/drivers/hwmon/ltc4222.c
@@ -0,0 +1,237 @@
1/*
2 * Driver for Linear Technology LTC4222 Dual Hot Swap controller
3 *
4 * Copyright (c) 2014 Guenter Roeck
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/err.h>
20#include <linux/slab.h>
21#include <linux/bitops.h>
22#include <linux/i2c.h>
23#include <linux/hwmon.h>
24#include <linux/hwmon-sysfs.h>
25#include <linux/jiffies.h>
26#include <linux/regmap.h>
27
28/* chip registers */
29
30#define LTC4222_CONTROL1 0xd0
31#define LTC4222_ALERT1 0xd1
32#define LTC4222_STATUS1 0xd2
33#define LTC4222_FAULT1 0xd3
34#define LTC4222_CONTROL2 0xd4
35#define LTC4222_ALERT2 0xd5
36#define LTC4222_STATUS2 0xd6
37#define LTC4222_FAULT2 0xd7
38#define LTC4222_SOURCE1 0xd8
39#define LTC4222_SOURCE2 0xda
40#define LTC4222_ADIN1 0xdc
41#define LTC4222_ADIN2 0xde
42#define LTC4222_SENSE1 0xe0
43#define LTC4222_SENSE2 0xe2
44#define LTC4222_ADC_CONTROL 0xe4
45
46/*
47 * Fault register bits
48 */
49#define FAULT_OV BIT(0)
50#define FAULT_UV BIT(1)
51#define FAULT_OC BIT(2)
52#define FAULT_POWER_BAD BIT(3)
53#define FAULT_FET_BAD BIT(5)
54
55/* Return the voltage from the given register in mV or mA */
56static int ltc4222_get_value(struct device *dev, u8 reg)
57{
58 struct regmap *regmap = dev_get_drvdata(dev);
59 unsigned int val;
60 u8 buf[2];
61 int ret;
62
63 ret = regmap_bulk_read(regmap, reg, buf, 2);
64 if (ret < 0)
65 return ret;
66
67 val = ((buf[0] << 8) + buf[1]) >> 6;
68
69 switch (reg) {
70 case LTC4222_ADIN1:
71 case LTC4222_ADIN2:
72 /* 1.25 mV resolution. Convert to mV. */
73 val = DIV_ROUND_CLOSEST(val * 5, 4);
74 break;
75 case LTC4222_SOURCE1:
76 case LTC4222_SOURCE2:
77 /* 31.25 mV resolution. Convert to mV. */
78 val = DIV_ROUND_CLOSEST(val * 125, 4);
79 break;
80 case LTC4222_SENSE1:
81 case LTC4222_SENSE2:
82 /*
83 * 62.5 uV resolution. Convert to current as measured with
84 * an 1 mOhm sense resistor, in mA. If a different sense
85 * resistor is installed, calculate the actual current by
86 * dividing the reported current by the sense resistor value
87 * in mOhm.
88 */
89 val = DIV_ROUND_CLOSEST(val * 125, 2);
90 break;
91 default:
92 return -EINVAL;
93 }
94 return val;
95}
96
97static ssize_t ltc4222_show_value(struct device *dev,
98 struct device_attribute *da, char *buf)
99{
100 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
101 int value;
102
103 value = ltc4222_get_value(dev, attr->index);
104 if (value < 0)
105 return value;
106 return snprintf(buf, PAGE_SIZE, "%d\n", value);
107}
108
109static ssize_t ltc4222_show_bool(struct device *dev,
110 struct device_attribute *da, char *buf)
111{
112 struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
113 struct regmap *regmap = dev_get_drvdata(dev);
114 unsigned int fault;
115 int ret;
116
117 ret = regmap_read(regmap, attr->nr, &fault);
118 if (ret < 0)
119 return ret;
120 fault &= attr->index;
121 if (fault) /* Clear reported faults in chip register */
122 regmap_update_bits(regmap, attr->nr, attr->index, 0);
123
124 return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
125}
126
127/* Voltages */
128static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL,
129 LTC4222_SOURCE1);
130static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL,
131 LTC4222_ADIN1);
132static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL,
133 LTC4222_SOURCE2);
134static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL,
135 LTC4222_ADIN2);
136
137/*
138 * Voltage alarms
139 * UV/OV faults are associated with the input voltage, and power bad and fet
140 * faults are associated with the output voltage.
141 */
142static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL,
143 LTC4222_FAULT1, FAULT_UV);
144static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
145 LTC4222_FAULT1, FAULT_OV);
146static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL,
147 LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD);
148
149static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL,
150 LTC4222_FAULT2, FAULT_UV);
151static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
152 LTC4222_FAULT2, FAULT_OV);
153static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL,
154 LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD);
155
156/* Current (via sense resistor) */
157static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL,
158 LTC4222_SENSE1);
159static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL,
160 LTC4222_SENSE2);
161
162/* Overcurrent alarm */
163static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
164 LTC4222_FAULT1, FAULT_OC);
165static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL,
166 LTC4222_FAULT2, FAULT_OC);
167
168static struct attribute *ltc4222_attrs[] = {
169 &sensor_dev_attr_in1_input.dev_attr.attr,
170 &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
171 &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
172 &sensor_dev_attr_in2_input.dev_attr.attr,
173 &sensor_dev_attr_in2_alarm.dev_attr.attr,
174 &sensor_dev_attr_in3_input.dev_attr.attr,
175 &sensor_dev_attr_in3_min_alarm.dev_attr.attr,
176 &sensor_dev_attr_in3_max_alarm.dev_attr.attr,
177 &sensor_dev_attr_in4_input.dev_attr.attr,
178 &sensor_dev_attr_in4_alarm.dev_attr.attr,
179
180 &sensor_dev_attr_curr1_input.dev_attr.attr,
181 &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
182 &sensor_dev_attr_curr2_input.dev_attr.attr,
183 &sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
184
185 NULL,
186};
187ATTRIBUTE_GROUPS(ltc4222);
188
189static struct regmap_config ltc4222_regmap_config = {
190 .reg_bits = 8,
191 .val_bits = 8,
192 .max_register = LTC4222_ADC_CONTROL,
193};
194
195static int ltc4222_probe(struct i2c_client *client,
196 const struct i2c_device_id *id)
197{
198 struct device *dev = &client->dev;
199 struct device *hwmon_dev;
200 struct regmap *regmap;
201
202 regmap = devm_regmap_init_i2c(client, &ltc4222_regmap_config);
203 if (IS_ERR(regmap)) {
204 dev_err(dev, "failed to allocate register map\n");
205 return PTR_ERR(regmap);
206 }
207
208 /* Clear faults */
209 regmap_write(regmap, LTC4222_FAULT1, 0x00);
210 regmap_write(regmap, LTC4222_FAULT2, 0x00);
211
212 hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
213 regmap,
214 ltc4222_groups);
215 return PTR_ERR_OR_ZERO(hwmon_dev);
216}
217
218static const struct i2c_device_id ltc4222_id[] = {
219 {"ltc4222", 0},
220 { }
221};
222
223MODULE_DEVICE_TABLE(i2c, ltc4222_id);
224
225static struct i2c_driver ltc4222_driver = {
226 .driver = {
227 .name = "ltc4222",
228 },
229 .probe = ltc4222_probe,
230 .id_table = ltc4222_id,
231};
232
233module_i2c_driver(ltc4222_driver);
234
235MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
236MODULE_DESCRIPTION("LTC4222 driver");
237MODULE_LICENSE("GPL");