brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · d7e520d Raw
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], &regval);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], &regval);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], &regval);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], &regval);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], &regval);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