Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * max77976_charger.c - Driver for the Maxim MAX77976 battery charger
0004  *
0005  * Copyright (C) 2021 Luca Ceresoli
0006  * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
0007  */
0008 
0009 #include <linux/i2c.h>
0010 #include <linux/module.h>
0011 #include <linux/power_supply.h>
0012 #include <linux/regmap.h>
0013 
0014 #define MAX77976_DRIVER_NAME    "max77976-charger"
0015 #define MAX77976_CHIP_ID    0x76
0016 
0017 static const char *max77976_manufacturer    = "Maxim Integrated";
0018 static const char *max77976_model       = "MAX77976";
0019 
0020 /* --------------------------------------------------------------------------
0021  * Register map
0022  */
0023 
0024 #define MAX77976_REG_CHIP_ID        0x00
0025 #define MAX77976_REG_CHIP_REVISION  0x01
0026 #define MAX77976_REG_CHG_INT_OK     0x12
0027 #define MAX77976_REG_CHG_DETAILS_01 0x14
0028 #define MAX77976_REG_CHG_CNFG_00    0x16
0029 #define MAX77976_REG_CHG_CNFG_02    0x18
0030 #define MAX77976_REG_CHG_CNFG_06    0x1c
0031 #define MAX77976_REG_CHG_CNFG_09    0x1f
0032 
0033 /* CHG_DETAILS_01.CHG_DTLS values */
0034 enum max77976_charging_state {
0035     MAX77976_CHARGING_PREQUALIFICATION = 0x0,
0036     MAX77976_CHARGING_FAST_CONST_CURRENT,
0037     MAX77976_CHARGING_FAST_CONST_VOLTAGE,
0038     MAX77976_CHARGING_TOP_OFF,
0039     MAX77976_CHARGING_DONE,
0040     MAX77976_CHARGING_RESERVED_05,
0041     MAX77976_CHARGING_TIMER_FAULT,
0042     MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
0043     MAX77976_CHARGING_OFF,
0044     MAX77976_CHARGING_RESERVED_09,
0045     MAX77976_CHARGING_THERMAL_SHUTDOWN,
0046     MAX77976_CHARGING_WATCHDOG_EXPIRED,
0047     MAX77976_CHARGING_SUSPENDED_JEITA,
0048     MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
0049     MAX77976_CHARGING_SUSPENDED_PIN,
0050     MAX77976_CHARGING_RESERVED_0F,
0051 };
0052 
0053 /* CHG_DETAILS_01.BAT_DTLS values */
0054 enum max77976_battery_state {
0055     MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
0056     MAX77976_BATTERY_PREQUALIFICATION,
0057     MAX77976_BATTERY_TIMER_FAULT,
0058     MAX77976_BATTERY_REGULAR_VOLTAGE,
0059     MAX77976_BATTERY_LOW_VOLTAGE,
0060     MAX77976_BATTERY_OVERVOLTAGE,
0061     MAX77976_BATTERY_RESERVED,
0062     MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
0063 };
0064 
0065 /* CHG_CNFG_00.MODE values */
0066 enum max77976_mode {
0067     MAX77976_MODE_CHARGER_BUCK      = 0x5,
0068     MAX77976_MODE_BOOST         = 0x9,
0069 };
0070 
0071 /* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
0072 #define MAX77976_CHG_CC_STEP              50000U
0073 #define MAX77976_CHG_CC_MIN          100000U
0074 #define MAX77976_CHG_CC_MAX         5500000U
0075 
0076 /* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
0077 #define MAX77976_CHGIN_ILIM_STEP         100000U
0078 #define MAX77976_CHGIN_ILIM_MIN          100000U
0079 #define MAX77976_CHGIN_ILIM_MAX         3200000U
0080 
0081 enum max77976_field_idx {
0082     VERSION, REVISION,                      /* CHIP_REVISION */
0083     CHGIN_OK,                               /* CHG_INT_OK */
0084     BAT_DTLS, CHG_DTLS,                     /* CHG_DETAILS_01 */
0085     MODE,                                   /* CHG_CNFG_00 */
0086     CHG_CC,                                 /* CHG_CNFG_02 */
0087     CHGPROT,                                /* CHG_CNFG_06 */
0088     CHGIN_ILIM,                             /* CHG_CNFG_09 */
0089     MAX77976_N_REGMAP_FIELDS
0090 };
0091 
0092 static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
0093     [VERSION]        = REG_FIELD(MAX77976_REG_CHIP_REVISION,   4, 7),
0094     [REVISION]       = REG_FIELD(MAX77976_REG_CHIP_REVISION,   0, 3),
0095     [CHGIN_OK]       = REG_FIELD(MAX77976_REG_CHG_INT_OK,      6, 6),
0096     [CHG_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  0, 3),
0097     [BAT_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  4, 6),
0098     [MODE]           = REG_FIELD(MAX77976_REG_CHG_CNFG_00,     0, 3),
0099     [CHG_CC]         = REG_FIELD(MAX77976_REG_CHG_CNFG_02,     0, 6),
0100     [CHGPROT]        = REG_FIELD(MAX77976_REG_CHG_CNFG_06,     2, 3),
0101     [CHGIN_ILIM]     = REG_FIELD(MAX77976_REG_CHG_CNFG_09,     0, 5),
0102 };
0103 
0104 static const struct regmap_config max77976_regmap_config = {
0105     .reg_bits = 8,
0106     .val_bits = 8,
0107     .max_register = 0x24,
0108 };
0109 
0110 /* --------------------------------------------------------------------------
0111  * Data structures
0112  */
0113 
0114 struct max77976 {
0115     struct i2c_client   *client;
0116     struct regmap       *regmap;
0117     struct regmap_field *rfield[MAX77976_N_REGMAP_FIELDS];
0118 };
0119 
0120 /* --------------------------------------------------------------------------
0121  * power_supply properties
0122  */
0123 
0124 static int max77976_get_status(struct max77976 *chg, int *val)
0125 {
0126     unsigned int regval;
0127     int err;
0128 
0129     err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
0130     if (err < 0)
0131         return err;
0132 
0133     switch (regval) {
0134     case MAX77976_CHARGING_PREQUALIFICATION:
0135     case MAX77976_CHARGING_FAST_CONST_CURRENT:
0136     case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
0137     case MAX77976_CHARGING_TOP_OFF:
0138         *val = POWER_SUPPLY_STATUS_CHARGING;
0139         break;
0140     case MAX77976_CHARGING_DONE:
0141         *val = POWER_SUPPLY_STATUS_FULL;
0142         break;
0143     case MAX77976_CHARGING_TIMER_FAULT:
0144     case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
0145     case MAX77976_CHARGING_SUSPENDED_JEITA:
0146     case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
0147     case MAX77976_CHARGING_SUSPENDED_PIN:
0148         *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
0149         break;
0150     case MAX77976_CHARGING_OFF:
0151     case MAX77976_CHARGING_THERMAL_SHUTDOWN:
0152     case MAX77976_CHARGING_WATCHDOG_EXPIRED:
0153         *val = POWER_SUPPLY_STATUS_DISCHARGING;
0154         break;
0155     default:
0156         *val = POWER_SUPPLY_STATUS_UNKNOWN;
0157     }
0158 
0159     return 0;
0160 }
0161 
0162 static int max77976_get_charge_type(struct max77976 *chg, int *val)
0163 {
0164     unsigned int regval;
0165     int err;
0166 
0167     err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
0168     if (err < 0)
0169         return err;
0170 
0171     switch (regval) {
0172     case MAX77976_CHARGING_PREQUALIFICATION:
0173         *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
0174         break;
0175     case MAX77976_CHARGING_FAST_CONST_CURRENT:
0176     case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
0177         *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
0178         break;
0179     case MAX77976_CHARGING_TOP_OFF:
0180         *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
0181         break;
0182     case MAX77976_CHARGING_DONE:
0183     case MAX77976_CHARGING_TIMER_FAULT:
0184     case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
0185     case MAX77976_CHARGING_OFF:
0186     case MAX77976_CHARGING_THERMAL_SHUTDOWN:
0187     case MAX77976_CHARGING_WATCHDOG_EXPIRED:
0188     case MAX77976_CHARGING_SUSPENDED_JEITA:
0189     case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
0190     case MAX77976_CHARGING_SUSPENDED_PIN:
0191         *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
0192         break;
0193     default:
0194         *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
0195     }
0196 
0197     return 0;
0198 }
0199 
0200 static int max77976_get_health(struct max77976 *chg, int *val)
0201 {
0202     unsigned int regval;
0203     int err;
0204 
0205     err = regmap_field_read(chg->rfield[BAT_DTLS], &regval);
0206     if (err < 0)
0207         return err;
0208 
0209     switch (regval) {
0210     case MAX77976_BATTERY_BATTERY_REMOVAL:
0211         *val = POWER_SUPPLY_HEALTH_NO_BATTERY;
0212         break;
0213     case MAX77976_BATTERY_LOW_VOLTAGE:
0214     case MAX77976_BATTERY_REGULAR_VOLTAGE:
0215         *val = POWER_SUPPLY_HEALTH_GOOD;
0216         break;
0217     case MAX77976_BATTERY_TIMER_FAULT:
0218         *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
0219         break;
0220     case MAX77976_BATTERY_OVERVOLTAGE:
0221         *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
0222         break;
0223     case MAX77976_BATTERY_PREQUALIFICATION:
0224     case MAX77976_BATTERY_BATTERY_ONLY:
0225         *val = POWER_SUPPLY_HEALTH_UNKNOWN;
0226         break;
0227     default:
0228         *val = POWER_SUPPLY_HEALTH_UNKNOWN;
0229     }
0230 
0231     return 0;
0232 }
0233 
0234 static int max77976_get_online(struct max77976 *chg, int *val)
0235 {
0236     unsigned int regval;
0237     int err;
0238 
0239     err = regmap_field_read(chg->rfield[CHGIN_OK], &regval);
0240     if (err < 0)
0241         return err;
0242 
0243     *val = (regval ? 1 : 0);
0244 
0245     return 0;
0246 }
0247 
0248 static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
0249                 unsigned int clamp_min, unsigned int clamp_max,
0250                 unsigned int mult, int *val)
0251 {
0252     unsigned int regval;
0253     int err;
0254 
0255     err = regmap_field_read(chg->rfield[fidx], &regval);
0256     if (err < 0)
0257         return err;
0258 
0259     *val = clamp_val(regval * mult, clamp_min, clamp_max);
0260 
0261     return 0;
0262 }
0263 
0264 static int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx,
0265                 unsigned int clamp_min, unsigned int clamp_max,
0266                 unsigned int div, int val)
0267 {
0268     unsigned int regval;
0269 
0270     regval = clamp_val(val, clamp_min, clamp_max) / div;
0271 
0272     return regmap_field_write(chg->rfield[fidx], regval);
0273 }
0274 
0275 static int max77976_get_property(struct power_supply *psy,
0276                  enum power_supply_property psp,
0277                  union power_supply_propval *val)
0278 {
0279     struct max77976 *chg = power_supply_get_drvdata(psy);
0280     int err = 0;
0281 
0282     switch (psp) {
0283     case POWER_SUPPLY_PROP_STATUS:
0284         err = max77976_get_status(chg, &val->intval);
0285         break;
0286     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0287         err = max77976_get_charge_type(chg, &val->intval);
0288         break;
0289     case POWER_SUPPLY_PROP_HEALTH:
0290         err = max77976_get_health(chg, &val->intval);
0291         break;
0292     case POWER_SUPPLY_PROP_ONLINE:
0293         err = max77976_get_online(chg, &val->intval);
0294         break;
0295     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
0296         val->intval = MAX77976_CHG_CC_MAX;
0297         break;
0298     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0299         err = max77976_get_integer(chg, CHG_CC,
0300                        MAX77976_CHG_CC_MIN,
0301                        MAX77976_CHG_CC_MAX,
0302                        MAX77976_CHG_CC_STEP,
0303                        &val->intval);
0304         break;
0305     case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0306         err = max77976_get_integer(chg, CHGIN_ILIM,
0307                        MAX77976_CHGIN_ILIM_MIN,
0308                        MAX77976_CHGIN_ILIM_MAX,
0309                        MAX77976_CHGIN_ILIM_STEP,
0310                        &val->intval);
0311         break;
0312     case POWER_SUPPLY_PROP_MODEL_NAME:
0313         val->strval = max77976_model;
0314         break;
0315     case POWER_SUPPLY_PROP_MANUFACTURER:
0316         val->strval = max77976_manufacturer;
0317         break;
0318     default:
0319         err = -EINVAL;
0320     }
0321 
0322     return err;
0323 }
0324 
0325 static int max77976_set_property(struct power_supply *psy,
0326                  enum power_supply_property psp,
0327                  const union power_supply_propval *val)
0328 {
0329     struct max77976 *chg = power_supply_get_drvdata(psy);
0330     int err = 0;
0331 
0332     switch (psp) {
0333     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0334         err = max77976_set_integer(chg, CHG_CC,
0335                        MAX77976_CHG_CC_MIN,
0336                        MAX77976_CHG_CC_MAX,
0337                        MAX77976_CHG_CC_STEP,
0338                        val->intval);
0339         break;
0340     case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0341         err = max77976_set_integer(chg, CHGIN_ILIM,
0342                        MAX77976_CHGIN_ILIM_MIN,
0343                        MAX77976_CHGIN_ILIM_MAX,
0344                        MAX77976_CHGIN_ILIM_STEP,
0345                        val->intval);
0346         break;
0347     default:
0348         err = -EINVAL;
0349     }
0350 
0351     return err;
0352 };
0353 
0354 static int max77976_property_is_writeable(struct power_supply *psy,
0355                       enum power_supply_property psp)
0356 {
0357     switch (psp) {
0358     case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
0359     case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0360         return true;
0361     default:
0362         return false;
0363     }
0364 }
0365 
0366 static enum power_supply_property max77976_psy_props[] = {
0367     POWER_SUPPLY_PROP_STATUS,
0368     POWER_SUPPLY_PROP_CHARGE_TYPE,
0369     POWER_SUPPLY_PROP_HEALTH,
0370     POWER_SUPPLY_PROP_ONLINE,
0371     POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
0372     POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
0373     POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
0374     POWER_SUPPLY_PROP_MODEL_NAME,
0375     POWER_SUPPLY_PROP_MANUFACTURER,
0376 };
0377 
0378 static const struct power_supply_desc max77976_psy_desc = {
0379     .name           = MAX77976_DRIVER_NAME,
0380     .type           = POWER_SUPPLY_TYPE_USB,
0381     .properties     = max77976_psy_props,
0382     .num_properties     = ARRAY_SIZE(max77976_psy_props),
0383     .get_property       = max77976_get_property,
0384     .set_property       = max77976_set_property,
0385     .property_is_writeable  = max77976_property_is_writeable,
0386 };
0387 
0388 /* --------------------------------------------------------------------------
0389  * Entry point
0390  */
0391 
0392 static int max77976_detect(struct max77976 *chg)
0393 {
0394     struct device *dev = &chg->client->dev;
0395     unsigned int id, ver, rev;
0396     int err;
0397 
0398     err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
0399     if (err)
0400         return dev_err_probe(dev, err, "cannot read chip ID\n");
0401 
0402     if (id != MAX77976_CHIP_ID)
0403         return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
0404 
0405     err = regmap_field_read(chg->rfield[VERSION], &ver);
0406     if (!err)
0407         err = regmap_field_read(chg->rfield[REVISION], &rev);
0408     if (err)
0409         return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
0410 
0411     dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
0412 
0413     return 0;
0414 }
0415 
0416 static int max77976_configure(struct max77976 *chg)
0417 {
0418     struct device *dev = &chg->client->dev;
0419     int err;
0420 
0421     /* Magic value to unlock writing to some registers */
0422     err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
0423     if (err)
0424         goto err;
0425 
0426     /*
0427      * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
0428      * Other modes are not implemented by this driver.
0429      */
0430     err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
0431     if (err)
0432         goto err;
0433 
0434     return 0;
0435 
0436 err:
0437     return dev_err_probe(dev, err, "error while configuring");
0438 }
0439 
0440 static int max77976_probe(struct i2c_client *client)
0441 {
0442     struct device *dev = &client->dev;
0443     struct power_supply_config psy_cfg = {};
0444     struct power_supply *psy;
0445     struct max77976 *chg;
0446     int err;
0447     int i;
0448 
0449     chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
0450     if (!chg)
0451         return -ENOMEM;
0452 
0453     i2c_set_clientdata(client, chg);
0454     psy_cfg.drv_data = chg;
0455     chg->client = client;
0456 
0457     chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
0458     if (IS_ERR(chg->regmap))
0459         return dev_err_probe(dev, PTR_ERR(chg->regmap),
0460                      "cannot allocate regmap\n");
0461 
0462     for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
0463         chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
0464                              max77976_reg_field[i]);
0465         if (IS_ERR(chg->rfield[i]))
0466             return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
0467                          "cannot allocate regmap field\n");
0468     }
0469 
0470     err = max77976_detect(chg);
0471     if (err)
0472         return err;
0473 
0474     err = max77976_configure(chg);
0475     if (err)
0476         return err;
0477 
0478     psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
0479     if (IS_ERR(psy))
0480         return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
0481 
0482     return 0;
0483 }
0484 
0485 static const struct i2c_device_id max77976_i2c_id[] = {
0486     { MAX77976_DRIVER_NAME, 0 },
0487     { },
0488 };
0489 MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
0490 
0491 static const struct of_device_id max77976_of_id[] = {
0492     { .compatible = "maxim,max77976" },
0493     { },
0494 };
0495 MODULE_DEVICE_TABLE(of, max77976_of_id);
0496 
0497 static struct i2c_driver max77976_driver = {
0498     .driver = {
0499         .name       = MAX77976_DRIVER_NAME,
0500         .of_match_table = max77976_of_id,
0501     },
0502     .probe_new  = max77976_probe,
0503     .id_table   = max77976_i2c_id,
0504 };
0505 module_i2c_driver(max77976_driver);
0506 
0507 MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
0508 MODULE_DESCRIPTION("Maxim MAX77976 charger driver");
0509 MODULE_LICENSE("GPL v2");