max77976_charger.c (13755B)
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@lucaceresoli.net> 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 17static const char *max77976_manufacturer = "Maxim Integrated"; 18static 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 */ 34enum 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 */ 54enum 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 */ 66enum 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 81enum 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 92static 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 104static 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 114struct 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 124static 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 162static 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 200static 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 234static 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 248static 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 264static 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 275static 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 325static 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 354static 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 366static 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 378static 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 392static 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 416static 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 436err: 437 return dev_err_probe(dev, err, "error while configuring"); 438} 439 440static 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 485static const struct i2c_device_id max77976_i2c_id[] = { 486 { MAX77976_DRIVER_NAME, 0 }, 487 { }, 488}; 489MODULE_DEVICE_TABLE(i2c, max77976_i2c_id); 490 491static const struct of_device_id max77976_of_id[] = { 492 { .compatible = "maxim,max77976" }, 493 { }, 494}; 495MODULE_DEVICE_TABLE(of, max77976_of_id); 496 497static struct i2c_driver max77976_driver = { 498 .driver = { 499 .name = MAX77976_DRIVER_NAME, 500 .of_match_table = max77976_of_id, 501 }, 502 .probe_new = max77976_probe, 503 .id_table = max77976_i2c_id, 504}; 505module_i2c_driver(max77976_driver); 506 507MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); 508MODULE_DESCRIPTION("Maxim MAX77976 charger driver"); 509MODULE_LICENSE("GPL v2");