510 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * max77976_charger.c - Driver for the Maxim MAX77976 battery charger
 | |
|  *
 | |
|  * Copyright (C) 2021 Luca Ceresoli
 | |
|  * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
 | |
|  */
 | |
| 
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/power_supply.h>
 | |
| #include <linux/regmap.h>
 | |
| 
 | |
| #define MAX77976_DRIVER_NAME	"max77976-charger"
 | |
| #define MAX77976_CHIP_ID	0x76
 | |
| 
 | |
| static const char *max77976_manufacturer	= "Maxim Integrated";
 | |
| static const char *max77976_model		= "MAX77976";
 | |
| 
 | |
| /* --------------------------------------------------------------------------
 | |
|  * Register map
 | |
|  */
 | |
| 
 | |
| #define MAX77976_REG_CHIP_ID		0x00
 | |
| #define MAX77976_REG_CHIP_REVISION	0x01
 | |
| #define MAX77976_REG_CHG_INT_OK		0x12
 | |
| #define MAX77976_REG_CHG_DETAILS_01	0x14
 | |
| #define MAX77976_REG_CHG_CNFG_00	0x16
 | |
| #define MAX77976_REG_CHG_CNFG_02	0x18
 | |
| #define MAX77976_REG_CHG_CNFG_06	0x1c
 | |
| #define MAX77976_REG_CHG_CNFG_09	0x1f
 | |
| 
 | |
| /* CHG_DETAILS_01.CHG_DTLS values */
 | |
| enum max77976_charging_state {
 | |
| 	MAX77976_CHARGING_PREQUALIFICATION = 0x0,
 | |
| 	MAX77976_CHARGING_FAST_CONST_CURRENT,
 | |
| 	MAX77976_CHARGING_FAST_CONST_VOLTAGE,
 | |
| 	MAX77976_CHARGING_TOP_OFF,
 | |
| 	MAX77976_CHARGING_DONE,
 | |
| 	MAX77976_CHARGING_RESERVED_05,
 | |
| 	MAX77976_CHARGING_TIMER_FAULT,
 | |
| 	MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
 | |
| 	MAX77976_CHARGING_OFF,
 | |
| 	MAX77976_CHARGING_RESERVED_09,
 | |
| 	MAX77976_CHARGING_THERMAL_SHUTDOWN,
 | |
| 	MAX77976_CHARGING_WATCHDOG_EXPIRED,
 | |
| 	MAX77976_CHARGING_SUSPENDED_JEITA,
 | |
| 	MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
 | |
| 	MAX77976_CHARGING_SUSPENDED_PIN,
 | |
| 	MAX77976_CHARGING_RESERVED_0F,
 | |
| };
 | |
| 
 | |
| /* CHG_DETAILS_01.BAT_DTLS values */
 | |
| enum max77976_battery_state {
 | |
| 	MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
 | |
| 	MAX77976_BATTERY_PREQUALIFICATION,
 | |
| 	MAX77976_BATTERY_TIMER_FAULT,
 | |
| 	MAX77976_BATTERY_REGULAR_VOLTAGE,
 | |
| 	MAX77976_BATTERY_LOW_VOLTAGE,
 | |
| 	MAX77976_BATTERY_OVERVOLTAGE,
 | |
| 	MAX77976_BATTERY_RESERVED,
 | |
| 	MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
 | |
| };
 | |
| 
 | |
| /* CHG_CNFG_00.MODE values */
 | |
| enum max77976_mode {
 | |
| 	MAX77976_MODE_CHARGER_BUCK		= 0x5,
 | |
| 	MAX77976_MODE_BOOST			= 0x9,
 | |
| };
 | |
| 
 | |
| /* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
 | |
| #define MAX77976_CHG_CC_STEP			  50000U
 | |
| #define MAX77976_CHG_CC_MIN			 100000U
 | |
| #define MAX77976_CHG_CC_MAX			5500000U
 | |
| 
 | |
| /* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
 | |
| #define MAX77976_CHGIN_ILIM_STEP		 100000U
 | |
| #define MAX77976_CHGIN_ILIM_MIN			 100000U
 | |
| #define MAX77976_CHGIN_ILIM_MAX			3200000U
 | |
| 
 | |
| enum max77976_field_idx {
 | |
| 	VERSION, REVISION,                      /* CHIP_REVISION */
 | |
| 	CHGIN_OK,                               /* CHG_INT_OK */
 | |
| 	BAT_DTLS, CHG_DTLS,                     /* CHG_DETAILS_01 */
 | |
| 	MODE,                                   /* CHG_CNFG_00 */
 | |
| 	CHG_CC,                                 /* CHG_CNFG_02 */
 | |
| 	CHGPROT,                                /* CHG_CNFG_06 */
 | |
| 	CHGIN_ILIM,                             /* CHG_CNFG_09 */
 | |
| 	MAX77976_N_REGMAP_FIELDS
 | |
| };
 | |
| 
 | |
| static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
 | |
| 	[VERSION]        = REG_FIELD(MAX77976_REG_CHIP_REVISION,   4, 7),
 | |
| 	[REVISION]       = REG_FIELD(MAX77976_REG_CHIP_REVISION,   0, 3),
 | |
| 	[CHGIN_OK]       = REG_FIELD(MAX77976_REG_CHG_INT_OK,      6, 6),
 | |
| 	[CHG_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  0, 3),
 | |
| 	[BAT_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  4, 6),
 | |
| 	[MODE]           = REG_FIELD(MAX77976_REG_CHG_CNFG_00,     0, 3),
 | |
| 	[CHG_CC]         = REG_FIELD(MAX77976_REG_CHG_CNFG_02,     0, 6),
 | |
| 	[CHGPROT]        = REG_FIELD(MAX77976_REG_CHG_CNFG_06,     2, 3),
 | |
| 	[CHGIN_ILIM]     = REG_FIELD(MAX77976_REG_CHG_CNFG_09,     0, 5),
 | |
| };
 | |
| 
 | |
| static const struct regmap_config max77976_regmap_config = {
 | |
| 	.reg_bits = 8,
 | |
| 	.val_bits = 8,
 | |
| 	.max_register = 0x24,
 | |
| };
 | |
| 
 | |
| /* --------------------------------------------------------------------------
 | |
|  * Data structures
 | |
|  */
 | |
| 
 | |
| struct max77976 {
 | |
| 	struct i2c_client	*client;
 | |
| 	struct regmap		*regmap;
 | |
| 	struct regmap_field	*rfield[MAX77976_N_REGMAP_FIELDS];
 | |
| };
 | |
| 
 | |
| /* --------------------------------------------------------------------------
 | |
|  * power_supply properties
 | |
|  */
 | |
| 
 | |
| static int max77976_get_status(struct max77976 *chg, int *val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[CHG_DTLS], ®val);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	switch (regval) {
 | |
| 	case MAX77976_CHARGING_PREQUALIFICATION:
 | |
| 	case MAX77976_CHARGING_FAST_CONST_CURRENT:
 | |
| 	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
 | |
| 	case MAX77976_CHARGING_TOP_OFF:
 | |
| 		*val = POWER_SUPPLY_STATUS_CHARGING;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_DONE:
 | |
| 		*val = POWER_SUPPLY_STATUS_FULL;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_TIMER_FAULT:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_JEITA:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_PIN:
 | |
| 		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_OFF:
 | |
| 	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
 | |
| 	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
 | |
| 		*val = POWER_SUPPLY_STATUS_DISCHARGING;
 | |
| 		break;
 | |
| 	default:
 | |
| 		*val = POWER_SUPPLY_STATUS_UNKNOWN;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_get_charge_type(struct max77976 *chg, int *val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[CHG_DTLS], ®val);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	switch (regval) {
 | |
| 	case MAX77976_CHARGING_PREQUALIFICATION:
 | |
| 		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_FAST_CONST_CURRENT:
 | |
| 	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
 | |
| 		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_TOP_OFF:
 | |
| 		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
 | |
| 		break;
 | |
| 	case MAX77976_CHARGING_DONE:
 | |
| 	case MAX77976_CHARGING_TIMER_FAULT:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
 | |
| 	case MAX77976_CHARGING_OFF:
 | |
| 	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
 | |
| 	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_JEITA:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
 | |
| 	case MAX77976_CHARGING_SUSPENDED_PIN:
 | |
| 		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
 | |
| 		break;
 | |
| 	default:
 | |
| 		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_get_health(struct max77976 *chg, int *val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[BAT_DTLS], ®val);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	switch (regval) {
 | |
| 	case MAX77976_BATTERY_BATTERY_REMOVAL:
 | |
| 		*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
 | |
| 		break;
 | |
| 	case MAX77976_BATTERY_LOW_VOLTAGE:
 | |
| 	case MAX77976_BATTERY_REGULAR_VOLTAGE:
 | |
| 		*val = POWER_SUPPLY_HEALTH_GOOD;
 | |
| 		break;
 | |
| 	case MAX77976_BATTERY_TIMER_FAULT:
 | |
| 		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
 | |
| 		break;
 | |
| 	case MAX77976_BATTERY_OVERVOLTAGE:
 | |
| 		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 | |
| 		break;
 | |
| 	case MAX77976_BATTERY_PREQUALIFICATION:
 | |
| 	case MAX77976_BATTERY_BATTERY_ONLY:
 | |
| 		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
 | |
| 		break;
 | |
| 	default:
 | |
| 		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_get_online(struct max77976 *chg, int *val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[CHGIN_OK], ®val);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	*val = (regval ? 1 : 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
 | |
| 				unsigned int clamp_min, unsigned int clamp_max,
 | |
| 				unsigned int mult, int *val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[fidx], ®val);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	*val = clamp_val(regval * mult, clamp_min, clamp_max);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx,
 | |
| 				unsigned int clamp_min, unsigned int clamp_max,
 | |
| 				unsigned int div, int val)
 | |
| {
 | |
| 	unsigned int regval;
 | |
| 
 | |
| 	regval = clamp_val(val, clamp_min, clamp_max) / div;
 | |
| 
 | |
| 	return regmap_field_write(chg->rfield[fidx], regval);
 | |
| }
 | |
| 
 | |
| static int max77976_get_property(struct power_supply *psy,
 | |
| 				 enum power_supply_property psp,
 | |
| 				 union power_supply_propval *val)
 | |
| {
 | |
| 	struct max77976 *chg = power_supply_get_drvdata(psy);
 | |
| 	int err = 0;
 | |
| 
 | |
| 	switch (psp) {
 | |
| 	case POWER_SUPPLY_PROP_STATUS:
 | |
| 		err = max77976_get_status(chg, &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
 | |
| 		err = max77976_get_charge_type(chg, &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_HEALTH:
 | |
| 		err = max77976_get_health(chg, &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_ONLINE:
 | |
| 		err = max77976_get_online(chg, &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
 | |
| 		val->intval = MAX77976_CHG_CC_MAX;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 | |
| 		err = max77976_get_integer(chg, CHG_CC,
 | |
| 					   MAX77976_CHG_CC_MIN,
 | |
| 					   MAX77976_CHG_CC_MAX,
 | |
| 					   MAX77976_CHG_CC_STEP,
 | |
| 					   &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 | |
| 		err = max77976_get_integer(chg, CHGIN_ILIM,
 | |
| 					   MAX77976_CHGIN_ILIM_MIN,
 | |
| 					   MAX77976_CHGIN_ILIM_MAX,
 | |
| 					   MAX77976_CHGIN_ILIM_STEP,
 | |
| 					   &val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_MODEL_NAME:
 | |
| 		val->strval = max77976_model;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_MANUFACTURER:
 | |
| 		val->strval = max77976_manufacturer;
 | |
| 		break;
 | |
| 	default:
 | |
| 		err = -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int max77976_set_property(struct power_supply *psy,
 | |
| 				 enum power_supply_property psp,
 | |
| 				 const union power_supply_propval *val)
 | |
| {
 | |
| 	struct max77976 *chg = power_supply_get_drvdata(psy);
 | |
| 	int err = 0;
 | |
| 
 | |
| 	switch (psp) {
 | |
| 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 | |
| 		err = max77976_set_integer(chg, CHG_CC,
 | |
| 					   MAX77976_CHG_CC_MIN,
 | |
| 					   MAX77976_CHG_CC_MAX,
 | |
| 					   MAX77976_CHG_CC_STEP,
 | |
| 					   val->intval);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 | |
| 		err = max77976_set_integer(chg, CHGIN_ILIM,
 | |
| 					   MAX77976_CHGIN_ILIM_MIN,
 | |
| 					   MAX77976_CHGIN_ILIM_MAX,
 | |
| 					   MAX77976_CHGIN_ILIM_STEP,
 | |
| 					   val->intval);
 | |
| 		break;
 | |
| 	default:
 | |
| 		err = -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| };
 | |
| 
 | |
| static int max77976_property_is_writeable(struct power_supply *psy,
 | |
| 					  enum power_supply_property psp)
 | |
| {
 | |
| 	switch (psp) {
 | |
| 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 | |
| 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 | |
| 		return true;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static enum power_supply_property max77976_psy_props[] = {
 | |
| 	POWER_SUPPLY_PROP_STATUS,
 | |
| 	POWER_SUPPLY_PROP_CHARGE_TYPE,
 | |
| 	POWER_SUPPLY_PROP_HEALTH,
 | |
| 	POWER_SUPPLY_PROP_ONLINE,
 | |
| 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
 | |
| 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
 | |
| 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 | |
| 	POWER_SUPPLY_PROP_MODEL_NAME,
 | |
| 	POWER_SUPPLY_PROP_MANUFACTURER,
 | |
| };
 | |
| 
 | |
| static const struct power_supply_desc max77976_psy_desc = {
 | |
| 	.name			= MAX77976_DRIVER_NAME,
 | |
| 	.type			= POWER_SUPPLY_TYPE_USB,
 | |
| 	.properties		= max77976_psy_props,
 | |
| 	.num_properties		= ARRAY_SIZE(max77976_psy_props),
 | |
| 	.get_property		= max77976_get_property,
 | |
| 	.set_property		= max77976_set_property,
 | |
| 	.property_is_writeable	= max77976_property_is_writeable,
 | |
| };
 | |
| 
 | |
| /* --------------------------------------------------------------------------
 | |
|  * Entry point
 | |
|  */
 | |
| 
 | |
| static int max77976_detect(struct max77976 *chg)
 | |
| {
 | |
| 	struct device *dev = &chg->client->dev;
 | |
| 	unsigned int id, ver, rev;
 | |
| 	int err;
 | |
| 
 | |
| 	err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
 | |
| 	if (err)
 | |
| 		return dev_err_probe(dev, err, "cannot read chip ID\n");
 | |
| 
 | |
| 	if (id != MAX77976_CHIP_ID)
 | |
| 		return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
 | |
| 
 | |
| 	err = regmap_field_read(chg->rfield[VERSION], &ver);
 | |
| 	if (!err)
 | |
| 		err = regmap_field_read(chg->rfield[REVISION], &rev);
 | |
| 	if (err)
 | |
| 		return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
 | |
| 
 | |
| 	dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int max77976_configure(struct max77976 *chg)
 | |
| {
 | |
| 	struct device *dev = &chg->client->dev;
 | |
| 	int err;
 | |
| 
 | |
| 	/* Magic value to unlock writing to some registers */
 | |
| 	err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
 | |
| 	if (err)
 | |
| 		goto err;
 | |
| 
 | |
| 	/*
 | |
| 	 * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
 | |
| 	 * Other modes are not implemented by this driver.
 | |
| 	 */
 | |
| 	err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
 | |
| 	if (err)
 | |
| 		goto err;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err:
 | |
| 	return dev_err_probe(dev, err, "error while configuring");
 | |
| }
 | |
| 
 | |
| static int max77976_probe(struct i2c_client *client)
 | |
| {
 | |
| 	struct device *dev = &client->dev;
 | |
| 	struct power_supply_config psy_cfg = {};
 | |
| 	struct power_supply *psy;
 | |
| 	struct max77976 *chg;
 | |
| 	int err;
 | |
| 	int i;
 | |
| 
 | |
| 	chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
 | |
| 	if (!chg)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	i2c_set_clientdata(client, chg);
 | |
| 	psy_cfg.drv_data = chg;
 | |
| 	chg->client = client;
 | |
| 
 | |
| 	chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
 | |
| 	if (IS_ERR(chg->regmap))
 | |
| 		return dev_err_probe(dev, PTR_ERR(chg->regmap),
 | |
| 				     "cannot allocate regmap\n");
 | |
| 
 | |
| 	for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
 | |
| 		chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
 | |
| 							 max77976_reg_field[i]);
 | |
| 		if (IS_ERR(chg->rfield[i]))
 | |
| 			return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
 | |
| 					     "cannot allocate regmap field\n");
 | |
| 	}
 | |
| 
 | |
| 	err = max77976_detect(chg);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = max77976_configure(chg);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
 | |
| 	if (IS_ERR(psy))
 | |
| 		return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct i2c_device_id max77976_i2c_id[] = {
 | |
| 	{ MAX77976_DRIVER_NAME, 0 },
 | |
| 	{ },
 | |
| };
 | |
| MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
 | |
| 
 | |
| static const struct of_device_id max77976_of_id[] = {
 | |
| 	{ .compatible = "maxim,max77976" },
 | |
| 	{ },
 | |
| };
 | |
| MODULE_DEVICE_TABLE(of, max77976_of_id);
 | |
| 
 | |
| static struct i2c_driver max77976_driver = {
 | |
| 	.driver = {
 | |
| 		.name		= MAX77976_DRIVER_NAME,
 | |
| 		.of_match_table	= max77976_of_id,
 | |
| 	},
 | |
| 	.probe_new	= max77976_probe,
 | |
| 	.id_table	= max77976_i2c_id,
 | |
| };
 | |
| module_i2c_driver(max77976_driver);
 | |
| 
 | |
| MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
 | |
| MODULE_DESCRIPTION("Maxim MAX77976 charger driver");
 | |
| MODULE_LICENSE("GPL v2");
 |