6
6
* Copyright (C) 2021 Nathan Rossi <[email protected] >
7
7
*/
8
8
9
+ #include <linux/bitops.h>
9
10
#include <linux/err.h>
10
11
#include <linux/hwmon.h>
11
12
#include <linux/i2c.h>
107
108
#define INA238_DIE_TEMP_LSB 1250000 /* 125.0000 mC/lsb */
108
109
#define SQ52206_BUS_VOLTAGE_LSB 3750 /* 3.75 mV/lsb */
109
110
#define SQ52206_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */
111
+ #define INA228_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */
110
112
111
113
static const struct regmap_config ina238_regmap_config = {
112
114
.max_register = INA238_REGISTERS ,
113
115
.reg_bits = 8 ,
114
116
.val_bits = 16 ,
115
117
};
116
118
117
- enum ina238_ids { ina238 , ina237 , sq52206 };
119
+ enum ina238_ids { ina238 , ina237 , sq52206 , ina228 };
118
120
119
121
struct ina238_config {
122
+ bool has_20bit_voltage_current ; /* vshunt, vbus and current are 20-bit fields */
120
123
bool has_power_highest ; /* chip detection power peak */
121
124
bool has_energy ; /* chip detection energy */
122
125
u8 temp_shift ; /* fixed parameters for temp calculate */
@@ -137,6 +140,7 @@ struct ina238_data {
137
140
138
141
static const struct ina238_config ina238_config [] = {
139
142
[ina238 ] = {
143
+ .has_20bit_voltage_current = false,
140
144
.has_energy = false,
141
145
.has_power_highest = false,
142
146
.temp_shift = 4 ,
@@ -146,6 +150,7 @@ static const struct ina238_config ina238_config[] = {
146
150
.temp_lsb = INA238_DIE_TEMP_LSB ,
147
151
},
148
152
[ina237 ] = {
153
+ .has_20bit_voltage_current = false,
149
154
.has_energy = false,
150
155
.has_power_highest = false,
151
156
.temp_shift = 4 ,
@@ -155,6 +160,7 @@ static const struct ina238_config ina238_config[] = {
155
160
.temp_lsb = INA238_DIE_TEMP_LSB ,
156
161
},
157
162
[sq52206 ] = {
163
+ .has_20bit_voltage_current = false,
158
164
.has_energy = true,
159
165
.has_power_highest = true,
160
166
.temp_shift = 0 ,
@@ -163,6 +169,16 @@ static const struct ina238_config ina238_config[] = {
163
169
.bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB ,
164
170
.temp_lsb = SQ52206_DIE_TEMP_LSB ,
165
171
},
172
+ [ina228 ] = {
173
+ .has_20bit_voltage_current = true,
174
+ .has_energy = true,
175
+ .has_power_highest = false,
176
+ .temp_shift = 0 ,
177
+ .power_calculate_factor = 20 ,
178
+ .config_default = INA238_CONFIG_DEFAULT ,
179
+ .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB ,
180
+ .temp_lsb = INA228_DIE_TEMP_LSB ,
181
+ },
166
182
};
167
183
168
184
static int ina238_read_reg24 (const struct i2c_client * client , u8 reg , u32 * val )
@@ -199,6 +215,65 @@ static int ina238_read_reg40(const struct i2c_client *client, u8 reg, u64 *val)
199
215
return 0 ;
200
216
}
201
217
218
+ static int ina238_read_field_s20 (const struct i2c_client * client , u8 reg , s32 * val )
219
+ {
220
+ u32 regval ;
221
+ int err ;
222
+
223
+ err = ina238_read_reg24 (client , reg , & regval );
224
+ if (err )
225
+ return err ;
226
+
227
+ /* bits 3-0 Reserved, always zero */
228
+ regval >>= 4 ;
229
+
230
+ * val = sign_extend32 (regval , 19 );
231
+
232
+ return 0 ;
233
+ }
234
+
235
+ static int ina228_read_shunt_voltage (struct device * dev , u32 attr , int channel ,
236
+ long * val )
237
+ {
238
+ struct ina238_data * data = dev_get_drvdata (dev );
239
+ int regval ;
240
+ int err ;
241
+
242
+ err = ina238_read_field_s20 (data -> client , INA238_SHUNT_VOLTAGE , & regval );
243
+ if (err )
244
+ return err ;
245
+
246
+ /*
247
+ * gain of 1 -> LSB / 4
248
+ * This field has 16 bit on ina238. ina228 adds another 4 bits of
249
+ * precision. ina238 conversion factors can still be applied when
250
+ * dividing by 16.
251
+ */
252
+ * val = (regval * INA238_SHUNT_VOLTAGE_LSB ) * data -> gain / (1000 * 4 ) / 16 ;
253
+ return 0 ;
254
+ }
255
+
256
+ static int ina228_read_bus_voltage (struct device * dev , u32 attr , int channel ,
257
+ long * val )
258
+ {
259
+ struct ina238_data * data = dev_get_drvdata (dev );
260
+ int regval ;
261
+ int err ;
262
+
263
+ err = ina238_read_field_s20 (data -> client , INA238_BUS_VOLTAGE , & regval );
264
+ if (err )
265
+ return err ;
266
+
267
+ /*
268
+ * gain of 1 -> LSB / 4
269
+ * This field has 16 bit on ina238. ina228 adds another 4 bits of
270
+ * precision. ina238 conversion factors can still be applied when
271
+ * dividing by 16.
272
+ */
273
+ * val = (regval * data -> config -> bus_voltage_lsb ) / 1000 / 16 ;
274
+ return 0 ;
275
+ }
276
+
202
277
static int ina238_read_in (struct device * dev , u32 attr , int channel ,
203
278
long * val )
204
279
{
@@ -211,6 +286,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
211
286
case 0 :
212
287
switch (attr ) {
213
288
case hwmon_in_input :
289
+ if (data -> config -> has_20bit_voltage_current )
290
+ return ina228_read_shunt_voltage (dev , attr , channel , val );
214
291
reg = INA238_SHUNT_VOLTAGE ;
215
292
break ;
216
293
case hwmon_in_max :
@@ -234,6 +311,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
234
311
case 1 :
235
312
switch (attr ) {
236
313
case hwmon_in_input :
314
+ if (data -> config -> has_20bit_voltage_current )
315
+ return ina228_read_bus_voltage (dev , attr , channel , val );
237
316
reg = INA238_BUS_VOLTAGE ;
238
317
break ;
239
318
case hwmon_in_max :
@@ -341,13 +420,25 @@ static int ina238_read_current(struct device *dev, u32 attr, long *val)
341
420
342
421
switch (attr ) {
343
422
case hwmon_curr_input :
344
- err = regmap_read (data -> regmap , INA238_CURRENT , & regval );
345
- if (err < 0 )
346
- return err ;
423
+ if (data -> config -> has_20bit_voltage_current ) {
424
+ err = ina238_read_field_s20 (data -> client , INA238_CURRENT , & regval );
425
+ if (err )
426
+ return err ;
427
+ } else {
428
+ err = regmap_read (data -> regmap , INA238_CURRENT , & regval );
429
+ if (err < 0 )
430
+ return err ;
431
+ /* sign-extend */
432
+ regval = (s16 )regval ;
433
+ }
347
434
348
435
/* Signed register, fixed 1mA current lsb. result in mA */
349
- * val = div_s64 ((s16 )regval * INA238_FIXED_SHUNT * data -> gain ,
436
+ * val = div_s64 ((s64 )regval * INA238_FIXED_SHUNT * data -> gain ,
350
437
data -> rshunt * 4 );
438
+
439
+ /* Account for 4 bit offset */
440
+ if (data -> config -> has_20bit_voltage_current )
441
+ * val /= 16 ;
351
442
break ;
352
443
default :
353
444
return - EOPNOTSUPP ;
@@ -750,6 +841,7 @@ static int ina238_probe(struct i2c_client *client)
750
841
}
751
842
752
843
static const struct i2c_device_id ina238_id [] = {
844
+ { "ina228" , ina228 },
753
845
{ "ina237" , ina237 },
754
846
{ "ina238" , ina238 },
755
847
{ "sq52206" , sq52206 },
@@ -758,6 +850,10 @@ static const struct i2c_device_id ina238_id[] = {
758
850
MODULE_DEVICE_TABLE (i2c , ina238_id );
759
851
760
852
static const struct of_device_id __maybe_unused ina238_of_match [] = {
853
+ {
854
+ .compatible = "ti,ina228" ,
855
+ .data = (void * )ina228
856
+ },
761
857
{
762
858
.compatible = "ti,ina237" ,
763
859
.data = (void * )ina237
0 commit comments