1010#include <linux/bitfield.h>
1111#include <linux/i2c.h>
1212#include <linux/module.h>
13+ #include <linux/nvmem-provider.h>
1314#include <linux/power_supply.h>
1415#include <linux/regmap.h>
1516
1617#include <asm/unaligned.h>
1718
1819/* Nonvolatile registers */
20+ #define MAX1720X_NXTABLE0 0x80
1921#define MAX1720X_NRSENSE 0xCF /* RSense in 10^-5 Ohm */
22+ #define MAX1720X_NDEVICE_NAME4 0xDF
2023
2124/* ModelGauge m5 */
2225#define MAX172XX_STATUS 0x00 /* Status */
@@ -46,6 +49,8 @@ static const char *const max17205_model = "MAX17205";
4649
4750struct max1720x_device_info {
4851 struct regmap * regmap ;
52+ struct regmap * regmap_nv ;
53+ struct i2c_client * ancillary ;
4954 int rsense ;
5055};
5156
@@ -106,6 +111,134 @@ static const struct regmap_config max1720x_regmap_cfg = {
106111 .cache_type = REGCACHE_RBTREE ,
107112};
108113
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+
109242static const enum power_supply_property max1720x_battery_props [] = {
110243 POWER_SUPPLY_PROP_PRESENT ,
111244 POWER_SUPPLY_PROP_CAPACITY ,
@@ -249,30 +382,81 @@ static int max1720x_battery_get_property(struct power_supply *psy,
249382 return ret ;
250383}
251384
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 )
254403{
255404 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 ;
257420 int ret ;
258421
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 )) {
261424 dev_err (dev , "Failed to initialize ancillary i2c device\n" );
262- return PTR_ERR (ancillary );
425+ return PTR_ERR (info -> ancillary );
263426 }
264427
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" );
268432 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+ }
269447
270- info -> rsense = ret ;
448+ info -> rsense = val ;
271449 if (!info -> rsense ) {
272450 dev_warn (dev , "RSense not calibrated, set 10 mOhms!\n" );
273451 info -> rsense = 1000 ; /* in regs in 10^-5 */
274452 }
275453
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+
276460 return 0 ;
277461}
278462
@@ -299,15 +483,15 @@ static int max1720x_probe(struct i2c_client *client)
299483
300484 psy_cfg .drv_data = info ;
301485 psy_cfg .fwnode = dev_fwnode (dev );
486+ i2c_set_clientdata (client , info );
302487 info -> regmap = devm_regmap_init_i2c (client , & max1720x_regmap_cfg );
303488 if (IS_ERR (info -> regmap ))
304489 return dev_err_probe (dev , PTR_ERR (info -> regmap ),
305490 "regmap initialization failed\n" );
306491
307- ret = max1720x_probe_sense_resistor (client , info );
492+ ret = max1720x_probe_nvmem (client , info );
308493 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" );
311495
312496 bat = devm_power_supply_register (dev , & max1720x_bat_desc , & psy_cfg );
313497 if (IS_ERR (bat ))
0 commit comments