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

hwmon: (ina3221) Add summation feature support

This patch implements the summation feature of INA3221, mainly the
SCC (enabling) and SF (warning flag) bits of MASK_ENABLE register,
INA3221_SHUNT_SUM (summation of shunt voltages) register, and the
INA3221_CRIT_SUM (its critical alert setting) register.

Although the summation feature allows user to select which channels
to be added to the result, as an initial support, this patch simply
selects all channels by default, with one only condition: all shunt
resistor values need to be the same. This is because the summation
of current channels can be only accurately calculated, using shunt
voltage sum register, if all shunt resistors are equivalent.

Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
Link: https://lore.kernel.org/r/20191016235702.22039-1-nicoleotsuka@gmail.com
[groeck: summation->sum in documentation and label]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Nicolin Chen and committed by
Guenter Roeck
2057bdfb 5ff02752

+153 -22
+12
Documentation/hwmon/ina3221.rst
··· 41 41 average is above this value. 42 42 curr[123]_max_alarm Warning alert current limit exceeded 43 43 in[456]_input Shunt voltage(uV) for channels 1, 2, and 3 respectively 44 + in7_input Sum of shunt voltage(uV) channels 45 + in7_label Channel label for sum of shunt voltage 46 + curr4_input Sum of current(mA) measurement channels, 47 + (only available when all channels use the same resistor 48 + value for their shunt resistors) 49 + curr4_crit Critical alert current(mA) setting for sum of current 50 + measurements, activates the corresponding alarm 51 + when the respective current is above this value 52 + (only effective when all channels use the same resistor 53 + value for their shunt resistors) 54 + curr4_crit_alarm Critical alert current limit exceeded for sum of 55 + current measurements. 44 56 samples Number of samples using in the averaging mode. 45 57 46 58 Supports the list of number of samples:
+141 -22
drivers/hwmon/ina3221.c
··· 31 31 #define INA3221_WARN2 0x0a 32 32 #define INA3221_CRIT3 0x0b 33 33 #define INA3221_WARN3 0x0c 34 + #define INA3221_SHUNT_SUM 0x0d 35 + #define INA3221_CRIT_SUM 0x0e 34 36 #define INA3221_MASK_ENABLE 0x0f 35 37 36 38 #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) ··· 52 50 #define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12) 53 51 #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) 54 52 53 + #define INA3221_MASK_ENABLE_SCC_MASK GENMASK(14, 12) 54 + 55 55 #define INA3221_CONFIG_DEFAULT 0x7127 56 56 #define INA3221_RSHUNT_DEFAULT 10000 57 57 ··· 64 60 /* Status Flags */ 65 61 F_CVRF, 66 62 67 - /* Alert Flags */ 63 + /* Warning Flags */ 68 64 F_WF3, F_WF2, F_WF1, 69 - F_CF3, F_CF2, F_CF1, 65 + 66 + /* Alert Flags: SF is the summation-alert flag */ 67 + F_SF, F_CF3, F_CF2, F_CF1, 70 68 71 69 /* sentinel */ 72 70 F_MAX_FIELDS ··· 81 75 [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), 82 76 [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), 83 77 [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), 78 + [F_SF] = REG_FIELD(INA3221_MASK_ENABLE, 6, 6), 84 79 [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), 85 80 [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), 86 81 [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), ··· 114 107 * @inputs: Array of channel input source specific structures 115 108 * @lock: mutex lock to serialize sysfs attribute accesses 116 109 * @reg_config: Register value of INA3221_CONFIG 110 + * @summation_shunt_resistor: equivalent shunt resistor value for summation 117 111 * @single_shot: running in single-shot operating mode 118 112 */ 119 113 struct ina3221_data { ··· 124 116 struct ina3221_input inputs[INA3221_NUM_CHANNELS]; 125 117 struct mutex lock; 126 118 u32 reg_config; 119 + int summation_shunt_resistor; 127 120 128 121 bool single_shot; 129 122 }; 130 123 131 124 static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 132 125 { 126 + /* Summation channel checks shunt resistor values */ 127 + if (channel > INA3221_CHANNEL3) 128 + return ina->summation_shunt_resistor != 0; 129 + 133 130 return pm_runtime_active(ina->pm_dev) && 134 131 (ina->reg_config & INA3221_CONFIG_CHx_EN(channel)); 132 + } 133 + 134 + /** 135 + * Helper function to return the resistor value for current summation. 136 + * 137 + * There is a condition to calculate current summation -- all the shunt 138 + * resistor values should be the same, so as to simply fit the formula: 139 + * current summation = shunt voltage summation / shunt resistor 140 + * 141 + * Returns the equivalent shunt resistor value on success or 0 on failure 142 + */ 143 + static inline int ina3221_summation_shunt_resistor(struct ina3221_data *ina) 144 + { 145 + struct ina3221_input *input = ina->inputs; 146 + int i, shunt_resistor = 0; 147 + 148 + for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 149 + if (input[i].disconnected || !input[i].shunt_resistor) 150 + continue; 151 + if (!shunt_resistor) { 152 + /* Found the reference shunt resistor value */ 153 + shunt_resistor = input[i].shunt_resistor; 154 + } else { 155 + /* No summation if resistor values are different */ 156 + if (shunt_resistor != input[i].shunt_resistor) 157 + return 0; 158 + } 159 + } 160 + 161 + return shunt_resistor; 135 162 } 136 163 137 164 /* Lookup table for Bus and Shunt conversion times in usec */ ··· 226 183 if (ret) 227 184 return ret; 228 185 229 - *val = sign_extend32(regval >> 3, 12); 186 + /* 187 + * Shunt Voltage Sum register has 14-bit value with 1-bit shift 188 + * Other Shunt Voltage registers have 12 bits with 3-bit shift 189 + */ 190 + if (reg == INA3221_SHUNT_SUM) 191 + *val = sign_extend32(regval >> 1, 14); 192 + else 193 + *val = sign_extend32(regval >> 3, 12); 230 194 231 195 return 0; 232 196 } ··· 245 195 INA3221_SHUNT1, 246 196 INA3221_SHUNT2, 247 197 INA3221_SHUNT3, 198 + INA3221_SHUNT_SUM, 248 199 }; 249 200 250 201 static int ina3221_read_chip(struct device *dev, u32 attr, long *val) ··· 275 224 u8 reg = ina3221_in_reg[channel]; 276 225 int regval, ret; 277 226 278 - /* Translate shunt channel index to sensor channel index */ 279 - channel %= INA3221_NUM_CHANNELS; 227 + /* 228 + * Translate shunt channel index to sensor channel index except 229 + * the 7th channel (6 since being 0-aligned) is for summation. 230 + */ 231 + if (channel != 6) 232 + channel %= INA3221_NUM_CHANNELS; 280 233 281 234 switch (attr) { 282 235 case hwmon_in_input: ··· 314 259 } 315 260 } 316 261 317 - static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { 318 - [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, 319 - [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, 320 - [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, 321 - [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, 322 - [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, 262 + static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS + 1] = { 263 + [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, 264 + INA3221_SHUNT3, INA3221_SHUNT_SUM }, 265 + [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3, 0 }, 266 + [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, 267 + INA3221_CRIT3, INA3221_CRIT_SUM }, 268 + [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3, 0 }, 269 + [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3, F_SF }, 323 270 }; 324 271 325 272 static int ina3221_read_curr(struct device *dev, u32 attr, 326 273 int channel, long *val) 327 274 { 328 275 struct ina3221_data *ina = dev_get_drvdata(dev); 329 - struct ina3221_input *input = &ina->inputs[channel]; 330 - int resistance_uo = input->shunt_resistor; 276 + struct ina3221_input *input = ina->inputs; 331 277 u8 reg = ina3221_curr_reg[attr][channel]; 332 - int regval, voltage_nv, ret; 278 + int resistance_uo, voltage_nv; 279 + int regval, ret; 280 + 281 + if (channel > INA3221_CHANNEL3) 282 + resistance_uo = ina->summation_shunt_resistor; 283 + else 284 + resistance_uo = input[channel].shunt_resistor; 333 285 334 286 switch (attr) { 335 287 case hwmon_curr_input: ··· 355 293 /* fall through */ 356 294 case hwmon_curr_crit: 357 295 case hwmon_curr_max: 296 + if (!resistance_uo) 297 + return -ENODATA; 298 + 358 299 ret = ina3221_read_value(ina, reg, &regval); 359 300 if (ret) 360 301 return ret; ··· 431 366 int channel, long val) 432 367 { 433 368 struct ina3221_data *ina = dev_get_drvdata(dev); 434 - struct ina3221_input *input = &ina->inputs[channel]; 435 - int resistance_uo = input->shunt_resistor; 369 + struct ina3221_input *input = ina->inputs; 436 370 u8 reg = ina3221_curr_reg[attr][channel]; 437 - int regval, current_ma, voltage_uv; 371 + int resistance_uo, current_ma, voltage_uv; 372 + int regval; 373 + 374 + if (channel > INA3221_CHANNEL3) 375 + resistance_uo = ina->summation_shunt_resistor; 376 + else 377 + resistance_uo = input[channel].shunt_resistor; 378 + 379 + if (!resistance_uo) 380 + return -EOPNOTSUPP; 438 381 439 382 /* clamp current */ 440 383 current_ma = clamp_val(val, ··· 454 381 /* clamp voltage */ 455 382 voltage_uv = clamp_val(voltage_uv, -163800, 163800); 456 383 457 - /* 1 / 40uV(scale) << 3(register shift) = 5 */ 458 - regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 384 + /* 385 + * Formula to convert voltage_uv to register value: 386 + * regval = (voltage_uv / scale) << shift 387 + * Note: 388 + * The scale is 40uV for all shunt voltage registers 389 + * Shunt Voltage Sum register left-shifts 1 bit 390 + * All other Shunt Voltage registers shift 3 bits 391 + * Results: 392 + * SHUNT_SUM: (1 / 40uV) << 1 = 1 / 20uV 393 + * SHUNT[1-3]: (1 / 40uV) << 3 = 1 / 5uV 394 + */ 395 + if (reg == INA3221_SHUNT_SUM) 396 + regval = DIV_ROUND_CLOSEST(voltage_uv, 20) & 0xfffe; 397 + else 398 + regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 459 399 460 400 return regmap_write(ina->regmap, reg, regval); 461 401 } ··· 585 499 struct ina3221_data *ina = dev_get_drvdata(dev); 586 500 int index = channel - 1; 587 501 588 - *str = ina->inputs[index].label; 502 + if (channel == 7) 503 + *str = "sum of shunt voltages"; 504 + else 505 + *str = ina->inputs[index].label; 589 506 590 507 return 0; 591 508 } ··· 618 529 case hwmon_in_label: 619 530 if (channel - 1 <= INA3221_CHANNEL3) 620 531 input = &ina->inputs[channel - 1]; 532 + else if (channel == 7) 533 + return 0444; 621 534 /* Hide label node if label is not provided */ 622 535 return (input && input->label) ? 0444 : 0; 623 536 case hwmon_in_input: ··· 664 573 /* 4-6: shunt voltage Channels */ 665 574 HWMON_I_INPUT, 666 575 HWMON_I_INPUT, 667 - HWMON_I_INPUT), 576 + HWMON_I_INPUT, 577 + /* 7: summation of shunt voltage channels */ 578 + HWMON_I_INPUT | HWMON_I_LABEL), 668 579 HWMON_CHANNEL_INFO(curr, 580 + /* 1-3: current channels*/ 669 581 INA3221_HWMON_CURR_CONFIG, 670 582 INA3221_HWMON_CURR_CONFIG, 671 - INA3221_HWMON_CURR_CONFIG), 583 + INA3221_HWMON_CURR_CONFIG, 584 + /* 4: summation of current channels */ 585 + HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM), 672 586 NULL 673 587 }; 674 588 ··· 720 624 721 625 input->shunt_resistor = val; 722 626 627 + /* Update summation_shunt_resistor for summation channel */ 628 + ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina); 629 + 723 630 return count; 724 631 } 725 632 ··· 741 642 742 643 static const struct regmap_range ina3221_yes_ranges[] = { 743 644 regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 645 + regmap_reg_range(INA3221_SHUNT_SUM, INA3221_SHUNT_SUM), 744 646 regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 745 647 }; 746 648 ··· 872 772 ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 873 773 } 874 774 775 + /* Initialize summation_shunt_resistor for summation channel control */ 776 + ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina); 777 + 875 778 ina->pm_dev = dev; 876 779 mutex_init(&ina->lock); 877 780 dev_set_drvdata(dev, ina); ··· 977 874 ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 978 875 if (ret) 979 876 return ret; 877 + 878 + /* Initialize summation channel control */ 879 + if (ina->summation_shunt_resistor) { 880 + /* 881 + * Take all three channels into summation by default 882 + * Shunt measurements of disconnected channels should 883 + * be 0, so it does not matter for summation. 884 + */ 885 + ret = regmap_update_bits(ina->regmap, INA3221_MASK_ENABLE, 886 + INA3221_MASK_ENABLE_SCC_MASK, 887 + INA3221_MASK_ENABLE_SCC_MASK); 888 + if (ret) { 889 + dev_err(dev, "Unable to control summation channel\n"); 890 + return ret; 891 + } 892 + } 980 893 981 894 return 0; 982 895 }