10
10
#include <linux/bitfield.h>
11
11
#include <linux/i2c.h>
12
12
#include <linux/module.h>
13
+ #include <linux/nvmem-provider.h>
13
14
#include <linux/power_supply.h>
14
15
#include <linux/regmap.h>
15
16
16
17
#include <asm/unaligned.h>
17
18
18
19
/* Nonvolatile registers */
20
+ #define MAX1720X_NXTABLE0 0x80
19
21
#define MAX1720X_NRSENSE 0xCF /* RSense in 10^-5 Ohm */
22
+ #define MAX1720X_NDEVICE_NAME4 0xDF
20
23
21
24
/* ModelGauge m5 */
22
25
#define MAX172XX_STATUS 0x00 /* Status */
@@ -46,6 +49,8 @@ static const char *const max17205_model = "MAX17205";
46
49
47
50
struct max1720x_device_info {
48
51
struct regmap * regmap ;
52
+ struct regmap * regmap_nv ;
53
+ struct i2c_client * ancillary ;
49
54
int rsense ;
50
55
};
51
56
@@ -106,6 +111,134 @@ static const struct regmap_config max1720x_regmap_cfg = {
106
111
.cache_type = REGCACHE_RBTREE ,
107
112
};
108
113
114
+ static const struct regmap_range max1720x_nvmem_allow [] = {
115
+ regmap_reg_range (MAX1720X_NXTABLE0 , MAX1720X_NDEVICE_NAME4 ),
116
+ };
117
+
118
+ static const struct regmap_range max1720x_nvmem_deny [] = {
119
+ regmap_reg_range (0x00 , 0x7F ),
120
+ regmap_reg_range (0xE0 , 0xFF ),
121
+ };
122
+
123
+ static const struct regmap_access_table max1720x_nvmem_regs = {
124
+ .yes_ranges = max1720x_nvmem_allow ,
125
+ .n_yes_ranges = ARRAY_SIZE (max1720x_nvmem_allow ),
126
+ .no_ranges = max1720x_nvmem_deny ,
127
+ .n_no_ranges = ARRAY_SIZE (max1720x_nvmem_deny ),
128
+ };
129
+
130
+ static const struct regmap_config max1720x_nvmem_regmap_cfg = {
131
+ .reg_bits = 8 ,
132
+ .val_bits = 16 ,
133
+ .max_register = MAX1720X_NDEVICE_NAME4 ,
134
+ .val_format_endian = REGMAP_ENDIAN_LITTLE ,
135
+ .rd_table = & max1720x_nvmem_regs ,
136
+ };
137
+
138
+ static const struct nvmem_cell_info max1720x_nvmem_cells [] = {
139
+ { .name = "nXTable0" , .offset = 0 , .bytes = 2 , },
140
+ { .name = "nXTable1" , .offset = 2 , .bytes = 2 , },
141
+ { .name = "nXTable2" , .offset = 4 , .bytes = 2 , },
142
+ { .name = "nXTable3" , .offset = 6 , .bytes = 2 , },
143
+ { .name = "nXTable4" , .offset = 8 , .bytes = 2 , },
144
+ { .name = "nXTable5" , .offset = 10 , .bytes = 2 , },
145
+ { .name = "nXTable6" , .offset = 12 , .bytes = 2 , },
146
+ { .name = "nXTable7" , .offset = 14 , .bytes = 2 , },
147
+ { .name = "nXTable8" , .offset = 16 , .bytes = 2 , },
148
+ { .name = "nXTable9" , .offset = 18 , .bytes = 2 , },
149
+ { .name = "nXTable10" , .offset = 20 , .bytes = 2 , },
150
+ { .name = "nXTable11" , .offset = 22 , .bytes = 2 , },
151
+ { .name = "nUser18C" , .offset = 24 , .bytes = 2 , },
152
+ { .name = "nUser18D" , .offset = 26 , .bytes = 2 , },
153
+ { .name = "nODSCTh" , .offset = 28 , .bytes = 2 , },
154
+ { .name = "nODSCCfg" , .offset = 30 , .bytes = 2 , },
155
+
156
+ { .name = "nOCVTable0" , .offset = 32 , .bytes = 2 , },
157
+ { .name = "nOCVTable1" , .offset = 34 , .bytes = 2 , },
158
+ { .name = "nOCVTable2" , .offset = 36 , .bytes = 2 , },
159
+ { .name = "nOCVTable3" , .offset = 38 , .bytes = 2 , },
160
+ { .name = "nOCVTable4" , .offset = 40 , .bytes = 2 , },
161
+ { .name = "nOCVTable5" , .offset = 42 , .bytes = 2 , },
162
+ { .name = "nOCVTable6" , .offset = 44 , .bytes = 2 , },
163
+ { .name = "nOCVTable7" , .offset = 46 , .bytes = 2 , },
164
+ { .name = "nOCVTable8" , .offset = 48 , .bytes = 2 , },
165
+ { .name = "nOCVTable9" , .offset = 50 , .bytes = 2 , },
166
+ { .name = "nOCVTable10" , .offset = 52 , .bytes = 2 , },
167
+ { .name = "nOCVTable11" , .offset = 54 , .bytes = 2 , },
168
+ { .name = "nIChgTerm" , .offset = 56 , .bytes = 2 , },
169
+ { .name = "nFilterCfg" , .offset = 58 , .bytes = 2 , },
170
+ { .name = "nVEmpty" , .offset = 60 , .bytes = 2 , },
171
+ { .name = "nLearnCfg" , .offset = 62 , .bytes = 2 , },
172
+
173
+ { .name = "nQRTable00" , .offset = 64 , .bytes = 2 , },
174
+ { .name = "nQRTable10" , .offset = 66 , .bytes = 2 , },
175
+ { .name = "nQRTable20" , .offset = 68 , .bytes = 2 , },
176
+ { .name = "nQRTable30" , .offset = 70 , .bytes = 2 , },
177
+ { .name = "nCycles" , .offset = 72 , .bytes = 2 , },
178
+ { .name = "nFullCapNom" , .offset = 74 , .bytes = 2 , },
179
+ { .name = "nRComp0" , .offset = 76 , .bytes = 2 , },
180
+ { .name = "nTempCo" , .offset = 78 , .bytes = 2 , },
181
+ { .name = "nIAvgEmpty" , .offset = 80 , .bytes = 2 , },
182
+ { .name = "nFullCapRep" , .offset = 82 , .bytes = 2 , },
183
+ { .name = "nVoltTemp" , .offset = 84 , .bytes = 2 , },
184
+ { .name = "nMaxMinCurr" , .offset = 86 , .bytes = 2 , },
185
+ { .name = "nMaxMinVolt" , .offset = 88 , .bytes = 2 , },
186
+ { .name = "nMaxMinTemp" , .offset = 90 , .bytes = 2 , },
187
+ { .name = "nSOC" , .offset = 92 , .bytes = 2 , },
188
+ { .name = "nTimerH" , .offset = 94 , .bytes = 2 , },
189
+
190
+ { .name = "nConfig" , .offset = 96 , .bytes = 2 , },
191
+ { .name = "nRippleCfg" , .offset = 98 , .bytes = 2 , },
192
+ { .name = "nMiscCfg" , .offset = 100 , .bytes = 2 , },
193
+ { .name = "nDesignCap" , .offset = 102 , .bytes = 2 , },
194
+ { .name = "nHibCfg" , .offset = 104 , .bytes = 2 , },
195
+ { .name = "nPackCfg" , .offset = 106 , .bytes = 2 , },
196
+ { .name = "nRelaxCfg" , .offset = 108 , .bytes = 2 , },
197
+ { .name = "nConvgCfg" , .offset = 110 , .bytes = 2 , },
198
+ { .name = "nNVCfg0" , .offset = 112 , .bytes = 2 , },
199
+ { .name = "nNVCfg1" , .offset = 114 , .bytes = 2 , },
200
+ { .name = "nNVCfg2" , .offset = 116 , .bytes = 2 , },
201
+ { .name = "nSBSCfg" , .offset = 118 , .bytes = 2 , },
202
+ { .name = "nROMID0" , .offset = 120 , .bytes = 2 , },
203
+ { .name = "nROMID1" , .offset = 122 , .bytes = 2 , },
204
+ { .name = "nROMID2" , .offset = 124 , .bytes = 2 , },
205
+ { .name = "nROMID3" , .offset = 126 , .bytes = 2 , },
206
+
207
+ { .name = "nVAlrtTh" , .offset = 128 , .bytes = 2 , },
208
+ { .name = "nTAlrtTh" , .offset = 130 , .bytes = 2 , },
209
+ { .name = "nSAlrtTh" , .offset = 132 , .bytes = 2 , },
210
+ { .name = "nIAlrtTh" , .offset = 134 , .bytes = 2 , },
211
+ { .name = "nUser1C4" , .offset = 136 , .bytes = 2 , },
212
+ { .name = "nUser1C5" , .offset = 138 , .bytes = 2 , },
213
+ { .name = "nFullSOCThr" , .offset = 140 , .bytes = 2 , },
214
+ { .name = "nTTFCfg" , .offset = 142 , .bytes = 2 , },
215
+ { .name = "nCGain" , .offset = 144 , .bytes = 2 , },
216
+ { .name = "nTCurve" , .offset = 146 , .bytes = 2 , },
217
+ { .name = "nTGain" , .offset = 148 , .bytes = 2 , },
218
+ { .name = "nTOff" , .offset = 150 , .bytes = 2 , },
219
+ { .name = "nManfctrName0" , .offset = 152 , .bytes = 2 , },
220
+ { .name = "nManfctrName1" , .offset = 154 , .bytes = 2 , },
221
+ { .name = "nManfctrName2" , .offset = 156 , .bytes = 2 , },
222
+ { .name = "nRSense" , .offset = 158 , .bytes = 2 , },
223
+
224
+ { .name = "nUser1D0" , .offset = 160 , .bytes = 2 , },
225
+ { .name = "nUser1D1" , .offset = 162 , .bytes = 2 , },
226
+ { .name = "nAgeFcCfg" , .offset = 164 , .bytes = 2 , },
227
+ { .name = "nDesignVoltage" , .offset = 166 , .bytes = 2 , },
228
+ { .name = "nUser1D4" , .offset = 168 , .bytes = 2 , },
229
+ { .name = "nRFastVShdn" , .offset = 170 , .bytes = 2 , },
230
+ { .name = "nManfctrDate" , .offset = 172 , .bytes = 2 , },
231
+ { .name = "nFirstUsed" , .offset = 174 , .bytes = 2 , },
232
+ { .name = "nSerialNumber0" , .offset = 176 , .bytes = 2 , },
233
+ { .name = "nSerialNumber1" , .offset = 178 , .bytes = 2 , },
234
+ { .name = "nSerialNumber2" , .offset = 180 , .bytes = 2 , },
235
+ { .name = "nDeviceName0" , .offset = 182 , .bytes = 2 , },
236
+ { .name = "nDeviceName1" , .offset = 184 , .bytes = 2 , },
237
+ { .name = "nDeviceName2" , .offset = 186 , .bytes = 2 , },
238
+ { .name = "nDeviceName3" , .offset = 188 , .bytes = 2 , },
239
+ { .name = "nDeviceName4" , .offset = 190 , .bytes = 2 , },
240
+ };
241
+
109
242
static const enum power_supply_property max1720x_battery_props [] = {
110
243
POWER_SUPPLY_PROP_PRESENT ,
111
244
POWER_SUPPLY_PROP_CAPACITY ,
@@ -249,30 +382,81 @@ static int max1720x_battery_get_property(struct power_supply *psy,
249
382
return ret ;
250
383
}
251
384
252
- static int max1720x_probe_sense_resistor (struct i2c_client * client ,
253
- struct max1720x_device_info * info )
385
+ static
386
+ int max1720x_nvmem_reg_read (void * priv , unsigned int off , void * val , size_t len )
387
+ {
388
+ struct max1720x_device_info * info = priv ;
389
+ unsigned int reg = MAX1720X_NXTABLE0 + (off / 2 );
390
+
391
+ return regmap_bulk_read (info -> regmap_nv , reg , val , len / 2 );
392
+ }
393
+
394
+ static void max1720x_unregister_ancillary (void * data )
395
+ {
396
+ struct max1720x_device_info * info = data ;
397
+
398
+ i2c_unregister_device (info -> ancillary );
399
+ }
400
+
401
+ static int max1720x_probe_nvmem (struct i2c_client * client ,
402
+ struct max1720x_device_info * info )
254
403
{
255
404
struct device * dev = & client -> dev ;
256
- struct i2c_client * ancillary ;
405
+ struct nvmem_config nvmem_config = {
406
+ .dev = dev ,
407
+ .name = "max1720x_nvmem" ,
408
+ .cells = max1720x_nvmem_cells ,
409
+ .ncells = ARRAY_SIZE (max1720x_nvmem_cells ),
410
+ .read_only = true,
411
+ .root_only = true,
412
+ .reg_read = max1720x_nvmem_reg_read ,
413
+ .size = ARRAY_SIZE (max1720x_nvmem_cells ) * 2 ,
414
+ .word_size = 2 ,
415
+ .stride = 2 ,
416
+ .priv = info ,
417
+ };
418
+ struct nvmem_device * nvmem ;
419
+ unsigned int val ;
257
420
int ret ;
258
421
259
- ancillary = i2c_new_ancillary_device (client , "nvmem" , 0xb );
260
- if (IS_ERR (ancillary )) {
422
+ info -> ancillary = i2c_new_ancillary_device (client , "nvmem" , 0xb );
423
+ if (IS_ERR (info -> ancillary )) {
261
424
dev_err (dev , "Failed to initialize ancillary i2c device\n" );
262
- return PTR_ERR (ancillary );
425
+ return PTR_ERR (info -> ancillary );
263
426
}
264
427
265
- ret = i2c_smbus_read_word_data (ancillary , MAX1720X_NRSENSE );
266
- i2c_unregister_device (ancillary );
267
- if (ret < 0 )
428
+ ret = devm_add_action_or_reset (dev , max1720x_unregister_ancillary , info );
429
+ if (ret ) {
430
+ i2c_unregister_device (info -> ancillary );
431
+ dev_err (dev , "Failed to add unregister callback\n" );
268
432
return ret ;
433
+ }
434
+
435
+ info -> regmap_nv = devm_regmap_init_i2c (info -> ancillary ,
436
+ & max1720x_nvmem_regmap_cfg );
437
+ if (IS_ERR (info -> regmap_nv )) {
438
+ dev_err (dev , "regmap initialization of nvmem failed\n" );
439
+ return PTR_ERR (info -> regmap_nv );
440
+ }
441
+
442
+ ret = regmap_read (info -> regmap_nv , MAX1720X_NRSENSE , & val );
443
+ if (ret < 0 ) {
444
+ dev_err (dev , "Failed to read sense resistor value\n" );
445
+ return ret ;
446
+ }
269
447
270
- info -> rsense = ret ;
448
+ info -> rsense = val ;
271
449
if (!info -> rsense ) {
272
450
dev_warn (dev , "RSense not calibrated, set 10 mOhms!\n" );
273
451
info -> rsense = 1000 ; /* in regs in 10^-5 */
274
452
}
275
453
454
+ nvmem = devm_nvmem_register (dev , & nvmem_config );
455
+ if (IS_ERR (nvmem )) {
456
+ dev_err (dev , "Could not register nvmem!" );
457
+ return PTR_ERR (nvmem );
458
+ }
459
+
276
460
return 0 ;
277
461
}
278
462
@@ -299,15 +483,15 @@ static int max1720x_probe(struct i2c_client *client)
299
483
300
484
psy_cfg .drv_data = info ;
301
485
psy_cfg .fwnode = dev_fwnode (dev );
486
+ i2c_set_clientdata (client , info );
302
487
info -> regmap = devm_regmap_init_i2c (client , & max1720x_regmap_cfg );
303
488
if (IS_ERR (info -> regmap ))
304
489
return dev_err_probe (dev , PTR_ERR (info -> regmap ),
305
490
"regmap initialization failed\n" );
306
491
307
- ret = max1720x_probe_sense_resistor (client , info );
492
+ ret = max1720x_probe_nvmem (client , info );
308
493
if (ret )
309
- return dev_err_probe (dev , ret ,
310
- "Failed to read sense resistor value\n" );
494
+ return dev_err_probe (dev , ret , "Failed to probe nvmem\n" );
311
495
312
496
bat = devm_power_supply_register (dev , & max1720x_bat_desc , & psy_cfg );
313
497
if (IS_ERR (bat ))
0 commit comments