162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * max77976_charger.c - Driver for the Maxim MAX77976 battery charger
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 Luca Ceresoli
662306a36Sopenharmony_ci * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/power_supply.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define MAX77976_DRIVER_NAME	"max77976-charger"
1562306a36Sopenharmony_ci#define MAX77976_CHIP_ID	0x76
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const char *max77976_manufacturer	= "Maxim Integrated";
1862306a36Sopenharmony_cistatic const char *max77976_model		= "MAX77976";
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* --------------------------------------------------------------------------
2162306a36Sopenharmony_ci * Register map
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define MAX77976_REG_CHIP_ID		0x00
2562306a36Sopenharmony_ci#define MAX77976_REG_CHIP_REVISION	0x01
2662306a36Sopenharmony_ci#define MAX77976_REG_CHG_INT_OK		0x12
2762306a36Sopenharmony_ci#define MAX77976_REG_CHG_DETAILS_01	0x14
2862306a36Sopenharmony_ci#define MAX77976_REG_CHG_CNFG_00	0x16
2962306a36Sopenharmony_ci#define MAX77976_REG_CHG_CNFG_02	0x18
3062306a36Sopenharmony_ci#define MAX77976_REG_CHG_CNFG_06	0x1c
3162306a36Sopenharmony_ci#define MAX77976_REG_CHG_CNFG_09	0x1f
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* CHG_DETAILS_01.CHG_DTLS values */
3462306a36Sopenharmony_cienum max77976_charging_state {
3562306a36Sopenharmony_ci	MAX77976_CHARGING_PREQUALIFICATION = 0x0,
3662306a36Sopenharmony_ci	MAX77976_CHARGING_FAST_CONST_CURRENT,
3762306a36Sopenharmony_ci	MAX77976_CHARGING_FAST_CONST_VOLTAGE,
3862306a36Sopenharmony_ci	MAX77976_CHARGING_TOP_OFF,
3962306a36Sopenharmony_ci	MAX77976_CHARGING_DONE,
4062306a36Sopenharmony_ci	MAX77976_CHARGING_RESERVED_05,
4162306a36Sopenharmony_ci	MAX77976_CHARGING_TIMER_FAULT,
4262306a36Sopenharmony_ci	MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
4362306a36Sopenharmony_ci	MAX77976_CHARGING_OFF,
4462306a36Sopenharmony_ci	MAX77976_CHARGING_RESERVED_09,
4562306a36Sopenharmony_ci	MAX77976_CHARGING_THERMAL_SHUTDOWN,
4662306a36Sopenharmony_ci	MAX77976_CHARGING_WATCHDOG_EXPIRED,
4762306a36Sopenharmony_ci	MAX77976_CHARGING_SUSPENDED_JEITA,
4862306a36Sopenharmony_ci	MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
4962306a36Sopenharmony_ci	MAX77976_CHARGING_SUSPENDED_PIN,
5062306a36Sopenharmony_ci	MAX77976_CHARGING_RESERVED_0F,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* CHG_DETAILS_01.BAT_DTLS values */
5462306a36Sopenharmony_cienum max77976_battery_state {
5562306a36Sopenharmony_ci	MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
5662306a36Sopenharmony_ci	MAX77976_BATTERY_PREQUALIFICATION,
5762306a36Sopenharmony_ci	MAX77976_BATTERY_TIMER_FAULT,
5862306a36Sopenharmony_ci	MAX77976_BATTERY_REGULAR_VOLTAGE,
5962306a36Sopenharmony_ci	MAX77976_BATTERY_LOW_VOLTAGE,
6062306a36Sopenharmony_ci	MAX77976_BATTERY_OVERVOLTAGE,
6162306a36Sopenharmony_ci	MAX77976_BATTERY_RESERVED,
6262306a36Sopenharmony_ci	MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* CHG_CNFG_00.MODE values */
6662306a36Sopenharmony_cienum max77976_mode {
6762306a36Sopenharmony_ci	MAX77976_MODE_CHARGER_BUCK		= 0x5,
6862306a36Sopenharmony_ci	MAX77976_MODE_BOOST			= 0x9,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
7262306a36Sopenharmony_ci#define MAX77976_CHG_CC_STEP			  50000U
7362306a36Sopenharmony_ci#define MAX77976_CHG_CC_MIN			 100000U
7462306a36Sopenharmony_ci#define MAX77976_CHG_CC_MAX			5500000U
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
7762306a36Sopenharmony_ci#define MAX77976_CHGIN_ILIM_STEP		 100000U
7862306a36Sopenharmony_ci#define MAX77976_CHGIN_ILIM_MIN			 100000U
7962306a36Sopenharmony_ci#define MAX77976_CHGIN_ILIM_MAX			3200000U
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cienum max77976_field_idx {
8262306a36Sopenharmony_ci	VERSION, REVISION,                      /* CHIP_REVISION */
8362306a36Sopenharmony_ci	CHGIN_OK,                               /* CHG_INT_OK */
8462306a36Sopenharmony_ci	BAT_DTLS, CHG_DTLS,                     /* CHG_DETAILS_01 */
8562306a36Sopenharmony_ci	MODE,                                   /* CHG_CNFG_00 */
8662306a36Sopenharmony_ci	CHG_CC,                                 /* CHG_CNFG_02 */
8762306a36Sopenharmony_ci	CHGPROT,                                /* CHG_CNFG_06 */
8862306a36Sopenharmony_ci	CHGIN_ILIM,                             /* CHG_CNFG_09 */
8962306a36Sopenharmony_ci	MAX77976_N_REGMAP_FIELDS
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
9362306a36Sopenharmony_ci	[VERSION]        = REG_FIELD(MAX77976_REG_CHIP_REVISION,   4, 7),
9462306a36Sopenharmony_ci	[REVISION]       = REG_FIELD(MAX77976_REG_CHIP_REVISION,   0, 3),
9562306a36Sopenharmony_ci	[CHGIN_OK]       = REG_FIELD(MAX77976_REG_CHG_INT_OK,      6, 6),
9662306a36Sopenharmony_ci	[CHG_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  0, 3),
9762306a36Sopenharmony_ci	[BAT_DTLS]       = REG_FIELD(MAX77976_REG_CHG_DETAILS_01,  4, 6),
9862306a36Sopenharmony_ci	[MODE]           = REG_FIELD(MAX77976_REG_CHG_CNFG_00,     0, 3),
9962306a36Sopenharmony_ci	[CHG_CC]         = REG_FIELD(MAX77976_REG_CHG_CNFG_02,     0, 6),
10062306a36Sopenharmony_ci	[CHGPROT]        = REG_FIELD(MAX77976_REG_CHG_CNFG_06,     2, 3),
10162306a36Sopenharmony_ci	[CHGIN_ILIM]     = REG_FIELD(MAX77976_REG_CHG_CNFG_09,     0, 5),
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const struct regmap_config max77976_regmap_config = {
10562306a36Sopenharmony_ci	.reg_bits = 8,
10662306a36Sopenharmony_ci	.val_bits = 8,
10762306a36Sopenharmony_ci	.max_register = 0x24,
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* --------------------------------------------------------------------------
11162306a36Sopenharmony_ci * Data structures
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct max77976 {
11562306a36Sopenharmony_ci	struct i2c_client	*client;
11662306a36Sopenharmony_ci	struct regmap		*regmap;
11762306a36Sopenharmony_ci	struct regmap_field	*rfield[MAX77976_N_REGMAP_FIELDS];
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/* --------------------------------------------------------------------------
12162306a36Sopenharmony_ci * power_supply properties
12262306a36Sopenharmony_ci */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int max77976_get_status(struct max77976 *chg, int *val)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	unsigned int regval;
12762306a36Sopenharmony_ci	int err;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
13062306a36Sopenharmony_ci	if (err < 0)
13162306a36Sopenharmony_ci		return err;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	switch (regval) {
13462306a36Sopenharmony_ci	case MAX77976_CHARGING_PREQUALIFICATION:
13562306a36Sopenharmony_ci	case MAX77976_CHARGING_FAST_CONST_CURRENT:
13662306a36Sopenharmony_ci	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
13762306a36Sopenharmony_ci	case MAX77976_CHARGING_TOP_OFF:
13862306a36Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_CHARGING;
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	case MAX77976_CHARGING_DONE:
14162306a36Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_FULL;
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case MAX77976_CHARGING_TIMER_FAULT:
14462306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
14562306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_JEITA:
14662306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
14762306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_PIN:
14862306a36Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case MAX77976_CHARGING_OFF:
15162306a36Sopenharmony_ci	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
15262306a36Sopenharmony_ci	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
15362306a36Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_DISCHARGING;
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	default:
15662306a36Sopenharmony_ci		*val = POWER_SUPPLY_STATUS_UNKNOWN;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int max77976_get_charge_type(struct max77976 *chg, int *val)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	unsigned int regval;
16562306a36Sopenharmony_ci	int err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
16862306a36Sopenharmony_ci	if (err < 0)
16962306a36Sopenharmony_ci		return err;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	switch (regval) {
17262306a36Sopenharmony_ci	case MAX77976_CHARGING_PREQUALIFICATION:
17362306a36Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case MAX77976_CHARGING_FAST_CONST_CURRENT:
17662306a36Sopenharmony_ci	case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
17762306a36Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	case MAX77976_CHARGING_TOP_OFF:
18062306a36Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	case MAX77976_CHARGING_DONE:
18362306a36Sopenharmony_ci	case MAX77976_CHARGING_TIMER_FAULT:
18462306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
18562306a36Sopenharmony_ci	case MAX77976_CHARGING_OFF:
18662306a36Sopenharmony_ci	case MAX77976_CHARGING_THERMAL_SHUTDOWN:
18762306a36Sopenharmony_ci	case MAX77976_CHARGING_WATCHDOG_EXPIRED:
18862306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_JEITA:
18962306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
19062306a36Sopenharmony_ci	case MAX77976_CHARGING_SUSPENDED_PIN:
19162306a36Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	default:
19462306a36Sopenharmony_ci		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int max77976_get_health(struct max77976 *chg, int *val)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	unsigned int regval;
20362306a36Sopenharmony_ci	int err;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[BAT_DTLS], &regval);
20662306a36Sopenharmony_ci	if (err < 0)
20762306a36Sopenharmony_ci		return err;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	switch (regval) {
21062306a36Sopenharmony_ci	case MAX77976_BATTERY_BATTERY_REMOVAL:
21162306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	case MAX77976_BATTERY_LOW_VOLTAGE:
21462306a36Sopenharmony_ci	case MAX77976_BATTERY_REGULAR_VOLTAGE:
21562306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_GOOD;
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	case MAX77976_BATTERY_TIMER_FAULT:
21862306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	case MAX77976_BATTERY_OVERVOLTAGE:
22162306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	case MAX77976_BATTERY_PREQUALIFICATION:
22462306a36Sopenharmony_ci	case MAX77976_BATTERY_BATTERY_ONLY:
22562306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	default:
22862306a36Sopenharmony_ci		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int max77976_get_online(struct max77976 *chg, int *val)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	unsigned int regval;
23762306a36Sopenharmony_ci	int err;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[CHGIN_OK], &regval);
24062306a36Sopenharmony_ci	if (err < 0)
24162306a36Sopenharmony_ci		return err;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	*val = (regval ? 1 : 0);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return 0;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
24962306a36Sopenharmony_ci				unsigned int clamp_min, unsigned int clamp_max,
25062306a36Sopenharmony_ci				unsigned int mult, int *val)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	unsigned int regval;
25362306a36Sopenharmony_ci	int err;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[fidx], &regval);
25662306a36Sopenharmony_ci	if (err < 0)
25762306a36Sopenharmony_ci		return err;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	*val = clamp_val(regval * mult, clamp_min, clamp_max);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx,
26562306a36Sopenharmony_ci				unsigned int clamp_min, unsigned int clamp_max,
26662306a36Sopenharmony_ci				unsigned int div, int val)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	unsigned int regval;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	regval = clamp_val(val, clamp_min, clamp_max) / div;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return regmap_field_write(chg->rfield[fidx], regval);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int max77976_get_property(struct power_supply *psy,
27662306a36Sopenharmony_ci				 enum power_supply_property psp,
27762306a36Sopenharmony_ci				 union power_supply_propval *val)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct max77976 *chg = power_supply_get_drvdata(psy);
28062306a36Sopenharmony_ci	int err = 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	switch (psp) {
28362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
28462306a36Sopenharmony_ci		err = max77976_get_status(chg, &val->intval);
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
28762306a36Sopenharmony_ci		err = max77976_get_charge_type(chg, &val->intval);
28862306a36Sopenharmony_ci		break;
28962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
29062306a36Sopenharmony_ci		err = max77976_get_health(chg, &val->intval);
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
29362306a36Sopenharmony_ci		err = max77976_get_online(chg, &val->intval);
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
29662306a36Sopenharmony_ci		val->intval = MAX77976_CHG_CC_MAX;
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
29962306a36Sopenharmony_ci		err = max77976_get_integer(chg, CHG_CC,
30062306a36Sopenharmony_ci					   MAX77976_CHG_CC_MIN,
30162306a36Sopenharmony_ci					   MAX77976_CHG_CC_MAX,
30262306a36Sopenharmony_ci					   MAX77976_CHG_CC_STEP,
30362306a36Sopenharmony_ci					   &val->intval);
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
30662306a36Sopenharmony_ci		err = max77976_get_integer(chg, CHGIN_ILIM,
30762306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_MIN,
30862306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_MAX,
30962306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_STEP,
31062306a36Sopenharmony_ci					   &val->intval);
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_MODEL_NAME:
31362306a36Sopenharmony_ci		val->strval = max77976_model;
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_MANUFACTURER:
31662306a36Sopenharmony_ci		val->strval = max77976_manufacturer;
31762306a36Sopenharmony_ci		break;
31862306a36Sopenharmony_ci	default:
31962306a36Sopenharmony_ci		err = -EINVAL;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return err;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int max77976_set_property(struct power_supply *psy,
32662306a36Sopenharmony_ci				 enum power_supply_property psp,
32762306a36Sopenharmony_ci				 const union power_supply_propval *val)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct max77976 *chg = power_supply_get_drvdata(psy);
33062306a36Sopenharmony_ci	int err = 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	switch (psp) {
33362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
33462306a36Sopenharmony_ci		err = max77976_set_integer(chg, CHG_CC,
33562306a36Sopenharmony_ci					   MAX77976_CHG_CC_MIN,
33662306a36Sopenharmony_ci					   MAX77976_CHG_CC_MAX,
33762306a36Sopenharmony_ci					   MAX77976_CHG_CC_STEP,
33862306a36Sopenharmony_ci					   val->intval);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
34162306a36Sopenharmony_ci		err = max77976_set_integer(chg, CHGIN_ILIM,
34262306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_MIN,
34362306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_MAX,
34462306a36Sopenharmony_ci					   MAX77976_CHGIN_ILIM_STEP,
34562306a36Sopenharmony_ci					   val->intval);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		err = -EINVAL;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return err;
35262306a36Sopenharmony_ci};
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int max77976_property_is_writeable(struct power_supply *psy,
35562306a36Sopenharmony_ci					  enum power_supply_property psp)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	switch (psp) {
35862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
35962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
36062306a36Sopenharmony_ci		return true;
36162306a36Sopenharmony_ci	default:
36262306a36Sopenharmony_ci		return false;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic enum power_supply_property max77976_psy_props[] = {
36762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
36862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TYPE,
36962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
37062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
37162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
37262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
37362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
37462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_MODEL_NAME,
37562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_MANUFACTURER,
37662306a36Sopenharmony_ci};
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic const struct power_supply_desc max77976_psy_desc = {
37962306a36Sopenharmony_ci	.name			= MAX77976_DRIVER_NAME,
38062306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_USB,
38162306a36Sopenharmony_ci	.properties		= max77976_psy_props,
38262306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(max77976_psy_props),
38362306a36Sopenharmony_ci	.get_property		= max77976_get_property,
38462306a36Sopenharmony_ci	.set_property		= max77976_set_property,
38562306a36Sopenharmony_ci	.property_is_writeable	= max77976_property_is_writeable,
38662306a36Sopenharmony_ci};
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/* --------------------------------------------------------------------------
38962306a36Sopenharmony_ci * Entry point
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int max77976_detect(struct max77976 *chg)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct device *dev = &chg->client->dev;
39562306a36Sopenharmony_ci	unsigned int id, ver, rev;
39662306a36Sopenharmony_ci	int err;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
39962306a36Sopenharmony_ci	if (err)
40062306a36Sopenharmony_ci		return dev_err_probe(dev, err, "cannot read chip ID\n");
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (id != MAX77976_CHIP_ID)
40362306a36Sopenharmony_ci		return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = regmap_field_read(chg->rfield[VERSION], &ver);
40662306a36Sopenharmony_ci	if (!err)
40762306a36Sopenharmony_ci		err = regmap_field_read(chg->rfield[REVISION], &rev);
40862306a36Sopenharmony_ci	if (err)
40962306a36Sopenharmony_ci		return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int max77976_configure(struct max77976 *chg)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct device *dev = &chg->client->dev;
41962306a36Sopenharmony_ci	int err;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Magic value to unlock writing to some registers */
42262306a36Sopenharmony_ci	err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
42362306a36Sopenharmony_ci	if (err)
42462306a36Sopenharmony_ci		goto err;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
42862306a36Sopenharmony_ci	 * Other modes are not implemented by this driver.
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
43162306a36Sopenharmony_ci	if (err)
43262306a36Sopenharmony_ci		goto err;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cierr:
43762306a36Sopenharmony_ci	return dev_err_probe(dev, err, "error while configuring");
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int max77976_probe(struct i2c_client *client)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct device *dev = &client->dev;
44362306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
44462306a36Sopenharmony_ci	struct power_supply *psy;
44562306a36Sopenharmony_ci	struct max77976 *chg;
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
45062306a36Sopenharmony_ci	if (!chg)
45162306a36Sopenharmony_ci		return -ENOMEM;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	i2c_set_clientdata(client, chg);
45462306a36Sopenharmony_ci	psy_cfg.drv_data = chg;
45562306a36Sopenharmony_ci	chg->client = client;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
45862306a36Sopenharmony_ci	if (IS_ERR(chg->regmap))
45962306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(chg->regmap),
46062306a36Sopenharmony_ci				     "cannot allocate regmap\n");
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
46362306a36Sopenharmony_ci		chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
46462306a36Sopenharmony_ci							 max77976_reg_field[i]);
46562306a36Sopenharmony_ci		if (IS_ERR(chg->rfield[i]))
46662306a36Sopenharmony_ci			return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
46762306a36Sopenharmony_ci					     "cannot allocate regmap field\n");
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	err = max77976_detect(chg);
47162306a36Sopenharmony_ci	if (err)
47262306a36Sopenharmony_ci		return err;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	err = max77976_configure(chg);
47562306a36Sopenharmony_ci	if (err)
47662306a36Sopenharmony_ci		return err;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
47962306a36Sopenharmony_ci	if (IS_ERR(psy))
48062306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic const struct i2c_device_id max77976_i2c_id[] = {
48662306a36Sopenharmony_ci	{ MAX77976_DRIVER_NAME, 0 },
48762306a36Sopenharmony_ci	{ },
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic const struct of_device_id max77976_of_id[] = {
49262306a36Sopenharmony_ci	{ .compatible = "maxim,max77976" },
49362306a36Sopenharmony_ci	{ },
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max77976_of_id);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic struct i2c_driver max77976_driver = {
49862306a36Sopenharmony_ci	.driver = {
49962306a36Sopenharmony_ci		.name		= MAX77976_DRIVER_NAME,
50062306a36Sopenharmony_ci		.of_match_table	= max77976_of_id,
50162306a36Sopenharmony_ci	},
50262306a36Sopenharmony_ci	.probe		= max77976_probe,
50362306a36Sopenharmony_ci	.id_table	= max77976_i2c_id,
50462306a36Sopenharmony_ci};
50562306a36Sopenharmony_cimodule_i2c_driver(max77976_driver);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ciMODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
50862306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX77976 charger driver");
50962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
510