]>
Commit | Line | Data |
---|---|---|
7cb6dcff AD |
1 | /* |
2 | * INA3221 Triple Current/Voltage Monitor | |
3 | * | |
4 | * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * Andrew F. Davis <[email protected]> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/hwmon.h> | |
18 | #include <linux/hwmon-sysfs.h> | |
19 | #include <linux/i2c.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/regmap.h> | |
23 | ||
24 | #define INA3221_DRIVER_NAME "ina3221" | |
25 | ||
26 | #define INA3221_CONFIG 0x00 | |
27 | #define INA3221_SHUNT1 0x01 | |
28 | #define INA3221_BUS1 0x02 | |
29 | #define INA3221_SHUNT2 0x03 | |
30 | #define INA3221_BUS2 0x04 | |
31 | #define INA3221_SHUNT3 0x05 | |
32 | #define INA3221_BUS3 0x06 | |
33 | #define INA3221_CRIT1 0x07 | |
34 | #define INA3221_WARN1 0x08 | |
35 | #define INA3221_CRIT2 0x09 | |
36 | #define INA3221_WARN2 0x0a | |
37 | #define INA3221_CRIT3 0x0b | |
38 | #define INA3221_WARN3 0x0c | |
39 | #define INA3221_MASK_ENABLE 0x0f | |
40 | ||
59d608e1 NC |
41 | #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) |
42 | #define INA3221_CONFIG_MODE_POWERDOWN 0 | |
791ebc9d NC |
43 | #define INA3221_CONFIG_MODE_SHUNT BIT(0) |
44 | #define INA3221_CONFIG_MODE_BUS BIT(1) | |
45 | #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) | |
a9e9dd9c | 46 | #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) |
7cb6dcff AD |
47 | |
48 | #define INA3221_RSHUNT_DEFAULT 10000 | |
49 | ||
50 | enum ina3221_fields { | |
51 | /* Configuration */ | |
52 | F_RST, | |
53 | ||
54 | /* Alert Flags */ | |
55 | F_WF3, F_WF2, F_WF1, | |
56 | F_CF3, F_CF2, F_CF1, | |
57 | ||
58 | /* sentinel */ | |
59 | F_MAX_FIELDS | |
60 | }; | |
61 | ||
62 | static const struct reg_field ina3221_reg_fields[] = { | |
63 | [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), | |
64 | ||
65 | [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), | |
66 | [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), | |
67 | [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), | |
68 | [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), | |
69 | [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), | |
70 | [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), | |
71 | }; | |
72 | ||
73 | enum ina3221_channels { | |
74 | INA3221_CHANNEL1, | |
75 | INA3221_CHANNEL2, | |
76 | INA3221_CHANNEL3, | |
77 | INA3221_NUM_CHANNELS | |
78 | }; | |
79 | ||
a9e9dd9c NC |
80 | /** |
81 | * struct ina3221_input - channel input source specific information | |
82 | * @label: label of channel input source | |
83 | * @shunt_resistor: shunt resistor value of channel input source | |
84 | * @disconnected: connection status of channel input source | |
85 | */ | |
86 | struct ina3221_input { | |
87 | const char *label; | |
88 | int shunt_resistor; | |
89 | bool disconnected; | |
90 | }; | |
91 | ||
7cb6dcff AD |
92 | /** |
93 | * struct ina3221_data - device specific information | |
94 | * @regmap: Register map of the device | |
95 | * @fields: Register fields of the device | |
a9e9dd9c | 96 | * @inputs: Array of channel input source specific structures |
59d608e1 | 97 | * @reg_config: Register value of INA3221_CONFIG |
7cb6dcff AD |
98 | */ |
99 | struct ina3221_data { | |
100 | struct regmap *regmap; | |
101 | struct regmap_field *fields[F_MAX_FIELDS]; | |
a9e9dd9c | 102 | struct ina3221_input inputs[INA3221_NUM_CHANNELS]; |
59d608e1 | 103 | u32 reg_config; |
7cb6dcff AD |
104 | }; |
105 | ||
a9e9dd9c NC |
106 | static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) |
107 | { | |
108 | return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); | |
109 | } | |
110 | ||
d4b0166d NC |
111 | static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, |
112 | int *val) | |
113 | { | |
114 | unsigned int regval; | |
115 | int ret; | |
116 | ||
117 | ret = regmap_read(ina->regmap, reg, ®val); | |
118 | if (ret) | |
119 | return ret; | |
120 | ||
121 | *val = sign_extend32(regval >> 3, 12); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static const u8 ina3221_in_reg[] = { | |
127 | INA3221_BUS1, | |
128 | INA3221_BUS2, | |
129 | INA3221_BUS3, | |
130 | INA3221_SHUNT1, | |
131 | INA3221_SHUNT2, | |
132 | INA3221_SHUNT3, | |
133 | }; | |
134 | ||
135 | static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) | |
136 | { | |
137 | const bool is_shunt = channel > INA3221_CHANNEL3; | |
138 | struct ina3221_data *ina = dev_get_drvdata(dev); | |
139 | u8 reg = ina3221_in_reg[channel]; | |
140 | int regval, ret; | |
141 | ||
142 | /* Translate shunt channel index to sensor channel index */ | |
143 | channel %= INA3221_NUM_CHANNELS; | |
144 | ||
145 | switch (attr) { | |
146 | case hwmon_in_input: | |
147 | if (!ina3221_is_enabled(ina, channel)) | |
148 | return -ENODATA; | |
149 | ||
150 | ret = ina3221_read_value(ina, reg, ®val); | |
151 | if (ret) | |
152 | return ret; | |
153 | ||
154 | /* | |
155 | * Scale of shunt voltage (uV): LSB is 40uV | |
156 | * Scale of bus voltage (mV): LSB is 8mV | |
157 | */ | |
158 | *val = regval * (is_shunt ? 40 : 8); | |
159 | return 0; | |
160 | case hwmon_in_enable: | |
161 | *val = ina3221_is_enabled(ina, channel); | |
162 | return 0; | |
163 | default: | |
164 | return -EOPNOTSUPP; | |
165 | } | |
166 | } | |
167 | ||
168 | static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { | |
169 | [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, | |
170 | [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, | |
171 | [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, | |
172 | [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, | |
173 | [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, | |
174 | }; | |
175 | ||
176 | static int ina3221_read_curr(struct device *dev, u32 attr, | |
177 | int channel, long *val) | |
a9e9dd9c | 178 | { |
a9e9dd9c | 179 | struct ina3221_data *ina = dev_get_drvdata(dev); |
a9e9dd9c | 180 | struct ina3221_input *input = &ina->inputs[channel]; |
d4b0166d NC |
181 | int resistance_uo = input->shunt_resistor; |
182 | u8 reg = ina3221_curr_reg[attr][channel]; | |
183 | int regval, voltage_nv, ret; | |
184 | ||
185 | switch (attr) { | |
186 | case hwmon_curr_input: | |
187 | if (!ina3221_is_enabled(ina, channel)) | |
188 | return -ENODATA; | |
189 | /* fall through */ | |
190 | case hwmon_curr_crit: | |
191 | case hwmon_curr_max: | |
192 | ret = ina3221_read_value(ina, reg, ®val); | |
193 | if (ret) | |
194 | return ret; | |
a9e9dd9c | 195 | |
d4b0166d NC |
196 | /* Scale of shunt voltage: LSB is 40uV (40000nV) */ |
197 | voltage_nv = regval * 40000; | |
198 | /* Return current in mA */ | |
199 | *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); | |
200 | return 0; | |
201 | case hwmon_curr_crit_alarm: | |
202 | case hwmon_curr_max_alarm: | |
203 | ret = regmap_field_read(ina->fields[reg], ®val); | |
204 | if (ret) | |
205 | return ret; | |
206 | *val = regval; | |
207 | return 0; | |
208 | default: | |
209 | return -EOPNOTSUPP; | |
210 | } | |
a9e9dd9c NC |
211 | } |
212 | ||
d4b0166d NC |
213 | static int ina3221_write_curr(struct device *dev, u32 attr, |
214 | int channel, long val) | |
a9e9dd9c | 215 | { |
a9e9dd9c | 216 | struct ina3221_data *ina = dev_get_drvdata(dev); |
d4b0166d NC |
217 | struct ina3221_input *input = &ina->inputs[channel]; |
218 | int resistance_uo = input->shunt_resistor; | |
219 | u8 reg = ina3221_curr_reg[attr][channel]; | |
220 | int regval, current_ma, voltage_uv; | |
221 | ||
222 | /* clamp current */ | |
223 | current_ma = clamp_val(val, | |
224 | INT_MIN / resistance_uo, | |
225 | INT_MAX / resistance_uo); | |
226 | ||
227 | voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); | |
228 | ||
229 | /* clamp voltage */ | |
230 | voltage_uv = clamp_val(voltage_uv, -163800, 163800); | |
231 | ||
232 | /* 1 / 40uV(scale) << 3(register shift) = 5 */ | |
233 | regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; | |
a9e9dd9c | 234 | |
d4b0166d | 235 | return regmap_write(ina->regmap, reg, regval); |
a9e9dd9c NC |
236 | } |
237 | ||
d4b0166d | 238 | static int ina3221_write_enable(struct device *dev, int channel, bool enable) |
a9e9dd9c | 239 | { |
a9e9dd9c | 240 | struct ina3221_data *ina = dev_get_drvdata(dev); |
a9e9dd9c | 241 | u16 config, mask = INA3221_CONFIG_CHx_EN(channel); |
a9e9dd9c NC |
242 | int ret; |
243 | ||
a9e9dd9c NC |
244 | config = enable ? mask : 0; |
245 | ||
246 | /* Enable or disable the channel */ | |
247 | ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); | |
248 | if (ret) | |
249 | return ret; | |
250 | ||
251 | /* Cache the latest config register value */ | |
252 | ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); | |
253 | if (ret) | |
254 | return ret; | |
255 | ||
d4b0166d | 256 | return 0; |
a9e9dd9c NC |
257 | } |
258 | ||
d4b0166d NC |
259 | static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, |
260 | u32 attr, int channel, long *val) | |
7cb6dcff | 261 | { |
d4b0166d NC |
262 | switch (type) { |
263 | case hwmon_in: | |
264 | /* 0-align channel ID */ | |
265 | return ina3221_read_in(dev, attr, channel - 1, val); | |
266 | case hwmon_curr: | |
267 | return ina3221_read_curr(dev, attr, channel, val); | |
268 | default: | |
269 | return -EOPNOTSUPP; | |
270 | } | |
7cb6dcff AD |
271 | } |
272 | ||
d4b0166d NC |
273 | static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, |
274 | u32 attr, int channel, long val) | |
7cb6dcff | 275 | { |
d4b0166d NC |
276 | switch (type) { |
277 | case hwmon_in: | |
278 | /* 0-align channel ID */ | |
279 | return ina3221_write_enable(dev, channel - 1, val); | |
280 | case hwmon_curr: | |
281 | return ina3221_write_curr(dev, attr, channel, val); | |
282 | default: | |
283 | return -EOPNOTSUPP; | |
284 | } | |
7cb6dcff AD |
285 | } |
286 | ||
d4b0166d NC |
287 | static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, |
288 | u32 attr, int channel, const char **str) | |
7cb6dcff | 289 | { |
7cb6dcff | 290 | struct ina3221_data *ina = dev_get_drvdata(dev); |
d4b0166d | 291 | int index = channel - 1; |
7cb6dcff | 292 | |
d4b0166d | 293 | *str = ina->inputs[index].label; |
a9e9dd9c | 294 | |
d4b0166d | 295 | return 0; |
7cb6dcff AD |
296 | } |
297 | ||
d4b0166d NC |
298 | static umode_t ina3221_is_visible(const void *drvdata, |
299 | enum hwmon_sensor_types type, | |
300 | u32 attr, int channel) | |
7cb6dcff | 301 | { |
d4b0166d NC |
302 | const struct ina3221_data *ina = drvdata; |
303 | const struct ina3221_input *input = NULL; | |
304 | ||
305 | switch (type) { | |
306 | case hwmon_in: | |
307 | /* Ignore in0_ */ | |
308 | if (channel == 0) | |
309 | return 0; | |
310 | ||
311 | switch (attr) { | |
312 | case hwmon_in_label: | |
313 | if (channel - 1 <= INA3221_CHANNEL3) | |
314 | input = &ina->inputs[channel - 1]; | |
315 | /* Hide label node if label is not provided */ | |
316 | return (input && input->label) ? 0444 : 0; | |
317 | case hwmon_in_input: | |
318 | return 0444; | |
319 | case hwmon_in_enable: | |
320 | return 0644; | |
321 | default: | |
322 | return 0; | |
323 | } | |
324 | case hwmon_curr: | |
325 | switch (attr) { | |
326 | case hwmon_curr_input: | |
327 | case hwmon_curr_crit_alarm: | |
328 | case hwmon_curr_max_alarm: | |
329 | return 0444; | |
330 | case hwmon_curr_crit: | |
331 | case hwmon_curr_max: | |
332 | return 0644; | |
333 | default: | |
334 | return 0; | |
335 | } | |
336 | default: | |
337 | return 0; | |
338 | } | |
7cb6dcff AD |
339 | } |
340 | ||
d4b0166d NC |
341 | static const u32 ina3221_in_config[] = { |
342 | /* 0: dummy, skipped in is_visible */ | |
343 | HWMON_I_INPUT, | |
344 | /* 1-3: input voltage Channels */ | |
345 | HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, | |
346 | HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, | |
347 | HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, | |
348 | /* 4-6: shunt voltage Channels */ | |
349 | HWMON_I_INPUT, | |
350 | HWMON_I_INPUT, | |
351 | HWMON_I_INPUT, | |
352 | 0 | |
353 | }; | |
7cb6dcff | 354 | |
d4b0166d NC |
355 | static const struct hwmon_channel_info ina3221_in = { |
356 | .type = hwmon_in, | |
357 | .config = ina3221_in_config, | |
358 | }; | |
7cb6dcff | 359 | |
d4b0166d NC |
360 | #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ |
361 | HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ | |
362 | HWMON_C_MAX | HWMON_C_MAX_ALARM) | |
7cb6dcff | 363 | |
d4b0166d NC |
364 | static const u32 ina3221_curr_config[] = { |
365 | INA3221_HWMON_CURR_CONFIG, | |
366 | INA3221_HWMON_CURR_CONFIG, | |
367 | INA3221_HWMON_CURR_CONFIG, | |
368 | 0 | |
369 | }; | |
7cb6dcff | 370 | |
d4b0166d NC |
371 | static const struct hwmon_channel_info ina3221_curr = { |
372 | .type = hwmon_curr, | |
373 | .config = ina3221_curr_config, | |
374 | }; | |
7cb6dcff | 375 | |
d4b0166d NC |
376 | static const struct hwmon_channel_info *ina3221_info[] = { |
377 | &ina3221_in, | |
378 | &ina3221_curr, | |
379 | NULL | |
380 | }; | |
7cb6dcff | 381 | |
d4b0166d NC |
382 | static const struct hwmon_ops ina3221_hwmon_ops = { |
383 | .is_visible = ina3221_is_visible, | |
384 | .read_string = ina3221_read_string, | |
385 | .read = ina3221_read, | |
386 | .write = ina3221_write, | |
387 | }; | |
7cb6dcff | 388 | |
d4b0166d NC |
389 | static const struct hwmon_chip_info ina3221_chip_info = { |
390 | .ops = &ina3221_hwmon_ops, | |
391 | .info = ina3221_info, | |
392 | }; | |
7cb6dcff | 393 | |
d4b0166d | 394 | /* Extra attribute groups */ |
7cb6dcff AD |
395 | static ssize_t ina3221_show_shunt(struct device *dev, |
396 | struct device_attribute *attr, char *buf) | |
397 | { | |
398 | struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); | |
399 | struct ina3221_data *ina = dev_get_drvdata(dev); | |
400 | unsigned int channel = sd_attr->index; | |
a9e9dd9c | 401 | struct ina3221_input *input = &ina->inputs[channel]; |
7cb6dcff | 402 | |
a9e9dd9c | 403 | return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); |
7cb6dcff AD |
404 | } |
405 | ||
406 | static ssize_t ina3221_set_shunt(struct device *dev, | |
407 | struct device_attribute *attr, | |
408 | const char *buf, size_t count) | |
409 | { | |
410 | struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); | |
411 | struct ina3221_data *ina = dev_get_drvdata(dev); | |
412 | unsigned int channel = sd_attr->index; | |
a9e9dd9c | 413 | struct ina3221_input *input = &ina->inputs[channel]; |
9ad0df1a | 414 | int val; |
7cb6dcff AD |
415 | int ret; |
416 | ||
9ad0df1a | 417 | ret = kstrtoint(buf, 0, &val); |
7cb6dcff AD |
418 | if (ret) |
419 | return ret; | |
420 | ||
9ad0df1a | 421 | val = clamp_val(val, 1, INT_MAX); |
7cb6dcff | 422 | |
a9e9dd9c | 423 | input->shunt_resistor = val; |
7cb6dcff AD |
424 | |
425 | return count; | |
426 | } | |
427 | ||
7cb6dcff AD |
428 | /* shunt resistance */ |
429 | static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR, | |
430 | ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1); | |
431 | static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR, | |
432 | ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2); | |
433 | static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR, | |
434 | ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3); | |
435 | ||
7cb6dcff | 436 | static struct attribute *ina3221_attrs[] = { |
7cb6dcff | 437 | &sensor_dev_attr_shunt1_resistor.dev_attr.attr, |
7cb6dcff | 438 | &sensor_dev_attr_shunt2_resistor.dev_attr.attr, |
7cb6dcff | 439 | &sensor_dev_attr_shunt3_resistor.dev_attr.attr, |
7cb6dcff AD |
440 | NULL, |
441 | }; | |
d4b0166d | 442 | ATTRIBUTE_GROUPS(ina3221); |
7cb6dcff AD |
443 | |
444 | static const struct regmap_range ina3221_yes_ranges[] = { | |
c20217b3 | 445 | regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), |
7cb6dcff AD |
446 | regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), |
447 | }; | |
448 | ||
449 | static const struct regmap_access_table ina3221_volatile_table = { | |
450 | .yes_ranges = ina3221_yes_ranges, | |
451 | .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), | |
452 | }; | |
453 | ||
454 | static const struct regmap_config ina3221_regmap_config = { | |
455 | .reg_bits = 8, | |
456 | .val_bits = 16, | |
457 | ||
458 | .cache_type = REGCACHE_RBTREE, | |
459 | .volatile_table = &ina3221_volatile_table, | |
460 | }; | |
461 | ||
a9e9dd9c NC |
462 | static int ina3221_probe_child_from_dt(struct device *dev, |
463 | struct device_node *child, | |
464 | struct ina3221_data *ina) | |
465 | { | |
466 | struct ina3221_input *input; | |
467 | u32 val; | |
468 | int ret; | |
469 | ||
470 | ret = of_property_read_u32(child, "reg", &val); | |
471 | if (ret) { | |
472 | dev_err(dev, "missing reg property of %s\n", child->name); | |
473 | return ret; | |
474 | } else if (val > INA3221_CHANNEL3) { | |
475 | dev_err(dev, "invalid reg %d of %s\n", val, child->name); | |
476 | return ret; | |
477 | } | |
478 | ||
479 | input = &ina->inputs[val]; | |
480 | ||
481 | /* Log the disconnected channel input */ | |
482 | if (!of_device_is_available(child)) { | |
483 | input->disconnected = true; | |
484 | return 0; | |
485 | } | |
486 | ||
487 | /* Save the connected input label if available */ | |
488 | of_property_read_string(child, "label", &input->label); | |
489 | ||
490 | /* Overwrite default shunt resistor value optionally */ | |
a6e43263 NC |
491 | if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { |
492 | if (val < 1 || val > INT_MAX) { | |
493 | dev_err(dev, "invalid shunt resistor value %u of %s\n", | |
494 | val, child->name); | |
495 | return -EINVAL; | |
496 | } | |
a9e9dd9c | 497 | input->shunt_resistor = val; |
a6e43263 | 498 | } |
a9e9dd9c NC |
499 | |
500 | return 0; | |
501 | } | |
502 | ||
503 | static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) | |
504 | { | |
505 | const struct device_node *np = dev->of_node; | |
506 | struct device_node *child; | |
507 | int ret; | |
508 | ||
509 | /* Compatible with non-DT platforms */ | |
510 | if (!np) | |
511 | return 0; | |
512 | ||
513 | for_each_child_of_node(np, child) { | |
514 | ret = ina3221_probe_child_from_dt(dev, child, ina); | |
515 | if (ret) | |
516 | return ret; | |
517 | } | |
518 | ||
519 | return 0; | |
520 | } | |
521 | ||
7cb6dcff AD |
522 | static int ina3221_probe(struct i2c_client *client, |
523 | const struct i2c_device_id *id) | |
524 | { | |
525 | struct device *dev = &client->dev; | |
526 | struct ina3221_data *ina; | |
527 | struct device *hwmon_dev; | |
528 | int i, ret; | |
529 | ||
530 | ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); | |
531 | if (!ina) | |
532 | return -ENOMEM; | |
533 | ||
534 | ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); | |
535 | if (IS_ERR(ina->regmap)) { | |
536 | dev_err(dev, "Unable to allocate register map\n"); | |
537 | return PTR_ERR(ina->regmap); | |
538 | } | |
539 | ||
540 | for (i = 0; i < F_MAX_FIELDS; i++) { | |
541 | ina->fields[i] = devm_regmap_field_alloc(dev, | |
542 | ina->regmap, | |
543 | ina3221_reg_fields[i]); | |
544 | if (IS_ERR(ina->fields[i])) { | |
545 | dev_err(dev, "Unable to allocate regmap fields\n"); | |
546 | return PTR_ERR(ina->fields[i]); | |
547 | } | |
548 | } | |
549 | ||
550 | for (i = 0; i < INA3221_NUM_CHANNELS; i++) | |
a9e9dd9c NC |
551 | ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; |
552 | ||
553 | ret = ina3221_probe_from_dt(dev, ina); | |
554 | if (ret) { | |
555 | dev_err(dev, "Unable to probe from device tree\n"); | |
556 | return ret; | |
557 | } | |
7cb6dcff AD |
558 | |
559 | ret = regmap_field_write(ina->fields[F_RST], true); | |
560 | if (ret) { | |
561 | dev_err(dev, "Unable to reset device\n"); | |
562 | return ret; | |
563 | } | |
564 | ||
a9e9dd9c NC |
565 | /* Sync config register after reset */ |
566 | ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); | |
567 | if (ret) | |
568 | return ret; | |
569 | ||
570 | /* Disable channels if their inputs are disconnected */ | |
571 | for (i = 0; i < INA3221_NUM_CHANNELS; i++) { | |
572 | if (ina->inputs[i].disconnected) | |
573 | ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); | |
574 | } | |
575 | ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); | |
576 | if (ret) | |
577 | return ret; | |
578 | ||
59d608e1 NC |
579 | dev_set_drvdata(dev, ina); |
580 | ||
d4b0166d NC |
581 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, |
582 | &ina3221_chip_info, | |
583 | ina3221_groups); | |
7cb6dcff AD |
584 | if (IS_ERR(hwmon_dev)) { |
585 | dev_err(dev, "Unable to register hwmon device\n"); | |
586 | return PTR_ERR(hwmon_dev); | |
587 | } | |
588 | ||
589 | return 0; | |
590 | } | |
591 | ||
ead21c77 | 592 | static int __maybe_unused ina3221_suspend(struct device *dev) |
59d608e1 NC |
593 | { |
594 | struct ina3221_data *ina = dev_get_drvdata(dev); | |
595 | int ret; | |
596 | ||
597 | /* Save config register value and enable cache-only */ | |
598 | ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); | |
599 | if (ret) | |
600 | return ret; | |
601 | ||
602 | /* Set to power-down mode for power saving */ | |
603 | ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, | |
604 | INA3221_CONFIG_MODE_MASK, | |
605 | INA3221_CONFIG_MODE_POWERDOWN); | |
606 | if (ret) | |
607 | return ret; | |
608 | ||
609 | regcache_cache_only(ina->regmap, true); | |
610 | regcache_mark_dirty(ina->regmap); | |
611 | ||
612 | return 0; | |
613 | } | |
614 | ||
ead21c77 | 615 | static int __maybe_unused ina3221_resume(struct device *dev) |
59d608e1 NC |
616 | { |
617 | struct ina3221_data *ina = dev_get_drvdata(dev); | |
618 | int ret; | |
619 | ||
620 | regcache_cache_only(ina->regmap, false); | |
621 | ||
622 | /* Software reset the chip */ | |
623 | ret = regmap_field_write(ina->fields[F_RST], true); | |
624 | if (ret) { | |
625 | dev_err(dev, "Unable to reset device\n"); | |
626 | return ret; | |
627 | } | |
628 | ||
629 | /* Restore cached register values to hardware */ | |
630 | ret = regcache_sync(ina->regmap); | |
631 | if (ret) | |
632 | return ret; | |
633 | ||
634 | /* Restore config register value to hardware */ | |
635 | ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); | |
636 | if (ret) | |
637 | return ret; | |
638 | ||
639 | return 0; | |
640 | } | |
59d608e1 NC |
641 | |
642 | static const struct dev_pm_ops ina3221_pm = { | |
643 | SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume) | |
644 | }; | |
645 | ||
7cb6dcff AD |
646 | static const struct of_device_id ina3221_of_match_table[] = { |
647 | { .compatible = "ti,ina3221", }, | |
648 | { /* sentinel */ } | |
649 | }; | |
650 | MODULE_DEVICE_TABLE(of, ina3221_of_match_table); | |
651 | ||
652 | static const struct i2c_device_id ina3221_ids[] = { | |
653 | { "ina3221", 0 }, | |
654 | { /* sentinel */ } | |
655 | }; | |
656 | MODULE_DEVICE_TABLE(i2c, ina3221_ids); | |
657 | ||
658 | static struct i2c_driver ina3221_i2c_driver = { | |
659 | .probe = ina3221_probe, | |
660 | .driver = { | |
661 | .name = INA3221_DRIVER_NAME, | |
662 | .of_match_table = ina3221_of_match_table, | |
59d608e1 | 663 | .pm = &ina3221_pm, |
7cb6dcff AD |
664 | }, |
665 | .id_table = ina3221_ids, | |
666 | }; | |
667 | module_i2c_driver(ina3221_i2c_driver); | |
668 | ||
669 | MODULE_AUTHOR("Andrew F. Davis <[email protected]>"); | |
670 | MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); | |
671 | MODULE_LICENSE("GPL v2"); |