162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2018 Spreadtrum Communications Inc.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/platform_device.h>
662306a36Sopenharmony_ci#include <linux/power_supply.h>
762306a36Sopenharmony_ci#include <linux/usb/phy.h>
862306a36Sopenharmony_ci#include <linux/regmap.h>
962306a36Sopenharmony_ci#include <linux/notifier.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* PMIC global registers definition */
1362306a36Sopenharmony_ci#define SC2731_CHARGE_STATUS		0xedc
1462306a36Sopenharmony_ci#define SC2731_CHARGE_FULL		BIT(4)
1562306a36Sopenharmony_ci#define SC2731_MODULE_EN1		0xc0c
1662306a36Sopenharmony_ci#define SC2731_CHARGE_EN		BIT(5)
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* SC2731 switch charger registers definition */
1962306a36Sopenharmony_ci#define SC2731_CHG_CFG0			0x0
2062306a36Sopenharmony_ci#define SC2731_CHG_CFG1			0x4
2162306a36Sopenharmony_ci#define SC2731_CHG_CFG2			0x8
2262306a36Sopenharmony_ci#define SC2731_CHG_CFG3			0xc
2362306a36Sopenharmony_ci#define SC2731_CHG_CFG4			0x10
2462306a36Sopenharmony_ci#define SC2731_CHG_CFG5			0x28
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* SC2731_CHG_CFG0 register definition */
2762306a36Sopenharmony_ci#define SC2731_PRECHG_RNG_SHIFT		11
2862306a36Sopenharmony_ci#define SC2731_PRECHG_RNG_MASK		GENMASK(12, 11)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define SC2731_TERMINATION_VOL_MASK	GENMASK(2, 1)
3162306a36Sopenharmony_ci#define SC2731_TERMINATION_VOL_SHIFT	1
3262306a36Sopenharmony_ci#define SC2731_TERMINATION_VOL_CAL_MASK	GENMASK(8, 3)
3362306a36Sopenharmony_ci#define SC2731_TERMINATION_VOL_CAL_SHIFT	3
3462306a36Sopenharmony_ci#define SC2731_TERMINATION_CUR_MASK	GENMASK(2, 0)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define SC2731_CC_EN			BIT(13)
3762306a36Sopenharmony_ci#define SC2731_CHARGER_PD		BIT(0)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* SC2731_CHG_CFG1 register definition */
4062306a36Sopenharmony_ci#define SC2731_CUR_MASK			GENMASK(5, 0)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* SC2731_CHG_CFG5 register definition */
4362306a36Sopenharmony_ci#define SC2731_CUR_LIMIT_SHIFT		8
4462306a36Sopenharmony_ci#define SC2731_CUR_LIMIT_MASK		GENMASK(9, 8)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Default current definition (unit is mA) */
4762306a36Sopenharmony_ci#define SC2731_CURRENT_LIMIT_100	100
4862306a36Sopenharmony_ci#define SC2731_CURRENT_LIMIT_500	500
4962306a36Sopenharmony_ci#define SC2731_CURRENT_LIMIT_900	900
5062306a36Sopenharmony_ci#define SC2731_CURRENT_LIMIT_2000	2000
5162306a36Sopenharmony_ci#define SC2731_CURRENT_PRECHG		450
5262306a36Sopenharmony_ci#define SC2731_CURRENT_STEP		50
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct sc2731_charger_info {
5562306a36Sopenharmony_ci	struct device *dev;
5662306a36Sopenharmony_ci	struct regmap *regmap;
5762306a36Sopenharmony_ci	struct usb_phy *usb_phy;
5862306a36Sopenharmony_ci	struct notifier_block usb_notify;
5962306a36Sopenharmony_ci	struct power_supply *psy_usb;
6062306a36Sopenharmony_ci	struct work_struct work;
6162306a36Sopenharmony_ci	struct mutex lock;
6262306a36Sopenharmony_ci	bool charging;
6362306a36Sopenharmony_ci	u32 base;
6462306a36Sopenharmony_ci	u32 limit;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void sc2731_charger_stop_charge(struct sc2731_charger_info *info)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
7062306a36Sopenharmony_ci			   SC2731_CC_EN, 0);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
7362306a36Sopenharmony_ci			   SC2731_CHARGER_PD, SC2731_CHARGER_PD);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int sc2731_charger_start_charge(struct sc2731_charger_info *info)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int ret;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Enable charger constant current mode */
8162306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
8262306a36Sopenharmony_ci				 SC2731_CC_EN, SC2731_CC_EN);
8362306a36Sopenharmony_ci	if (ret)
8462306a36Sopenharmony_ci		return ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Start charging */
8762306a36Sopenharmony_ci	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
8862306a36Sopenharmony_ci				  SC2731_CHARGER_PD, 0);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int sc2731_charger_set_current_limit(struct sc2731_charger_info *info,
9262306a36Sopenharmony_ci					    u32 limit)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 val;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (limit <= SC2731_CURRENT_LIMIT_100)
9762306a36Sopenharmony_ci		val = 0;
9862306a36Sopenharmony_ci	else if (limit <= SC2731_CURRENT_LIMIT_500)
9962306a36Sopenharmony_ci		val = 3;
10062306a36Sopenharmony_ci	else if (limit <= SC2731_CURRENT_LIMIT_900)
10162306a36Sopenharmony_ci		val = 2;
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		val = 1;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG5,
10662306a36Sopenharmony_ci				  SC2731_CUR_LIMIT_MASK,
10762306a36Sopenharmony_ci				  val << SC2731_CUR_LIMIT_SHIFT);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int sc2731_charger_set_current(struct sc2731_charger_info *info, u32 cur)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 val;
11362306a36Sopenharmony_ci	int ret;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (cur > SC2731_CURRENT_LIMIT_2000)
11662306a36Sopenharmony_ci		cur = SC2731_CURRENT_LIMIT_2000;
11762306a36Sopenharmony_ci	else if (cur < SC2731_CURRENT_PRECHG)
11862306a36Sopenharmony_ci		cur = SC2731_CURRENT_PRECHG;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Calculate the step value, each step is 50 mA */
12162306a36Sopenharmony_ci	val = (cur - SC2731_CURRENT_PRECHG) / SC2731_CURRENT_STEP;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Set pre-charge current as 450 mA */
12462306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
12562306a36Sopenharmony_ci				 SC2731_PRECHG_RNG_MASK,
12662306a36Sopenharmony_ci				 0x3 << SC2731_PRECHG_RNG_SHIFT);
12762306a36Sopenharmony_ci	if (ret)
12862306a36Sopenharmony_ci		return ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG1,
13162306a36Sopenharmony_ci				  SC2731_CUR_MASK, val);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int sc2731_charger_get_status(struct sc2731_charger_info *info)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	u32 val;
13762306a36Sopenharmony_ci	int ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = regmap_read(info->regmap, SC2731_CHARGE_STATUS, &val);
14062306a36Sopenharmony_ci	if (ret)
14162306a36Sopenharmony_ci		return ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (val & SC2731_CHARGE_FULL)
14462306a36Sopenharmony_ci		return POWER_SUPPLY_STATUS_FULL;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return POWER_SUPPLY_STATUS_CHARGING;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int sc2731_charger_get_current(struct sc2731_charger_info *info,
15062306a36Sopenharmony_ci				      u32 *cur)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int ret;
15362306a36Sopenharmony_ci	u32 val;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, info->base + SC2731_CHG_CFG1, &val);
15662306a36Sopenharmony_ci	if (ret)
15762306a36Sopenharmony_ci		return ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	val &= SC2731_CUR_MASK;
16062306a36Sopenharmony_ci	*cur = val * SC2731_CURRENT_STEP + SC2731_CURRENT_PRECHG;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int sc2731_charger_get_current_limit(struct sc2731_charger_info *info,
16662306a36Sopenharmony_ci					    u32 *cur)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int ret;
16962306a36Sopenharmony_ci	u32 val;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ret = regmap_read(info->regmap, info->base + SC2731_CHG_CFG5, &val);
17262306a36Sopenharmony_ci	if (ret)
17362306a36Sopenharmony_ci		return ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	val = (val & SC2731_CUR_LIMIT_MASK) >> SC2731_CUR_LIMIT_SHIFT;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	switch (val) {
17862306a36Sopenharmony_ci	case 0:
17962306a36Sopenharmony_ci		*cur = SC2731_CURRENT_LIMIT_100;
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	case 1:
18362306a36Sopenharmony_ci		*cur = SC2731_CURRENT_LIMIT_2000;
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	case 2:
18762306a36Sopenharmony_ci		*cur = SC2731_CURRENT_LIMIT_900;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	case 3:
19162306a36Sopenharmony_ci		*cur = SC2731_CURRENT_LIMIT_500;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int
20262306a36Sopenharmony_cisc2731_charger_usb_set_property(struct power_supply *psy,
20362306a36Sopenharmony_ci				enum power_supply_property psp,
20462306a36Sopenharmony_ci				const union power_supply_propval *val)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct sc2731_charger_info *info = power_supply_get_drvdata(psy);
20762306a36Sopenharmony_ci	int ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	mutex_lock(&info->lock);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!info->charging) {
21262306a36Sopenharmony_ci		mutex_unlock(&info->lock);
21362306a36Sopenharmony_ci		return -ENODEV;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	switch (psp) {
21762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
21862306a36Sopenharmony_ci		ret = sc2731_charger_set_current(info, val->intval / 1000);
21962306a36Sopenharmony_ci		if (ret < 0)
22062306a36Sopenharmony_ci			dev_err(info->dev, "set charge current failed\n");
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
22462306a36Sopenharmony_ci		ret = sc2731_charger_set_current_limit(info,
22562306a36Sopenharmony_ci						       val->intval / 1000);
22662306a36Sopenharmony_ci		if (ret < 0)
22762306a36Sopenharmony_ci			dev_err(info->dev, "set input current limit failed\n");
22862306a36Sopenharmony_ci		break;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	default:
23162306a36Sopenharmony_ci		ret = -EINVAL;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mutex_unlock(&info->lock);
23562306a36Sopenharmony_ci	return ret;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int sc2731_charger_usb_get_property(struct power_supply *psy,
23962306a36Sopenharmony_ci					   enum power_supply_property psp,
24062306a36Sopenharmony_ci					   union power_supply_propval *val)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct sc2731_charger_info *info = power_supply_get_drvdata(psy);
24362306a36Sopenharmony_ci	int ret = 0;
24462306a36Sopenharmony_ci	u32 cur;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	mutex_lock(&info->lock);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	switch (psp) {
24962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
25062306a36Sopenharmony_ci		if (info->charging)
25162306a36Sopenharmony_ci			val->intval = sc2731_charger_get_status(info);
25262306a36Sopenharmony_ci		else
25362306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
25762306a36Sopenharmony_ci		if (!info->charging) {
25862306a36Sopenharmony_ci			val->intval = 0;
25962306a36Sopenharmony_ci		} else {
26062306a36Sopenharmony_ci			ret = sc2731_charger_get_current(info, &cur);
26162306a36Sopenharmony_ci			if (ret)
26262306a36Sopenharmony_ci				goto out;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci			val->intval = cur * 1000;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
26962306a36Sopenharmony_ci		if (!info->charging) {
27062306a36Sopenharmony_ci			val->intval = 0;
27162306a36Sopenharmony_ci		} else {
27262306a36Sopenharmony_ci			ret = sc2731_charger_get_current_limit(info, &cur);
27362306a36Sopenharmony_ci			if (ret)
27462306a36Sopenharmony_ci				goto out;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			val->intval = cur * 1000;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		ret = -EINVAL;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciout:
28562306a36Sopenharmony_ci	mutex_unlock(&info->lock);
28662306a36Sopenharmony_ci	return ret;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int sc2731_charger_property_is_writeable(struct power_supply *psy,
29062306a36Sopenharmony_ci						enum power_supply_property psp)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int ret;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	switch (psp) {
29562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
29662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
29762306a36Sopenharmony_ci		ret = 1;
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	default:
30162306a36Sopenharmony_ci		ret = 0;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return ret;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic enum power_supply_property sc2731_usb_props[] = {
30862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
30962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
31062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic const struct power_supply_desc sc2731_charger_desc = {
31462306a36Sopenharmony_ci	.name			= "sc2731_charger",
31562306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_USB,
31662306a36Sopenharmony_ci	.properties		= sc2731_usb_props,
31762306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(sc2731_usb_props),
31862306a36Sopenharmony_ci	.get_property		= sc2731_charger_usb_get_property,
31962306a36Sopenharmony_ci	.set_property		= sc2731_charger_usb_set_property,
32062306a36Sopenharmony_ci	.property_is_writeable	= sc2731_charger_property_is_writeable,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void sc2731_charger_work(struct work_struct *data)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct sc2731_charger_info *info =
32662306a36Sopenharmony_ci		container_of(data, struct sc2731_charger_info, work);
32762306a36Sopenharmony_ci	int ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	mutex_lock(&info->lock);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (info->limit > 0 && !info->charging) {
33262306a36Sopenharmony_ci		/* set current limitation and start to charge */
33362306a36Sopenharmony_ci		ret = sc2731_charger_set_current_limit(info, info->limit);
33462306a36Sopenharmony_ci		if (ret)
33562306a36Sopenharmony_ci			goto out;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		ret = sc2731_charger_set_current(info, info->limit);
33862306a36Sopenharmony_ci		if (ret)
33962306a36Sopenharmony_ci			goto out;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		ret = sc2731_charger_start_charge(info);
34262306a36Sopenharmony_ci		if (ret)
34362306a36Sopenharmony_ci			goto out;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		info->charging = true;
34662306a36Sopenharmony_ci	} else if (!info->limit && info->charging) {
34762306a36Sopenharmony_ci		/* Stop charging */
34862306a36Sopenharmony_ci		info->charging = false;
34962306a36Sopenharmony_ci		sc2731_charger_stop_charge(info);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciout:
35362306a36Sopenharmony_ci	mutex_unlock(&info->lock);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int sc2731_charger_usb_change(struct notifier_block *nb,
35762306a36Sopenharmony_ci				     unsigned long limit, void *data)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct sc2731_charger_info *info =
36062306a36Sopenharmony_ci		container_of(nb, struct sc2731_charger_info, usb_notify);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	info->limit = limit;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	schedule_work(&info->work);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return NOTIFY_OK;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int sc2731_charger_hw_init(struct sc2731_charger_info *info)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct power_supply_battery_info *bat_info;
37262306a36Sopenharmony_ci	u32 term_currrent, term_voltage, cur_val, vol_val;
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Enable charger module */
37662306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, SC2731_MODULE_EN1,
37762306a36Sopenharmony_ci				 SC2731_CHARGE_EN, SC2731_CHARGE_EN);
37862306a36Sopenharmony_ci	if (ret)
37962306a36Sopenharmony_ci		return ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ret = power_supply_get_battery_info(info->psy_usb, &bat_info);
38262306a36Sopenharmony_ci	if (ret) {
38362306a36Sopenharmony_ci		dev_warn(info->dev, "no battery information is supplied\n");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		/*
38662306a36Sopenharmony_ci		 * If no battery information is supplied, we should set
38762306a36Sopenharmony_ci		 * default charge termination current to 120 mA, and default
38862306a36Sopenharmony_ci		 * charge termination voltage to 4.35V.
38962306a36Sopenharmony_ci		 */
39062306a36Sopenharmony_ci		cur_val = 0x2;
39162306a36Sopenharmony_ci		vol_val = 0x1;
39262306a36Sopenharmony_ci	} else {
39362306a36Sopenharmony_ci		term_currrent = bat_info->charge_term_current_ua / 1000;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		if (term_currrent <= 90)
39662306a36Sopenharmony_ci			cur_val = 0;
39762306a36Sopenharmony_ci		else if (term_currrent >= 265)
39862306a36Sopenharmony_ci			cur_val = 0x7;
39962306a36Sopenharmony_ci		else
40062306a36Sopenharmony_ci			cur_val = ((term_currrent - 90) / 25) + 1;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		term_voltage = bat_info->constant_charge_voltage_max_uv / 1000;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (term_voltage > 4500)
40562306a36Sopenharmony_ci			term_voltage = 4500;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		if (term_voltage > 4200)
40862306a36Sopenharmony_ci			vol_val = (term_voltage - 4200) / 100;
40962306a36Sopenharmony_ci		else
41062306a36Sopenharmony_ci			vol_val = 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		power_supply_put_battery_info(info->psy_usb, bat_info);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* Set charge termination current */
41662306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG2,
41762306a36Sopenharmony_ci				 SC2731_TERMINATION_CUR_MASK, cur_val);
41862306a36Sopenharmony_ci	if (ret)
41962306a36Sopenharmony_ci		goto error;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Set charge termination voltage */
42262306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
42362306a36Sopenharmony_ci				 SC2731_TERMINATION_VOL_MASK |
42462306a36Sopenharmony_ci				 SC2731_TERMINATION_VOL_CAL_MASK,
42562306a36Sopenharmony_ci				 (vol_val << SC2731_TERMINATION_VOL_SHIFT) |
42662306a36Sopenharmony_ci				 (0x6 << SC2731_TERMINATION_VOL_CAL_SHIFT));
42762306a36Sopenharmony_ci	if (ret)
42862306a36Sopenharmony_ci		goto error;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cierror:
43362306a36Sopenharmony_ci	regmap_update_bits(info->regmap, SC2731_MODULE_EN1, SC2731_CHARGE_EN, 0);
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void sc2731_charger_detect_status(struct sc2731_charger_info *info)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	unsigned int min, max;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * If the USB charger status has been USB_CHARGER_PRESENT before
44362306a36Sopenharmony_ci	 * registering the notifier, we should start to charge with getting
44462306a36Sopenharmony_ci	 * the charge current.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	if (info->usb_phy->chg_state != USB_CHARGER_PRESENT)
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	usb_phy_get_charger_current(info->usb_phy, &min, &max);
45062306a36Sopenharmony_ci	info->limit = min;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	schedule_work(&info->work);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int sc2731_charger_probe(struct platform_device *pdev)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
45862306a36Sopenharmony_ci	struct sc2731_charger_info *info;
45962306a36Sopenharmony_ci	struct power_supply_config charger_cfg = { };
46062306a36Sopenharmony_ci	int ret;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
46362306a36Sopenharmony_ci	if (!info)
46462306a36Sopenharmony_ci		return -ENOMEM;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	mutex_init(&info->lock);
46762306a36Sopenharmony_ci	info->dev = &pdev->dev;
46862306a36Sopenharmony_ci	INIT_WORK(&info->work, sc2731_charger_work);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
47162306a36Sopenharmony_ci	if (!info->regmap) {
47262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get charger regmap\n");
47362306a36Sopenharmony_ci		return -ENODEV;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ret = of_property_read_u32(np, "reg", &info->base);
47762306a36Sopenharmony_ci	if (ret) {
47862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get register address\n");
47962306a36Sopenharmony_ci		return -ENODEV;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	charger_cfg.drv_data = info;
48362306a36Sopenharmony_ci	charger_cfg.of_node = np;
48462306a36Sopenharmony_ci	info->psy_usb = devm_power_supply_register(&pdev->dev,
48562306a36Sopenharmony_ci						   &sc2731_charger_desc,
48662306a36Sopenharmony_ci						   &charger_cfg);
48762306a36Sopenharmony_ci	if (IS_ERR(info->psy_usb)) {
48862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register power supply\n");
48962306a36Sopenharmony_ci		return PTR_ERR(info->psy_usb);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = sc2731_charger_hw_init(info);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	info->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0);
49762306a36Sopenharmony_ci	if (IS_ERR(info->usb_phy)) {
49862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to find USB phy\n");
49962306a36Sopenharmony_ci		return PTR_ERR(info->usb_phy);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	info->usb_notify.notifier_call = sc2731_charger_usb_change;
50362306a36Sopenharmony_ci	ret = usb_register_notifier(info->usb_phy, &info->usb_notify);
50462306a36Sopenharmony_ci	if (ret) {
50562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register notifier: %d\n", ret);
50662306a36Sopenharmony_ci		return ret;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	sc2731_charger_detect_status(info);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int sc2731_charger_remove(struct platform_device *pdev)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct sc2731_charger_info *info = platform_get_drvdata(pdev);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	usb_unregister_notifier(info->usb_phy, &info->usb_notify);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic const struct of_device_id sc2731_charger_of_match[] = {
52462306a36Sopenharmony_ci	{ .compatible = "sprd,sc2731-charger", },
52562306a36Sopenharmony_ci	{ }
52662306a36Sopenharmony_ci};
52762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sc2731_charger_of_match);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic struct platform_driver sc2731_charger_driver = {
53062306a36Sopenharmony_ci	.driver = {
53162306a36Sopenharmony_ci		.name = "sc2731-charger",
53262306a36Sopenharmony_ci		.of_match_table = sc2731_charger_of_match,
53362306a36Sopenharmony_ci	},
53462306a36Sopenharmony_ci	.probe = sc2731_charger_probe,
53562306a36Sopenharmony_ci	.remove = sc2731_charger_remove,
53662306a36Sopenharmony_ci};
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cimodule_platform_driver(sc2731_charger_driver);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum SC2731 Charger Driver");
54162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
542