510 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * max77976_charger.c - Driver for the Maxim MAX77976 battery charger4 *5 * Copyright (C) 2021 Luca Ceresoli6 * 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 0x7616 17static const char *max77976_manufacturer = "Maxim Integrated";18static const char *max77976_model = "MAX77976";19 20/* --------------------------------------------------------------------------21 * Register map22 */23 24#define MAX77976_REG_CHIP_ID 0x0025#define MAX77976_REG_CHIP_REVISION 0x0126#define MAX77976_REG_CHG_INT_OK 0x1227#define MAX77976_REG_CHG_DETAILS_01 0x1428#define MAX77976_REG_CHG_CNFG_00 0x1629#define MAX77976_REG_CHG_CNFG_02 0x1830#define MAX77976_REG_CHG_CNFG_06 0x1c31#define MAX77976_REG_CHG_CNFG_09 0x1f32 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 present63};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 50000U73#define MAX77976_CHG_CC_MIN 100000U74#define MAX77976_CHG_CC_MAX 5500000U75 76/* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */77#define MAX77976_CHGIN_ILIM_STEP 100000U78#define MAX77976_CHGIN_ILIM_MIN 100000U79#define MAX77976_CHGIN_ILIM_MAX 3200000U80 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_FIELDS90};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 structures112 */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 properties122 */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 point390 */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 },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 = max77976_probe,503 .id_table = max77976_i2c_id,504};505module_i2c_driver(max77976_driver);506 507MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");508MODULE_DESCRIPTION("Maxim MAX77976 charger driver");509MODULE_LICENSE("GPL v2");510