Skip to content

Commit 2057bdf

Browse files
Nicolin Chengroeck
authored andcommitted
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected] [groeck: summation->sum in documentation and label] Signed-off-by: Guenter Roeck <[email protected]>
1 parent 5ff0275 commit 2057bdf

File tree

2 files changed

+153
-22
lines changed

2 files changed

+153
-22
lines changed

Documentation/hwmon/ina3221.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ curr[123]_max Warning alert current(mA) setting, activates the
4141
average is above this value.
4242
curr[123]_max_alarm Warning alert current limit exceeded
4343
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.
4456
samples Number of samples using in the averaging mode.
4557

4658
Supports the list of number of samples:

drivers/hwmon/ina3221.c

Lines changed: 141 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#define INA3221_WARN2 0x0a
3232
#define INA3221_CRIT3 0x0b
3333
#define INA3221_WARN3 0x0c
34+
#define INA3221_SHUNT_SUM 0x0d
35+
#define INA3221_CRIT_SUM 0x0e
3436
#define INA3221_MASK_ENABLE 0x0f
3537

3638
#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0)
@@ -50,6 +52,8 @@
5052
#define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12)
5153
#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x))
5254

55+
#define INA3221_MASK_ENABLE_SCC_MASK GENMASK(14, 12)
56+
5357
#define INA3221_CONFIG_DEFAULT 0x7127
5458
#define INA3221_RSHUNT_DEFAULT 10000
5559

@@ -60,9 +64,11 @@ enum ina3221_fields {
6064
/* Status Flags */
6165
F_CVRF,
6266

63-
/* Alert Flags */
67+
/* Warning Flags */
6468
F_WF3, F_WF2, F_WF1,
65-
F_CF3, F_CF2, F_CF1,
69+
70+
/* Alert Flags: SF is the summation-alert flag */
71+
F_SF, F_CF3, F_CF2, F_CF1,
6672

6773
/* sentinel */
6874
F_MAX_FIELDS
@@ -75,6 +81,7 @@ static const struct reg_field ina3221_reg_fields[] = {
7581
[F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
7682
[F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
7783
[F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
84+
[F_SF] = REG_FIELD(INA3221_MASK_ENABLE, 6, 6),
7885
[F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
7986
[F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
8087
[F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
@@ -107,6 +114,7 @@ struct ina3221_input {
107114
* @inputs: Array of channel input source specific structures
108115
* @lock: mutex lock to serialize sysfs attribute accesses
109116
* @reg_config: Register value of INA3221_CONFIG
117+
* @summation_shunt_resistor: equivalent shunt resistor value for summation
110118
* @single_shot: running in single-shot operating mode
111119
*/
112120
struct ina3221_data {
@@ -116,16 +124,51 @@ struct ina3221_data {
116124
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
117125
struct mutex lock;
118126
u32 reg_config;
127+
int summation_shunt_resistor;
119128

120129
bool single_shot;
121130
};
122131

123132
static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
124133
{
134+
/* Summation channel checks shunt resistor values */
135+
if (channel > INA3221_CHANNEL3)
136+
return ina->summation_shunt_resistor != 0;
137+
125138
return pm_runtime_active(ina->pm_dev) &&
126139
(ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
127140
}
128141

142+
/**
143+
* Helper function to return the resistor value for current summation.
144+
*
145+
* There is a condition to calculate current summation -- all the shunt
146+
* resistor values should be the same, so as to simply fit the formula:
147+
* current summation = shunt voltage summation / shunt resistor
148+
*
149+
* Returns the equivalent shunt resistor value on success or 0 on failure
150+
*/
151+
static inline int ina3221_summation_shunt_resistor(struct ina3221_data *ina)
152+
{
153+
struct ina3221_input *input = ina->inputs;
154+
int i, shunt_resistor = 0;
155+
156+
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
157+
if (input[i].disconnected || !input[i].shunt_resistor)
158+
continue;
159+
if (!shunt_resistor) {
160+
/* Found the reference shunt resistor value */
161+
shunt_resistor = input[i].shunt_resistor;
162+
} else {
163+
/* No summation if resistor values are different */
164+
if (shunt_resistor != input[i].shunt_resistor)
165+
return 0;
166+
}
167+
}
168+
169+
return shunt_resistor;
170+
}
171+
129172
/* Lookup table for Bus and Shunt conversion times in usec */
130173
static const u16 ina3221_conv_time[] = {
131174
140, 204, 332, 588, 1100, 2116, 4156, 8244,
@@ -183,7 +226,14 @@ static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
183226
if (ret)
184227
return ret;
185228

186-
*val = sign_extend32(regval >> 3, 12);
229+
/*
230+
* Shunt Voltage Sum register has 14-bit value with 1-bit shift
231+
* Other Shunt Voltage registers have 12 bits with 3-bit shift
232+
*/
233+
if (reg == INA3221_SHUNT_SUM)
234+
*val = sign_extend32(regval >> 1, 14);
235+
else
236+
*val = sign_extend32(regval >> 3, 12);
187237

188238
return 0;
189239
}
@@ -195,6 +245,7 @@ static const u8 ina3221_in_reg[] = {
195245
INA3221_SHUNT1,
196246
INA3221_SHUNT2,
197247
INA3221_SHUNT3,
248+
INA3221_SHUNT_SUM,
198249
};
199250

200251
static int ina3221_read_chip(struct device *dev, u32 attr, long *val)
@@ -224,8 +275,12 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
224275
u8 reg = ina3221_in_reg[channel];
225276
int regval, ret;
226277

227-
/* Translate shunt channel index to sensor channel index */
228-
channel %= INA3221_NUM_CHANNELS;
278+
/*
279+
* Translate shunt channel index to sensor channel index except
280+
* the 7th channel (6 since being 0-aligned) is for summation.
281+
*/
282+
if (channel != 6)
283+
channel %= INA3221_NUM_CHANNELS;
229284

230285
switch (attr) {
231286
case hwmon_in_input:
@@ -259,22 +314,29 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
259314
}
260315
}
261316

262-
static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = {
263-
[hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 },
264-
[hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 },
265-
[hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 },
266-
[hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 },
267-
[hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 },
317+
static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS + 1] = {
318+
[hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2,
319+
INA3221_SHUNT3, INA3221_SHUNT_SUM },
320+
[hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3, 0 },
321+
[hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2,
322+
INA3221_CRIT3, INA3221_CRIT_SUM },
323+
[hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3, 0 },
324+
[hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3, F_SF },
268325
};
269326

270327
static int ina3221_read_curr(struct device *dev, u32 attr,
271328
int channel, long *val)
272329
{
273330
struct ina3221_data *ina = dev_get_drvdata(dev);
274-
struct ina3221_input *input = &ina->inputs[channel];
275-
int resistance_uo = input->shunt_resistor;
331+
struct ina3221_input *input = ina->inputs;
276332
u8 reg = ina3221_curr_reg[attr][channel];
277-
int regval, voltage_nv, ret;
333+
int resistance_uo, voltage_nv;
334+
int regval, ret;
335+
336+
if (channel > INA3221_CHANNEL3)
337+
resistance_uo = ina->summation_shunt_resistor;
338+
else
339+
resistance_uo = input[channel].shunt_resistor;
278340

279341
switch (attr) {
280342
case hwmon_curr_input:
@@ -293,6 +355,9 @@ static int ina3221_read_curr(struct device *dev, u32 attr,
293355
/* fall through */
294356
case hwmon_curr_crit:
295357
case hwmon_curr_max:
358+
if (!resistance_uo)
359+
return -ENODATA;
360+
296361
ret = ina3221_read_value(ina, reg, &regval);
297362
if (ret)
298363
return ret;
@@ -366,10 +431,18 @@ static int ina3221_write_curr(struct device *dev, u32 attr,
366431
int channel, long val)
367432
{
368433
struct ina3221_data *ina = dev_get_drvdata(dev);
369-
struct ina3221_input *input = &ina->inputs[channel];
370-
int resistance_uo = input->shunt_resistor;
434+
struct ina3221_input *input = ina->inputs;
371435
u8 reg = ina3221_curr_reg[attr][channel];
372-
int regval, current_ma, voltage_uv;
436+
int resistance_uo, current_ma, voltage_uv;
437+
int regval;
438+
439+
if (channel > INA3221_CHANNEL3)
440+
resistance_uo = ina->summation_shunt_resistor;
441+
else
442+
resistance_uo = input[channel].shunt_resistor;
443+
444+
if (!resistance_uo)
445+
return -EOPNOTSUPP;
373446

374447
/* clamp current */
375448
current_ma = clamp_val(val,
@@ -381,8 +454,21 @@ static int ina3221_write_curr(struct device *dev, u32 attr,
381454
/* clamp voltage */
382455
voltage_uv = clamp_val(voltage_uv, -163800, 163800);
383456

384-
/* 1 / 40uV(scale) << 3(register shift) = 5 */
385-
regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
457+
/*
458+
* Formula to convert voltage_uv to register value:
459+
* regval = (voltage_uv / scale) << shift
460+
* Note:
461+
* The scale is 40uV for all shunt voltage registers
462+
* Shunt Voltage Sum register left-shifts 1 bit
463+
* All other Shunt Voltage registers shift 3 bits
464+
* Results:
465+
* SHUNT_SUM: (1 / 40uV) << 1 = 1 / 20uV
466+
* SHUNT[1-3]: (1 / 40uV) << 3 = 1 / 5uV
467+
*/
468+
if (reg == INA3221_SHUNT_SUM)
469+
regval = DIV_ROUND_CLOSEST(voltage_uv, 20) & 0xfffe;
470+
else
471+
regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
386472

387473
return regmap_write(ina->regmap, reg, regval);
388474
}
@@ -499,7 +585,10 @@ static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type,
499585
struct ina3221_data *ina = dev_get_drvdata(dev);
500586
int index = channel - 1;
501587

502-
*str = ina->inputs[index].label;
588+
if (channel == 7)
589+
*str = "sum of shunt voltages";
590+
else
591+
*str = ina->inputs[index].label;
503592

504593
return 0;
505594
}
@@ -529,6 +618,8 @@ static umode_t ina3221_is_visible(const void *drvdata,
529618
case hwmon_in_label:
530619
if (channel - 1 <= INA3221_CHANNEL3)
531620
input = &ina->inputs[channel - 1];
621+
else if (channel == 7)
622+
return 0444;
532623
/* Hide label node if label is not provided */
533624
return (input && input->label) ? 0444 : 0;
534625
case hwmon_in_input:
@@ -573,11 +664,16 @@ static const struct hwmon_channel_info *ina3221_info[] = {
573664
/* 4-6: shunt voltage Channels */
574665
HWMON_I_INPUT,
575666
HWMON_I_INPUT,
576-
HWMON_I_INPUT),
667+
HWMON_I_INPUT,
668+
/* 7: summation of shunt voltage channels */
669+
HWMON_I_INPUT | HWMON_I_LABEL),
577670
HWMON_CHANNEL_INFO(curr,
671+
/* 1-3: current channels*/
672+
INA3221_HWMON_CURR_CONFIG,
578673
INA3221_HWMON_CURR_CONFIG,
579674
INA3221_HWMON_CURR_CONFIG,
580-
INA3221_HWMON_CURR_CONFIG),
675+
/* 4: summation of current channels */
676+
HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM),
581677
NULL
582678
};
583679

@@ -624,6 +720,9 @@ static ssize_t ina3221_shunt_store(struct device *dev,
624720

625721
input->shunt_resistor = val;
626722

723+
/* Update summation_shunt_resistor for summation channel */
724+
ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina);
725+
627726
return count;
628727
}
629728

@@ -642,6 +741,7 @@ ATTRIBUTE_GROUPS(ina3221);
642741

643742
static const struct regmap_range ina3221_yes_ranges[] = {
644743
regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
744+
regmap_reg_range(INA3221_SHUNT_SUM, INA3221_SHUNT_SUM),
645745
regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
646746
};
647747

@@ -772,6 +872,9 @@ static int ina3221_probe(struct i2c_client *client,
772872
ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i);
773873
}
774874

875+
/* Initialize summation_shunt_resistor for summation channel control */
876+
ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina);
877+
775878
ina->pm_dev = dev;
776879
mutex_init(&ina->lock);
777880
dev_set_drvdata(dev, ina);
@@ -875,6 +978,22 @@ static int __maybe_unused ina3221_resume(struct device *dev)
875978
if (ret)
876979
return ret;
877980

981+
/* Initialize summation channel control */
982+
if (ina->summation_shunt_resistor) {
983+
/*
984+
* Take all three channels into summation by default
985+
* Shunt measurements of disconnected channels should
986+
* be 0, so it does not matter for summation.
987+
*/
988+
ret = regmap_update_bits(ina->regmap, INA3221_MASK_ENABLE,
989+
INA3221_MASK_ENABLE_SCC_MASK,
990+
INA3221_MASK_ENABLE_SCC_MASK);
991+
if (ret) {
992+
dev_err(dev, "Unable to control summation channel\n");
993+
return ret;
994+
}
995+
}
996+
878997
return 0;
879998
}
880999

0 commit comments

Comments
 (0)