Skip to content

Commit 47271a9

Browse files
Dimitri Fedrausre
authored andcommitted
power: supply: max1720x: add read support for nvmem
ModelGauge m5 and device configuration values are stored in nonvolatile memory to prevent data loss if the IC loses power. Add read support for the nonvolatile memory on MAX1720X devices. Signed-off-by: Dimitri Fedrau <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sebastian Reichel <[email protected]>
1 parent 9dad012 commit 47271a9

File tree

1 file changed

+197
-13
lines changed

1 file changed

+197
-13
lines changed

drivers/power/supply/max1720x_battery.c

Lines changed: 197 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
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

4750
struct 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+
109242
static 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

Comments
 (0)