1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * max77976_charger.c - Driver for the Maxim MAX77976 battery charger
4 *
5 * Copyright (C) 2021 Luca Ceresoli
6 * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
7 */
8
9 #include <linux/i2c.h>
10 #include <linux/module.h>
11 #include <linux/power_supply.h>
12 #include <linux/regmap.h>
13
14 #define MAX77976_DRIVER_NAME "max77976-charger"
15 #define MAX77976_CHIP_ID 0x76
16
17 static const char *max77976_manufacturer = "Maxim Integrated";
18 static const char *max77976_model = "MAX77976";
19
20 /* --------------------------------------------------------------------------
21 * Register map
22 */
23
24 #define MAX77976_REG_CHIP_ID 0x00
25 #define MAX77976_REG_CHIP_REVISION 0x01
26 #define MAX77976_REG_CHG_INT_OK 0x12
27 #define MAX77976_REG_CHG_DETAILS_01 0x14
28 #define MAX77976_REG_CHG_CNFG_00 0x16
29 #define MAX77976_REG_CHG_CNFG_02 0x18
30 #define MAX77976_REG_CHG_CNFG_06 0x1c
31 #define MAX77976_REG_CHG_CNFG_09 0x1f
32
33 /* CHG_DETAILS_01.CHG_DTLS values */
34 enum max77976_charging_state {
35 MAX77976_CHARGING_PREQUALIFICATION = 0x0,
36 MAX77976_CHARGING_FAST_CONST_CURRENT,
37 MAX77976_CHARGING_FAST_CONST_VOLTAGE,
38 MAX77976_CHARGING_TOP_OFF,
39 MAX77976_CHARGING_DONE,
40 MAX77976_CHARGING_RESERVED_05,
41 MAX77976_CHARGING_TIMER_FAULT,
42 MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
43 MAX77976_CHARGING_OFF,
44 MAX77976_CHARGING_RESERVED_09,
45 MAX77976_CHARGING_THERMAL_SHUTDOWN,
46 MAX77976_CHARGING_WATCHDOG_EXPIRED,
47 MAX77976_CHARGING_SUSPENDED_JEITA,
48 MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
49 MAX77976_CHARGING_SUSPENDED_PIN,
50 MAX77976_CHARGING_RESERVED_0F,
51 };
52
53 /* CHG_DETAILS_01.BAT_DTLS values */
54 enum max77976_battery_state {
55 MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
56 MAX77976_BATTERY_PREQUALIFICATION,
57 MAX77976_BATTERY_TIMER_FAULT,
58 MAX77976_BATTERY_REGULAR_VOLTAGE,
59 MAX77976_BATTERY_LOW_VOLTAGE,
60 MAX77976_BATTERY_OVERVOLTAGE,
61 MAX77976_BATTERY_RESERVED,
62 MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
63 };
64
65 /* CHG_CNFG_00.MODE values */
66 enum max77976_mode {
67 MAX77976_MODE_CHARGER_BUCK = 0x5,
68 MAX77976_MODE_BOOST = 0x9,
69 };
70
71 /* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
72 #define MAX77976_CHG_CC_STEP 50000U
73 #define MAX77976_CHG_CC_MIN 100000U
74 #define MAX77976_CHG_CC_MAX 5500000U
75
76 /* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
77 #define MAX77976_CHGIN_ILIM_STEP 100000U
78 #define MAX77976_CHGIN_ILIM_MIN 100000U
79 #define MAX77976_CHGIN_ILIM_MAX 3200000U
80
81 enum max77976_field_idx {
82 VERSION, REVISION, /* CHIP_REVISION */
83 CHGIN_OK, /* CHG_INT_OK */
84 BAT_DTLS, CHG_DTLS, /* CHG_DETAILS_01 */
85 MODE, /* CHG_CNFG_00 */
86 CHG_CC, /* CHG_CNFG_02 */
87 CHGPROT, /* CHG_CNFG_06 */
88 CHGIN_ILIM, /* CHG_CNFG_09 */
89 MAX77976_N_REGMAP_FIELDS
90 };
91
92 static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
93 [VERSION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 4, 7),
94 [REVISION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 0, 3),
95 [CHGIN_OK] = REG_FIELD(MAX77976_REG_CHG_INT_OK, 6, 6),
96 [CHG_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 0, 3),
97 [BAT_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 4, 6),
98 [MODE] = REG_FIELD(MAX77976_REG_CHG_CNFG_00, 0, 3),
99 [CHG_CC] = REG_FIELD(MAX77976_REG_CHG_CNFG_02, 0, 6),
100 [CHGPROT] = REG_FIELD(MAX77976_REG_CHG_CNFG_06, 2, 3),
101 [CHGIN_ILIM] = REG_FIELD(MAX77976_REG_CHG_CNFG_09, 0, 5),
102 };
103
104 static const struct regmap_config max77976_regmap_config = {
105 .reg_bits = 8,
106 .val_bits = 8,
107 .max_register = 0x24,
108 };
109
110 /* --------------------------------------------------------------------------
111 * Data structures
112 */
113
114 struct max77976 {
115 struct i2c_client *client;
116 struct regmap *regmap;
117 struct regmap_field *rfield[MAX77976_N_REGMAP_FIELDS];
118 };
119
120 /* --------------------------------------------------------------------------
121 * power_supply properties
122 */
123
max77976_get_status(struct max77976 * chg,int * val)124 static int max77976_get_status(struct max77976 *chg, int *val)
125 {
126 unsigned int regval;
127 int err;
128
129 err = regmap_field_read(chg->rfield[CHG_DTLS], ®val);
130 if (err < 0)
131 return err;
132
133 switch (regval) {
134 case MAX77976_CHARGING_PREQUALIFICATION:
135 case MAX77976_CHARGING_FAST_CONST_CURRENT:
136 case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
137 case MAX77976_CHARGING_TOP_OFF:
138 *val = POWER_SUPPLY_STATUS_CHARGING;
139 break;
140 case MAX77976_CHARGING_DONE:
141 *val = POWER_SUPPLY_STATUS_FULL;
142 break;
143 case MAX77976_CHARGING_TIMER_FAULT:
144 case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
145 case MAX77976_CHARGING_SUSPENDED_JEITA:
146 case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
147 case MAX77976_CHARGING_SUSPENDED_PIN:
148 *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
149 break;
150 case MAX77976_CHARGING_OFF:
151 case MAX77976_CHARGING_THERMAL_SHUTDOWN:
152 case MAX77976_CHARGING_WATCHDOG_EXPIRED:
153 *val = POWER_SUPPLY_STATUS_DISCHARGING;
154 break;
155 default:
156 *val = POWER_SUPPLY_STATUS_UNKNOWN;
157 }
158
159 return 0;
160 }
161
max77976_get_charge_type(struct max77976 * chg,int * val)162 static int max77976_get_charge_type(struct max77976 *chg, int *val)
163 {
164 unsigned int regval;
165 int err;
166
167 err = regmap_field_read(chg->rfield[CHG_DTLS], ®val);
168 if (err < 0)
169 return err;
170
171 switch (regval) {
172 case MAX77976_CHARGING_PREQUALIFICATION:
173 *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
174 break;
175 case MAX77976_CHARGING_FAST_CONST_CURRENT:
176 case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
177 *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
178 break;
179 case MAX77976_CHARGING_TOP_OFF:
180 *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
181 break;
182 case MAX77976_CHARGING_DONE:
183 case MAX77976_CHARGING_TIMER_FAULT:
184 case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
185 case MAX77976_CHARGING_OFF:
186 case MAX77976_CHARGING_THERMAL_SHUTDOWN:
187 case MAX77976_CHARGING_WATCHDOG_EXPIRED:
188 case MAX77976_CHARGING_SUSPENDED_JEITA:
189 case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
190 case MAX77976_CHARGING_SUSPENDED_PIN:
191 *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
192 break;
193 default:
194 *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
195 }
196
197 return 0;
198 }
199
max77976_get_health(struct max77976 * chg,int * val)200 static int max77976_get_health(struct max77976 *chg, int *val)
201 {
202 unsigned int regval;
203 int err;
204
205 err = regmap_field_read(chg->rfield[BAT_DTLS], ®val);
206 if (err < 0)
207 return err;
208
209 switch (regval) {
210 case MAX77976_BATTERY_BATTERY_REMOVAL:
211 *val = POWER_SUPPLY_HEALTH_NO_BATTERY;
212 break;
213 case MAX77976_BATTERY_LOW_VOLTAGE:
214 case MAX77976_BATTERY_REGULAR_VOLTAGE:
215 *val = POWER_SUPPLY_HEALTH_GOOD;
216 break;
217 case MAX77976_BATTERY_TIMER_FAULT:
218 *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
219 break;
220 case MAX77976_BATTERY_OVERVOLTAGE:
221 *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
222 break;
223 case MAX77976_BATTERY_PREQUALIFICATION:
224 case MAX77976_BATTERY_BATTERY_ONLY:
225 *val = POWER_SUPPLY_HEALTH_UNKNOWN;
226 break;
227 default:
228 *val = POWER_SUPPLY_HEALTH_UNKNOWN;
229 }
230
231 return 0;
232 }
233
max77976_get_online(struct max77976 * chg,int * val)234 static int max77976_get_online(struct max77976 *chg, int *val)
235 {
236 unsigned int regval;
237 int err;
238
239 err = regmap_field_read(chg->rfield[CHGIN_OK], ®val);
240 if (err < 0)
241 return err;
242
243 *val = (regval ? 1 : 0);
244
245 return 0;
246 }
247
max77976_get_integer(struct max77976 * chg,enum max77976_field_idx fidx,unsigned int clamp_min,unsigned int clamp_max,unsigned int mult,int * val)248 static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
249 unsigned int clamp_min, unsigned int clamp_max,
250 unsigned int mult, int *val)
251 {
252 unsigned int regval;
253 int err;
254
255 err = regmap_field_read(chg->rfield[fidx], ®val);
256 if (err < 0)
257 return err;
258
259 *val = clamp_val(regval * mult, clamp_min, clamp_max);
260
261 return 0;
262 }
263
max77976_set_integer(struct max77976 * chg,enum max77976_field_idx fidx,unsigned int clamp_min,unsigned int clamp_max,unsigned int div,int val)264 static int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx,
265 unsigned int clamp_min, unsigned int clamp_max,
266 unsigned int div, int val)
267 {
268 unsigned int regval;
269
270 regval = clamp_val(val, clamp_min, clamp_max) / div;
271
272 return regmap_field_write(chg->rfield[fidx], regval);
273 }
274
max77976_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)275 static int max77976_get_property(struct power_supply *psy,
276 enum power_supply_property psp,
277 union power_supply_propval *val)
278 {
279 struct max77976 *chg = power_supply_get_drvdata(psy);
280 int err = 0;
281
282 switch (psp) {
283 case POWER_SUPPLY_PROP_STATUS:
284 err = max77976_get_status(chg, &val->intval);
285 break;
286 case POWER_SUPPLY_PROP_CHARGE_TYPE:
287 err = max77976_get_charge_type(chg, &val->intval);
288 break;
289 case POWER_SUPPLY_PROP_HEALTH:
290 err = max77976_get_health(chg, &val->intval);
291 break;
292 case POWER_SUPPLY_PROP_ONLINE:
293 err = max77976_get_online(chg, &val->intval);
294 break;
295 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
296 val->intval = MAX77976_CHG_CC_MAX;
297 break;
298 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
299 err = max77976_get_integer(chg, CHG_CC,
300 MAX77976_CHG_CC_MIN,
301 MAX77976_CHG_CC_MAX,
302 MAX77976_CHG_CC_STEP,
303 &val->intval);
304 break;
305 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
306 err = max77976_get_integer(chg, CHGIN_ILIM,
307 MAX77976_CHGIN_ILIM_MIN,
308 MAX77976_CHGIN_ILIM_MAX,
309 MAX77976_CHGIN_ILIM_STEP,
310 &val->intval);
311 break;
312 case POWER_SUPPLY_PROP_MODEL_NAME:
313 val->strval = max77976_model;
314 break;
315 case POWER_SUPPLY_PROP_MANUFACTURER:
316 val->strval = max77976_manufacturer;
317 break;
318 default:
319 err = -EINVAL;
320 }
321
322 return err;
323 }
324
max77976_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)325 static int max77976_set_property(struct power_supply *psy,
326 enum power_supply_property psp,
327 const union power_supply_propval *val)
328 {
329 struct max77976 *chg = power_supply_get_drvdata(psy);
330 int err = 0;
331
332 switch (psp) {
333 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
334 err = max77976_set_integer(chg, CHG_CC,
335 MAX77976_CHG_CC_MIN,
336 MAX77976_CHG_CC_MAX,
337 MAX77976_CHG_CC_STEP,
338 val->intval);
339 break;
340 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
341 err = max77976_set_integer(chg, CHGIN_ILIM,
342 MAX77976_CHGIN_ILIM_MIN,
343 MAX77976_CHGIN_ILIM_MAX,
344 MAX77976_CHGIN_ILIM_STEP,
345 val->intval);
346 break;
347 default:
348 err = -EINVAL;
349 }
350
351 return err;
352 };
353
max77976_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)354 static int max77976_property_is_writeable(struct power_supply *psy,
355 enum power_supply_property psp)
356 {
357 switch (psp) {
358 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
359 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
360 return true;
361 default:
362 return false;
363 }
364 }
365
366 static enum power_supply_property max77976_psy_props[] = {
367 POWER_SUPPLY_PROP_STATUS,
368 POWER_SUPPLY_PROP_CHARGE_TYPE,
369 POWER_SUPPLY_PROP_HEALTH,
370 POWER_SUPPLY_PROP_ONLINE,
371 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
372 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
373 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
374 POWER_SUPPLY_PROP_MODEL_NAME,
375 POWER_SUPPLY_PROP_MANUFACTURER,
376 };
377
378 static const struct power_supply_desc max77976_psy_desc = {
379 .name = MAX77976_DRIVER_NAME,
380 .type = POWER_SUPPLY_TYPE_USB,
381 .properties = max77976_psy_props,
382 .num_properties = ARRAY_SIZE(max77976_psy_props),
383 .get_property = max77976_get_property,
384 .set_property = max77976_set_property,
385 .property_is_writeable = max77976_property_is_writeable,
386 };
387
388 /* --------------------------------------------------------------------------
389 * Entry point
390 */
391
max77976_detect(struct max77976 * chg)392 static int max77976_detect(struct max77976 *chg)
393 {
394 struct device *dev = &chg->client->dev;
395 unsigned int id, ver, rev;
396 int err;
397
398 err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
399 if (err)
400 return dev_err_probe(dev, err, "cannot read chip ID\n");
401
402 if (id != MAX77976_CHIP_ID)
403 return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
404
405 err = regmap_field_read(chg->rfield[VERSION], &ver);
406 if (!err)
407 err = regmap_field_read(chg->rfield[REVISION], &rev);
408 if (err)
409 return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
410
411 dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
412
413 return 0;
414 }
415
max77976_configure(struct max77976 * chg)416 static int max77976_configure(struct max77976 *chg)
417 {
418 struct device *dev = &chg->client->dev;
419 int err;
420
421 /* Magic value to unlock writing to some registers */
422 err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
423 if (err)
424 goto err;
425
426 /*
427 * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
428 * Other modes are not implemented by this driver.
429 */
430 err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
431 if (err)
432 goto err;
433
434 return 0;
435
436 err:
437 return dev_err_probe(dev, err, "error while configuring");
438 }
439
max77976_probe(struct i2c_client * client)440 static int max77976_probe(struct i2c_client *client)
441 {
442 struct device *dev = &client->dev;
443 struct power_supply_config psy_cfg = {};
444 struct power_supply *psy;
445 struct max77976 *chg;
446 int err;
447 int i;
448
449 chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
450 if (!chg)
451 return -ENOMEM;
452
453 i2c_set_clientdata(client, chg);
454 psy_cfg.drv_data = chg;
455 chg->client = client;
456
457 chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
458 if (IS_ERR(chg->regmap))
459 return dev_err_probe(dev, PTR_ERR(chg->regmap),
460 "cannot allocate regmap\n");
461
462 for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
463 chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
464 max77976_reg_field[i]);
465 if (IS_ERR(chg->rfield[i]))
466 return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
467 "cannot allocate regmap field\n");
468 }
469
470 err = max77976_detect(chg);
471 if (err)
472 return err;
473
474 err = max77976_configure(chg);
475 if (err)
476 return err;
477
478 psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
479 if (IS_ERR(psy))
480 return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
481
482 return 0;
483 }
484
485 static const struct i2c_device_id max77976_i2c_id[] = {
486 { MAX77976_DRIVER_NAME, 0 },
487 { },
488 };
489 MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
490
491 static const struct of_device_id max77976_of_id[] = {
492 { .compatible = "maxim,max77976" },
493 { },
494 };
495 MODULE_DEVICE_TABLE(of, max77976_of_id);
496
497 static struct i2c_driver max77976_driver = {
498 .driver = {
499 .name = MAX77976_DRIVER_NAME,
500 .of_match_table = max77976_of_id,
501 },
502 .probe = max77976_probe,
503 .id_table = max77976_i2c_id,
504 };
505 module_i2c_driver(max77976_driver);
506
507 MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
508 MODULE_DESCRIPTION("Maxim MAX77976 charger driver");
509 MODULE_LICENSE("GPL v2");
510