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)
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 */
112120struct 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
123132static 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 */
130173static 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
200251static 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
270327static 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
643742static 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