summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Levens <tom.levens@cern.ch>2017-07-03 00:29:00 -0400
committerGuenter Roeck <linux@roeck-us.net>2018-05-21 10:52:01 -0400
commit5d9ca430ea3ae87e41a69333ca30533e9cdc17cf (patch)
tree475cd040bf02cbbabbee202b8a598905f8d7e55f
parent7de859fcf92c8990902faea594d0128c541d14f6 (diff)
hwmon: (ltc2990) support all measurement modes
Updated version of the ltc2990 driver which supports all measurement modes (current, voltage, temperature) available in the chip. If devicetree is used, the mode must be specified with the property "lltc,meas-mode". The format and possible values of the property are described in the binding. If devicetree is not used, the mode of the chip will not be configured. Unless the chip is configured by another source, only the internal temperature and supply voltage will be measured. Signed-off-by: Tom Levens <tom.levens@cern.ch> Tested-By: mike.looijmans@topic.nl [groeck: Fixed compiler warning] Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r--Documentation/hwmon/ltc299024
-rw-r--r--drivers/hwmon/Kconfig7
-rw-r--r--drivers/hwmon/ltc2990.c196
3 files changed, 185 insertions, 42 deletions
diff --git a/Documentation/hwmon/ltc2990 b/Documentation/hwmon/ltc2990
index c25211e90bdc..3ed68f676c0f 100644
--- a/Documentation/hwmon/ltc2990
+++ b/Documentation/hwmon/ltc2990
@@ -8,6 +8,7 @@ Supported chips:
8 Datasheet: http://www.linear.com/product/ltc2990 8 Datasheet: http://www.linear.com/product/ltc2990
9 9
10Author: Mike Looijmans <mike.looijmans@topic.nl> 10Author: Mike Looijmans <mike.looijmans@topic.nl>
11 Tom Levens <tom.levens@cern.ch>
11 12
12 13
13Description 14Description
@@ -16,10 +17,8 @@ Description
16LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor. 17LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
17The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4) 18The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
18can be combined to measure a differential voltage, which is typically used to 19can be combined to measure a differential voltage, which is typically used to
19measure current through a series resistor, or a temperature. 20measure current through a series resistor, or a temperature with an external
20 21diode.
21This driver currently uses the 2x differential mode only. In order to support
22other modes, the driver will need to be expanded.
23 22
24 23
25Usage Notes 24Usage Notes
@@ -32,12 +31,19 @@ devices explicitly.
32Sysfs attributes 31Sysfs attributes
33---------------- 32----------------
34 33
34in0_input Voltage at Vcc pin in millivolt (range 2.5V to 5V)
35temp1_input Internal chip temperature in millidegrees Celcius
36
37A subset of the following attributes are visible, depending on the measurement
38mode of the chip.
39
40in[1-4]_input Voltage at V[1-4] pin in millivolt
41temp2_input External temperature sensor TR1 in millidegrees Celcius
42temp3_input External temperature sensor TR2 in millidegrees Celcius
43curr1_input Current in mA across V1-V2 assuming a 1mOhm sense resistor
44curr2_input Current in mA across V3-V4 assuming a 1mOhm sense resistor
45
35The "curr*_input" measurements actually report the voltage drop across the 46The "curr*_input" measurements actually report the voltage drop across the
36input pins in microvolts. This is equivalent to the current through a 1mOhm 47input pins in microvolts. This is equivalent to the current through a 1mOhm
37sense resistor. Divide the reported value by the actual sense resistor value 48sense resistor. Divide the reported value by the actual sense resistor value
38in mOhm to get the actual value. 49in mOhm to get the actual value.
39
40in0_input Voltage at Vcc pin in millivolt (range 2.5V to 5V)
41temp1_input Internal chip temperature in millidegrees Celcius
42curr1_input Current in mA across v1-v2 assuming a 1mOhm sense resistor.
43curr2_input Current in mA across v3-v4 assuming a 1mOhm sense resistor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6ec307c93ece..f10840ad465c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -717,15 +717,12 @@ config SENSORS_LTC2945
717 be called ltc2945. 717 be called ltc2945.
718 718
719config SENSORS_LTC2990 719config SENSORS_LTC2990
720 tristate "Linear Technology LTC2990 (current monitoring mode only)" 720 tristate "Linear Technology LTC2990"
721 depends on I2C 721 depends on I2C
722 help 722 help
723 If you say yes here you get support for Linear Technology LTC2990 723 If you say yes here you get support for Linear Technology LTC2990
724 I2C System Monitor. The LTC2990 supports a combination of voltage, 724 I2C System Monitor. The LTC2990 supports a combination of voltage,
725 current and temperature monitoring, but in addition to the Vcc supply 725 current and temperature monitoring.
726 voltage and chip temperature, this driver currently only supports
727 reading two currents by measuring two differential voltages across
728 series resistors.
729 726
730 This driver can also be built as a module. If so, the module will 727 This driver can also be built as a module. If so, the module will
731 be called ltc2990. 728 be called ltc2990.
diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
index e320d212ac27..2aefdc58b242 100644
--- a/drivers/hwmon/ltc2990.c
+++ b/drivers/hwmon/ltc2990.c
@@ -5,10 +5,6 @@
5 * Author: Mike Looijmans <mike.looijmans@topic.nl> 5 * Author: Mike Looijmans <mike.looijmans@topic.nl>
6 * 6 *
7 * License: GPLv2 7 * License: GPLv2
8 *
9 * This driver assumes the chip is wired as a dual current monitor, and
10 * reports the voltage drop across two series resistors. It also reports
11 * the chip's internal temperature and Vcc power supply voltage.
12 */ 8 */
13 9
14#include <linux/bitops.h> 10#include <linux/bitops.h>
@@ -18,6 +14,7 @@
18#include <linux/i2c.h> 14#include <linux/i2c.h>
19#include <linux/kernel.h> 15#include <linux/kernel.h>
20#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/of.h>
21 18
22#define LTC2990_STATUS 0x00 19#define LTC2990_STATUS 0x00
23#define LTC2990_CONTROL 0x01 20#define LTC2990_CONTROL 0x01
@@ -29,35 +26,109 @@
29#define LTC2990_V4_MSB 0x0C 26#define LTC2990_V4_MSB 0x0C
30#define LTC2990_VCC_MSB 0x0E 27#define LTC2990_VCC_MSB 0x0E
31 28
32#define LTC2990_CONTROL_KELVIN BIT(7) 29#define LTC2990_IN0 BIT(0)
33#define LTC2990_CONTROL_SINGLE BIT(6) 30#define LTC2990_IN1 BIT(1)
34#define LTC2990_CONTROL_MEASURE_ALL (0x3 << 3) 31#define LTC2990_IN2 BIT(2)
35#define LTC2990_CONTROL_MODE_CURRENT 0x06 32#define LTC2990_IN3 BIT(3)
36#define LTC2990_CONTROL_MODE_VOLTAGE 0x07 33#define LTC2990_IN4 BIT(4)
34#define LTC2990_CURR1 BIT(5)
35#define LTC2990_CURR2 BIT(6)
36#define LTC2990_TEMP1 BIT(7)
37#define LTC2990_TEMP2 BIT(8)
38#define LTC2990_TEMP3 BIT(9)
39#define LTC2990_NONE 0
40#define LTC2990_ALL GENMASK(9, 0)
41
42#define LTC2990_MODE0_SHIFT 0
43#define LTC2990_MODE0_MASK GENMASK(2, 0)
44#define LTC2990_MODE1_SHIFT 3
45#define LTC2990_MODE1_MASK GENMASK(1, 0)
46
47/* Enabled measurements for mode bits 2..0 */
48static const int ltc2990_attrs_ena_0[] = {
49 LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
50 LTC2990_CURR1 | LTC2990_TEMP3,
51 LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
52 LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
53 LTC2990_TEMP2 | LTC2990_CURR2,
54 LTC2990_TEMP2 | LTC2990_TEMP3,
55 LTC2990_CURR1 | LTC2990_CURR2,
56 LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
57};
58
59/* Enabled measurements for mode bits 4..3 */
60static const int ltc2990_attrs_ena_1[] = {
61 LTC2990_NONE,
62 LTC2990_TEMP2 | LTC2990_IN1 | LTC2990_CURR1,
63 LTC2990_TEMP3 | LTC2990_IN3 | LTC2990_CURR2,
64 LTC2990_ALL
65};
66
67struct ltc2990_data {
68 struct i2c_client *i2c;
69 u32 mode[2];
70};
37 71
38/* Return the converted value from the given register in uV or mC */ 72/* Return the converted value from the given register in uV or mC */
39static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result) 73static int ltc2990_get_value(struct i2c_client *i2c, int index, int *result)
40{ 74{
41 int val; 75 int val;
76 u8 reg;
77
78 switch (index) {
79 case LTC2990_IN0:
80 reg = LTC2990_VCC_MSB;
81 break;
82 case LTC2990_IN1:
83 case LTC2990_CURR1:
84 case LTC2990_TEMP2:
85 reg = LTC2990_V1_MSB;
86 break;
87 case LTC2990_IN2:
88 reg = LTC2990_V2_MSB;
89 break;
90 case LTC2990_IN3:
91 case LTC2990_CURR2:
92 case LTC2990_TEMP3:
93 reg = LTC2990_V3_MSB;
94 break;
95 case LTC2990_IN4:
96 reg = LTC2990_V4_MSB;
97 break;
98 case LTC2990_TEMP1:
99 reg = LTC2990_TINT_MSB;
100 break;
101 default:
102 return -EINVAL;
103 }
42 104
43 val = i2c_smbus_read_word_swapped(i2c, reg); 105 val = i2c_smbus_read_word_swapped(i2c, reg);
44 if (unlikely(val < 0)) 106 if (unlikely(val < 0))
45 return val; 107 return val;
46 108
47 switch (reg) { 109 switch (index) {
48 case LTC2990_TINT_MSB: 110 case LTC2990_TEMP1:
49 /* internal temp, 0.0625 degrees/LSB, 13-bit */ 111 case LTC2990_TEMP2:
112 case LTC2990_TEMP3:
113 /* temp, 0.0625 degrees/LSB */
50 *result = sign_extend32(val, 12) * 1000 / 16; 114 *result = sign_extend32(val, 12) * 1000 / 16;
51 break; 115 break;
52 case LTC2990_V1_MSB: 116 case LTC2990_CURR1:
53 case LTC2990_V3_MSB: 117 case LTC2990_CURR2:
54 /* Vx-Vy, 19.42uV/LSB. Depends on mode. */ 118 /* Vx-Vy, 19.42uV/LSB */
55 *result = sign_extend32(val, 14) * 1942 / 100; 119 *result = sign_extend32(val, 14) * 1942 / 100;
56 break; 120 break;
57 case LTC2990_VCC_MSB: 121 case LTC2990_IN0:
58 /* Vcc, 305.18μV/LSB, 2.5V offset */ 122 /* Vcc, 305.18uV/LSB, 2.5V offset */
59 *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500; 123 *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
60 break; 124 break;
125 case LTC2990_IN1:
126 case LTC2990_IN2:
127 case LTC2990_IN3:
128 case LTC2990_IN4:
129 /* Vx, 305.18uV/LSB */
130 *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
131 break;
61 default: 132 default:
62 return -EINVAL; /* won't happen, keep compiler happy */ 133 return -EINVAL; /* won't happen, keep compiler happy */
63 } 134 }
@@ -69,48 +140,117 @@ static ssize_t ltc2990_show_value(struct device *dev,
69 struct device_attribute *da, char *buf) 140 struct device_attribute *da, char *buf)
70{ 141{
71 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 142 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
143 struct ltc2990_data *data = dev_get_drvdata(dev);
72 int value; 144 int value;
73 int ret; 145 int ret;
74 146
75 ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value); 147 ret = ltc2990_get_value(data->i2c, attr->index, &value);
76 if (unlikely(ret < 0)) 148 if (unlikely(ret < 0))
77 return ret; 149 return ret;
78 150
79 return snprintf(buf, PAGE_SIZE, "%d\n", value); 151 return snprintf(buf, PAGE_SIZE, "%d\n", value);
80} 152}
81 153
154static umode_t ltc2990_attrs_visible(struct kobject *kobj,
155 struct attribute *a, int n)
156{
157 struct device *dev = container_of(kobj, struct device, kobj);
158 struct ltc2990_data *data = dev_get_drvdata(dev);
159 struct device_attribute *da =
160 container_of(a, struct device_attribute, attr);
161 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
162
163 int attrs_mask = LTC2990_IN0 | LTC2990_TEMP1 |
164 (ltc2990_attrs_ena_0[data->mode[0]] &
165 ltc2990_attrs_ena_1[data->mode[1]]);
166
167 if (attr->index & attrs_mask)
168 return a->mode;
169
170 return 0;
171}
172
82static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL, 173static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
83 LTC2990_TINT_MSB); 174 LTC2990_TEMP1);
175static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, ltc2990_show_value, NULL,
176 LTC2990_TEMP2);
177static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, ltc2990_show_value, NULL,
178 LTC2990_TEMP3);
84static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL, 179static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
85 LTC2990_V1_MSB); 180 LTC2990_CURR1);
86static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL, 181static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
87 LTC2990_V3_MSB); 182 LTC2990_CURR2);
88static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL, 183static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
89 LTC2990_VCC_MSB); 184 LTC2990_IN0);
185static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2990_show_value, NULL,
186 LTC2990_IN1);
187static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2990_show_value, NULL,
188 LTC2990_IN2);
189static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc2990_show_value, NULL,
190 LTC2990_IN3);
191static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc2990_show_value, NULL,
192 LTC2990_IN4);
90 193
91static struct attribute *ltc2990_attrs[] = { 194static struct attribute *ltc2990_attrs[] = {
92 &sensor_dev_attr_temp1_input.dev_attr.attr, 195 &sensor_dev_attr_temp1_input.dev_attr.attr,
196 &sensor_dev_attr_temp2_input.dev_attr.attr,
197 &sensor_dev_attr_temp3_input.dev_attr.attr,
93 &sensor_dev_attr_curr1_input.dev_attr.attr, 198 &sensor_dev_attr_curr1_input.dev_attr.attr,
94 &sensor_dev_attr_curr2_input.dev_attr.attr, 199 &sensor_dev_attr_curr2_input.dev_attr.attr,
95 &sensor_dev_attr_in0_input.dev_attr.attr, 200 &sensor_dev_attr_in0_input.dev_attr.attr,
201 &sensor_dev_attr_in1_input.dev_attr.attr,
202 &sensor_dev_attr_in2_input.dev_attr.attr,
203 &sensor_dev_attr_in3_input.dev_attr.attr,
204 &sensor_dev_attr_in4_input.dev_attr.attr,
96 NULL, 205 NULL,
97}; 206};
98ATTRIBUTE_GROUPS(ltc2990); 207
208static const struct attribute_group ltc2990_group = {
209 .attrs = ltc2990_attrs,
210 .is_visible = ltc2990_attrs_visible,
211};
212__ATTRIBUTE_GROUPS(ltc2990);
99 213
100static int ltc2990_i2c_probe(struct i2c_client *i2c, 214static int ltc2990_i2c_probe(struct i2c_client *i2c,
101 const struct i2c_device_id *id) 215 const struct i2c_device_id *id)
102{ 216{
103 int ret; 217 int ret;
104 struct device *hwmon_dev; 218 struct device *hwmon_dev;
219 struct ltc2990_data *data;
220 struct device_node *of_node = i2c->dev.of_node;
105 221
106 if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | 222 if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
107 I2C_FUNC_SMBUS_WORD_DATA)) 223 I2C_FUNC_SMBUS_WORD_DATA))
108 return -ENODEV; 224 return -ENODEV;
109 225
110 /* Setup continuous mode, current monitor */ 226 data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
227 if (unlikely(!data))
228 return -ENOMEM;
229
230 data->i2c = i2c;
231
232 if (of_node) {
233 ret = of_property_read_u32_array(of_node, "lltc,meas-mode",
234 data->mode, 2);
235 if (ret < 0)
236 return ret;
237
238 if (data->mode[0] & ~LTC2990_MODE0_MASK ||
239 data->mode[1] & ~LTC2990_MODE1_MASK)
240 return -EINVAL;
241 } else {
242 ret = i2c_smbus_read_byte_data(i2c, LTC2990_CONTROL);
243 if (ret < 0)
244 return ret;
245
246 data->mode[0] = ret >> LTC2990_MODE0_SHIFT & LTC2990_MODE0_MASK;
247 data->mode[1] = ret >> LTC2990_MODE1_SHIFT & LTC2990_MODE1_MASK;
248 }
249
250 /* Setup continuous mode */
111 ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL, 251 ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
112 LTC2990_CONTROL_MEASURE_ALL | 252 data->mode[0] << LTC2990_MODE0_SHIFT |
113 LTC2990_CONTROL_MODE_CURRENT); 253 data->mode[1] << LTC2990_MODE1_SHIFT);
114 if (ret < 0) { 254 if (ret < 0) {
115 dev_err(&i2c->dev, "Error: Failed to set control mode.\n"); 255 dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
116 return ret; 256 return ret;
@@ -124,7 +264,7 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
124 264
125 hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev, 265 hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
126 i2c->name, 266 i2c->name,
127 i2c, 267 data,
128 ltc2990_groups); 268 ltc2990_groups);
129 269
130 return PTR_ERR_OR_ZERO(hwmon_dev); 270 return PTR_ERR_OR_ZERO(hwmon_dev);