11c3d7b03SBaolin Wang // SPDX-License-Identifier: GPL-2.0
21c3d7b03SBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc.
31c3d7b03SBaolin Wang 
41c3d7b03SBaolin Wang #include <linux/module.h>
51c3d7b03SBaolin Wang #include <linux/platform_device.h>
61c3d7b03SBaolin Wang #include <linux/power_supply.h>
71c3d7b03SBaolin Wang #include <linux/usb/phy.h>
81c3d7b03SBaolin Wang #include <linux/regmap.h>
91c3d7b03SBaolin Wang #include <linux/notifier.h>
101c3d7b03SBaolin Wang #include <linux/of.h>
111c3d7b03SBaolin Wang 
121c3d7b03SBaolin Wang /* PMIC global registers definition */
131c3d7b03SBaolin Wang #define SC2731_CHARGE_STATUS		0xedc
141c3d7b03SBaolin Wang #define SC2731_CHARGE_FULL		BIT(4)
151c3d7b03SBaolin Wang #define SC2731_MODULE_EN1		0xc0c
161c3d7b03SBaolin Wang #define SC2731_CHARGE_EN		BIT(5)
171c3d7b03SBaolin Wang 
181c3d7b03SBaolin Wang /* SC2731 switch charger registers definition */
191c3d7b03SBaolin Wang #define SC2731_CHG_CFG0			0x0
201c3d7b03SBaolin Wang #define SC2731_CHG_CFG1			0x4
211c3d7b03SBaolin Wang #define SC2731_CHG_CFG2			0x8
221c3d7b03SBaolin Wang #define SC2731_CHG_CFG3			0xc
231c3d7b03SBaolin Wang #define SC2731_CHG_CFG4			0x10
241c3d7b03SBaolin Wang #define SC2731_CHG_CFG5			0x28
251c3d7b03SBaolin Wang 
261c3d7b03SBaolin Wang /* SC2731_CHG_CFG0 register definition */
271c3d7b03SBaolin Wang #define SC2731_PRECHG_RNG_SHIFT		11
281c3d7b03SBaolin Wang #define SC2731_PRECHG_RNG_MASK		GENMASK(12, 11)
291c3d7b03SBaolin Wang 
301c3d7b03SBaolin Wang #define SC2731_TERMINATION_VOL_MASK	GENMASK(2, 1)
311c3d7b03SBaolin Wang #define SC2731_TERMINATION_VOL_SHIFT	1
321c3d7b03SBaolin Wang #define SC2731_TERMINATION_VOL_CAL_MASK	GENMASK(8, 3)
331c3d7b03SBaolin Wang #define SC2731_TERMINATION_VOL_CAL_SHIFT	3
341c3d7b03SBaolin Wang #define SC2731_TERMINATION_CUR_MASK	GENMASK(2, 0)
351c3d7b03SBaolin Wang 
361c3d7b03SBaolin Wang #define SC2731_CC_EN			BIT(13)
371c3d7b03SBaolin Wang #define SC2731_CHARGER_PD		BIT(0)
381c3d7b03SBaolin Wang 
391c3d7b03SBaolin Wang /* SC2731_CHG_CFG1 register definition */
401c3d7b03SBaolin Wang #define SC2731_CUR_MASK			GENMASK(5, 0)
411c3d7b03SBaolin Wang 
421c3d7b03SBaolin Wang /* SC2731_CHG_CFG5 register definition */
431c3d7b03SBaolin Wang #define SC2731_CUR_LIMIT_SHIFT		8
441c3d7b03SBaolin Wang #define SC2731_CUR_LIMIT_MASK		GENMASK(9, 8)
451c3d7b03SBaolin Wang 
461c3d7b03SBaolin Wang /* Default current definition (unit is mA) */
471c3d7b03SBaolin Wang #define SC2731_CURRENT_LIMIT_100	100
481c3d7b03SBaolin Wang #define SC2731_CURRENT_LIMIT_500	500
491c3d7b03SBaolin Wang #define SC2731_CURRENT_LIMIT_900	900
501c3d7b03SBaolin Wang #define SC2731_CURRENT_LIMIT_2000	2000
511c3d7b03SBaolin Wang #define SC2731_CURRENT_PRECHG		450
521c3d7b03SBaolin Wang #define SC2731_CURRENT_STEP		50
531c3d7b03SBaolin Wang 
541c3d7b03SBaolin Wang struct sc2731_charger_info {
551c3d7b03SBaolin Wang 	struct device *dev;
561c3d7b03SBaolin Wang 	struct regmap *regmap;
571c3d7b03SBaolin Wang 	struct usb_phy *usb_phy;
581c3d7b03SBaolin Wang 	struct notifier_block usb_notify;
591c3d7b03SBaolin Wang 	struct power_supply *psy_usb;
608ac1091eSBaolin Wang 	struct work_struct work;
611c3d7b03SBaolin Wang 	struct mutex lock;
621c3d7b03SBaolin Wang 	bool charging;
631c3d7b03SBaolin Wang 	u32 base;
648ac1091eSBaolin Wang 	u32 limit;
651c3d7b03SBaolin Wang };
661c3d7b03SBaolin Wang 
sc2731_charger_stop_charge(struct sc2731_charger_info * info)671c3d7b03SBaolin Wang static void sc2731_charger_stop_charge(struct sc2731_charger_info *info)
681c3d7b03SBaolin Wang {
691c3d7b03SBaolin Wang 	regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
701c3d7b03SBaolin Wang 			   SC2731_CC_EN, 0);
711c3d7b03SBaolin Wang 
721c3d7b03SBaolin Wang 	regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
731c3d7b03SBaolin Wang 			   SC2731_CHARGER_PD, SC2731_CHARGER_PD);
741c3d7b03SBaolin Wang }
751c3d7b03SBaolin Wang 
sc2731_charger_start_charge(struct sc2731_charger_info * info)761c3d7b03SBaolin Wang static int sc2731_charger_start_charge(struct sc2731_charger_info *info)
771c3d7b03SBaolin Wang {
781c3d7b03SBaolin Wang 	int ret;
791c3d7b03SBaolin Wang 
801c3d7b03SBaolin Wang 	/* Enable charger constant current mode */
811c3d7b03SBaolin Wang 	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
821c3d7b03SBaolin Wang 				 SC2731_CC_EN, SC2731_CC_EN);
831c3d7b03SBaolin Wang 	if (ret)
841c3d7b03SBaolin Wang 		return ret;
851c3d7b03SBaolin Wang 
861c3d7b03SBaolin Wang 	/* Start charging */
871c3d7b03SBaolin Wang 	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
881c3d7b03SBaolin Wang 				  SC2731_CHARGER_PD, 0);
891c3d7b03SBaolin Wang }
901c3d7b03SBaolin Wang 
sc2731_charger_set_current_limit(struct sc2731_charger_info * info,u32 limit)911c3d7b03SBaolin Wang static int sc2731_charger_set_current_limit(struct sc2731_charger_info *info,
921c3d7b03SBaolin Wang 					    u32 limit)
931c3d7b03SBaolin Wang {
941c3d7b03SBaolin Wang 	u32 val;
951c3d7b03SBaolin Wang 
961c3d7b03SBaolin Wang 	if (limit <= SC2731_CURRENT_LIMIT_100)
971c3d7b03SBaolin Wang 		val = 0;
981c3d7b03SBaolin Wang 	else if (limit <= SC2731_CURRENT_LIMIT_500)
991c3d7b03SBaolin Wang 		val = 3;
1001c3d7b03SBaolin Wang 	else if (limit <= SC2731_CURRENT_LIMIT_900)
1011c3d7b03SBaolin Wang 		val = 2;
1021c3d7b03SBaolin Wang 	else
1031c3d7b03SBaolin Wang 		val = 1;
1041c3d7b03SBaolin Wang 
1051c3d7b03SBaolin Wang 	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG5,
1061c3d7b03SBaolin Wang 				  SC2731_CUR_LIMIT_MASK,
1071c3d7b03SBaolin Wang 				  val << SC2731_CUR_LIMIT_SHIFT);
1081c3d7b03SBaolin Wang }
1091c3d7b03SBaolin Wang 
sc2731_charger_set_current(struct sc2731_charger_info * info,u32 cur)1101c3d7b03SBaolin Wang static int sc2731_charger_set_current(struct sc2731_charger_info *info, u32 cur)
1111c3d7b03SBaolin Wang {
1121c3d7b03SBaolin Wang 	u32 val;
1131c3d7b03SBaolin Wang 	int ret;
1141c3d7b03SBaolin Wang 
1151c3d7b03SBaolin Wang 	if (cur > SC2731_CURRENT_LIMIT_2000)
1161c3d7b03SBaolin Wang 		cur = SC2731_CURRENT_LIMIT_2000;
1171c3d7b03SBaolin Wang 	else if (cur < SC2731_CURRENT_PRECHG)
1181c3d7b03SBaolin Wang 		cur = SC2731_CURRENT_PRECHG;
1191c3d7b03SBaolin Wang 
1201c3d7b03SBaolin Wang 	/* Calculate the step value, each step is 50 mA */
1211c3d7b03SBaolin Wang 	val = (cur - SC2731_CURRENT_PRECHG) / SC2731_CURRENT_STEP;
1221c3d7b03SBaolin Wang 
1231c3d7b03SBaolin Wang 	/* Set pre-charge current as 450 mA */
1241c3d7b03SBaolin Wang 	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
1251c3d7b03SBaolin Wang 				 SC2731_PRECHG_RNG_MASK,
1261c3d7b03SBaolin Wang 				 0x3 << SC2731_PRECHG_RNG_SHIFT);
1271c3d7b03SBaolin Wang 	if (ret)
1281c3d7b03SBaolin Wang 		return ret;
1291c3d7b03SBaolin Wang 
1301c3d7b03SBaolin Wang 	return regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG1,
1311c3d7b03SBaolin Wang 				  SC2731_CUR_MASK, val);
1321c3d7b03SBaolin Wang }
1331c3d7b03SBaolin Wang 
sc2731_charger_get_status(struct sc2731_charger_info * info)1341c3d7b03SBaolin Wang static int sc2731_charger_get_status(struct sc2731_charger_info *info)
1351c3d7b03SBaolin Wang {
1361c3d7b03SBaolin Wang 	u32 val;
1371c3d7b03SBaolin Wang 	int ret;
1381c3d7b03SBaolin Wang 
1391c3d7b03SBaolin Wang 	ret = regmap_read(info->regmap, SC2731_CHARGE_STATUS, &val);
1401c3d7b03SBaolin Wang 	if (ret)
1411c3d7b03SBaolin Wang 		return ret;
1421c3d7b03SBaolin Wang 
1431c3d7b03SBaolin Wang 	if (val & SC2731_CHARGE_FULL)
1441c3d7b03SBaolin Wang 		return POWER_SUPPLY_STATUS_FULL;
1451c3d7b03SBaolin Wang 
1461c3d7b03SBaolin Wang 	return POWER_SUPPLY_STATUS_CHARGING;
1471c3d7b03SBaolin Wang }
1481c3d7b03SBaolin Wang 
sc2731_charger_get_current(struct sc2731_charger_info * info,u32 * cur)1491c3d7b03SBaolin Wang static int sc2731_charger_get_current(struct sc2731_charger_info *info,
1501c3d7b03SBaolin Wang 				      u32 *cur)
1511c3d7b03SBaolin Wang {
1521c3d7b03SBaolin Wang 	int ret;
1531c3d7b03SBaolin Wang 	u32 val;
1541c3d7b03SBaolin Wang 
1551c3d7b03SBaolin Wang 	ret = regmap_read(info->regmap, info->base + SC2731_CHG_CFG1, &val);
1561c3d7b03SBaolin Wang 	if (ret)
1571c3d7b03SBaolin Wang 		return ret;
1581c3d7b03SBaolin Wang 
1591c3d7b03SBaolin Wang 	val &= SC2731_CUR_MASK;
1601c3d7b03SBaolin Wang 	*cur = val * SC2731_CURRENT_STEP + SC2731_CURRENT_PRECHG;
1611c3d7b03SBaolin Wang 
1621c3d7b03SBaolin Wang 	return 0;
1631c3d7b03SBaolin Wang }
1641c3d7b03SBaolin Wang 
sc2731_charger_get_current_limit(struct sc2731_charger_info * info,u32 * cur)1651c3d7b03SBaolin Wang static int sc2731_charger_get_current_limit(struct sc2731_charger_info *info,
1661c3d7b03SBaolin Wang 					    u32 *cur)
1671c3d7b03SBaolin Wang {
1681c3d7b03SBaolin Wang 	int ret;
1691c3d7b03SBaolin Wang 	u32 val;
1701c3d7b03SBaolin Wang 
1711c3d7b03SBaolin Wang 	ret = regmap_read(info->regmap, info->base + SC2731_CHG_CFG5, &val);
1721c3d7b03SBaolin Wang 	if (ret)
1731c3d7b03SBaolin Wang 		return ret;
1741c3d7b03SBaolin Wang 
1751c3d7b03SBaolin Wang 	val = (val & SC2731_CUR_LIMIT_MASK) >> SC2731_CUR_LIMIT_SHIFT;
1761c3d7b03SBaolin Wang 
1771c3d7b03SBaolin Wang 	switch (val) {
1781c3d7b03SBaolin Wang 	case 0:
1791c3d7b03SBaolin Wang 		*cur = SC2731_CURRENT_LIMIT_100;
1801c3d7b03SBaolin Wang 		break;
1811c3d7b03SBaolin Wang 
1821c3d7b03SBaolin Wang 	case 1:
1831c3d7b03SBaolin Wang 		*cur = SC2731_CURRENT_LIMIT_2000;
1841c3d7b03SBaolin Wang 		break;
1851c3d7b03SBaolin Wang 
1861c3d7b03SBaolin Wang 	case 2:
1871c3d7b03SBaolin Wang 		*cur = SC2731_CURRENT_LIMIT_900;
1881c3d7b03SBaolin Wang 		break;
1891c3d7b03SBaolin Wang 
1901c3d7b03SBaolin Wang 	case 3:
1911c3d7b03SBaolin Wang 		*cur = SC2731_CURRENT_LIMIT_500;
1921c3d7b03SBaolin Wang 		break;
1931c3d7b03SBaolin Wang 
1941c3d7b03SBaolin Wang 	default:
1951c3d7b03SBaolin Wang 		return -EINVAL;
1961c3d7b03SBaolin Wang 	}
1971c3d7b03SBaolin Wang 
1981c3d7b03SBaolin Wang 	return 0;
1991c3d7b03SBaolin Wang }
2001c3d7b03SBaolin Wang 
2011c3d7b03SBaolin Wang static int
sc2731_charger_usb_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)2021c3d7b03SBaolin Wang sc2731_charger_usb_set_property(struct power_supply *psy,
2031c3d7b03SBaolin Wang 				enum power_supply_property psp,
2041c3d7b03SBaolin Wang 				const union power_supply_propval *val)
2051c3d7b03SBaolin Wang {
2061c3d7b03SBaolin Wang 	struct sc2731_charger_info *info = power_supply_get_drvdata(psy);
2071c3d7b03SBaolin Wang 	int ret;
2081c3d7b03SBaolin Wang 
2091c3d7b03SBaolin Wang 	mutex_lock(&info->lock);
2101c3d7b03SBaolin Wang 
2111c3d7b03SBaolin Wang 	if (!info->charging) {
2121c3d7b03SBaolin Wang 		mutex_unlock(&info->lock);
2131c3d7b03SBaolin Wang 		return -ENODEV;
2141c3d7b03SBaolin Wang 	}
2151c3d7b03SBaolin Wang 
2161c3d7b03SBaolin Wang 	switch (psp) {
2171c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
2181c3d7b03SBaolin Wang 		ret = sc2731_charger_set_current(info, val->intval / 1000);
2191c3d7b03SBaolin Wang 		if (ret < 0)
2201c3d7b03SBaolin Wang 			dev_err(info->dev, "set charge current failed\n");
2211c3d7b03SBaolin Wang 		break;
2221c3d7b03SBaolin Wang 
2231c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
2241c3d7b03SBaolin Wang 		ret = sc2731_charger_set_current_limit(info,
2251c3d7b03SBaolin Wang 						       val->intval / 1000);
2261c3d7b03SBaolin Wang 		if (ret < 0)
2271c3d7b03SBaolin Wang 			dev_err(info->dev, "set input current limit failed\n");
2281c3d7b03SBaolin Wang 		break;
2291c3d7b03SBaolin Wang 
2301c3d7b03SBaolin Wang 	default:
2311c3d7b03SBaolin Wang 		ret = -EINVAL;
2321c3d7b03SBaolin Wang 	}
2331c3d7b03SBaolin Wang 
2341c3d7b03SBaolin Wang 	mutex_unlock(&info->lock);
2351c3d7b03SBaolin Wang 	return ret;
2361c3d7b03SBaolin Wang }
2371c3d7b03SBaolin Wang 
sc2731_charger_usb_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)2381c3d7b03SBaolin Wang static int sc2731_charger_usb_get_property(struct power_supply *psy,
2391c3d7b03SBaolin Wang 					   enum power_supply_property psp,
2401c3d7b03SBaolin Wang 					   union power_supply_propval *val)
2411c3d7b03SBaolin Wang {
2421c3d7b03SBaolin Wang 	struct sc2731_charger_info *info = power_supply_get_drvdata(psy);
2431c3d7b03SBaolin Wang 	int ret = 0;
2441c3d7b03SBaolin Wang 	u32 cur;
2451c3d7b03SBaolin Wang 
2461c3d7b03SBaolin Wang 	mutex_lock(&info->lock);
2471c3d7b03SBaolin Wang 
2481c3d7b03SBaolin Wang 	switch (psp) {
2491c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
2501c3d7b03SBaolin Wang 		if (info->charging)
2511c3d7b03SBaolin Wang 			val->intval = sc2731_charger_get_status(info);
2521c3d7b03SBaolin Wang 		else
2531c3d7b03SBaolin Wang 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
2541c3d7b03SBaolin Wang 		break;
2551c3d7b03SBaolin Wang 
2561c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
2571c3d7b03SBaolin Wang 		if (!info->charging) {
2581c3d7b03SBaolin Wang 			val->intval = 0;
2591c3d7b03SBaolin Wang 		} else {
2601c3d7b03SBaolin Wang 			ret = sc2731_charger_get_current(info, &cur);
2611c3d7b03SBaolin Wang 			if (ret)
2621c3d7b03SBaolin Wang 				goto out;
2631c3d7b03SBaolin Wang 
2641c3d7b03SBaolin Wang 			val->intval = cur * 1000;
2651c3d7b03SBaolin Wang 		}
2661c3d7b03SBaolin Wang 		break;
2671c3d7b03SBaolin Wang 
2681c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
2691c3d7b03SBaolin Wang 		if (!info->charging) {
2701c3d7b03SBaolin Wang 			val->intval = 0;
2711c3d7b03SBaolin Wang 		} else {
2721c3d7b03SBaolin Wang 			ret = sc2731_charger_get_current_limit(info, &cur);
2731c3d7b03SBaolin Wang 			if (ret)
2741c3d7b03SBaolin Wang 				goto out;
2751c3d7b03SBaolin Wang 
2761c3d7b03SBaolin Wang 			val->intval = cur * 1000;
2771c3d7b03SBaolin Wang 		}
2781c3d7b03SBaolin Wang 		break;
2791c3d7b03SBaolin Wang 
2801c3d7b03SBaolin Wang 	default:
2811c3d7b03SBaolin Wang 		ret = -EINVAL;
2821c3d7b03SBaolin Wang 	}
2831c3d7b03SBaolin Wang 
2841c3d7b03SBaolin Wang out:
2851c3d7b03SBaolin Wang 	mutex_unlock(&info->lock);
2861c3d7b03SBaolin Wang 	return ret;
2871c3d7b03SBaolin Wang }
2881c3d7b03SBaolin Wang 
sc2731_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)2891c3d7b03SBaolin Wang static int sc2731_charger_property_is_writeable(struct power_supply *psy,
2901c3d7b03SBaolin Wang 						enum power_supply_property psp)
2911c3d7b03SBaolin Wang {
2921c3d7b03SBaolin Wang 	int ret;
2931c3d7b03SBaolin Wang 
2941c3d7b03SBaolin Wang 	switch (psp) {
2951c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
2961c3d7b03SBaolin Wang 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
2971c3d7b03SBaolin Wang 		ret = 1;
2981c3d7b03SBaolin Wang 		break;
2991c3d7b03SBaolin Wang 
3001c3d7b03SBaolin Wang 	default:
3011c3d7b03SBaolin Wang 		ret = 0;
3021c3d7b03SBaolin Wang 	}
3031c3d7b03SBaolin Wang 
3041c3d7b03SBaolin Wang 	return ret;
3051c3d7b03SBaolin Wang }
3061c3d7b03SBaolin Wang 
3071c3d7b03SBaolin Wang static enum power_supply_property sc2731_usb_props[] = {
3081c3d7b03SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
3091c3d7b03SBaolin Wang 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
3101c3d7b03SBaolin Wang 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
3111c3d7b03SBaolin Wang };
3121c3d7b03SBaolin Wang 
3131c3d7b03SBaolin Wang static const struct power_supply_desc sc2731_charger_desc = {
3141c3d7b03SBaolin Wang 	.name			= "sc2731_charger",
3151c3d7b03SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_USB,
3161c3d7b03SBaolin Wang 	.properties		= sc2731_usb_props,
3171c3d7b03SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc2731_usb_props),
3181c3d7b03SBaolin Wang 	.get_property		= sc2731_charger_usb_get_property,
3191c3d7b03SBaolin Wang 	.set_property		= sc2731_charger_usb_set_property,
3201c3d7b03SBaolin Wang 	.property_is_writeable	= sc2731_charger_property_is_writeable,
3211c3d7b03SBaolin Wang };
3221c3d7b03SBaolin Wang 
sc2731_charger_work(struct work_struct * data)3238ac1091eSBaolin Wang static void sc2731_charger_work(struct work_struct *data)
3241c3d7b03SBaolin Wang {
3251c3d7b03SBaolin Wang 	struct sc2731_charger_info *info =
3268ac1091eSBaolin Wang 		container_of(data, struct sc2731_charger_info, work);
3278ac1091eSBaolin Wang 	int ret;
3281c3d7b03SBaolin Wang 
3291c3d7b03SBaolin Wang 	mutex_lock(&info->lock);
3301c3d7b03SBaolin Wang 
331a8aaff6bSBaolin Wang 	if (info->limit > 0 && !info->charging) {
3321c3d7b03SBaolin Wang 		/* set current limitation and start to charge */
3338ac1091eSBaolin Wang 		ret = sc2731_charger_set_current_limit(info, info->limit);
3341c3d7b03SBaolin Wang 		if (ret)
3351c3d7b03SBaolin Wang 			goto out;
3361c3d7b03SBaolin Wang 
3378ac1091eSBaolin Wang 		ret = sc2731_charger_set_current(info, info->limit);
3381c3d7b03SBaolin Wang 		if (ret)
3391c3d7b03SBaolin Wang 			goto out;
3401c3d7b03SBaolin Wang 
3411c3d7b03SBaolin Wang 		ret = sc2731_charger_start_charge(info);
3421c3d7b03SBaolin Wang 		if (ret)
3431c3d7b03SBaolin Wang 			goto out;
3441c3d7b03SBaolin Wang 
3451c3d7b03SBaolin Wang 		info->charging = true;
346a8aaff6bSBaolin Wang 	} else if (!info->limit && info->charging) {
3471c3d7b03SBaolin Wang 		/* Stop charging */
3481c3d7b03SBaolin Wang 		info->charging = false;
3491c3d7b03SBaolin Wang 		sc2731_charger_stop_charge(info);
3501c3d7b03SBaolin Wang 	}
3511c3d7b03SBaolin Wang 
3521c3d7b03SBaolin Wang out:
3531c3d7b03SBaolin Wang 	mutex_unlock(&info->lock);
3548ac1091eSBaolin Wang }
3558ac1091eSBaolin Wang 
sc2731_charger_usb_change(struct notifier_block * nb,unsigned long limit,void * data)3568ac1091eSBaolin Wang static int sc2731_charger_usb_change(struct notifier_block *nb,
3578ac1091eSBaolin Wang 				     unsigned long limit, void *data)
3588ac1091eSBaolin Wang {
3598ac1091eSBaolin Wang 	struct sc2731_charger_info *info =
3608ac1091eSBaolin Wang 		container_of(nb, struct sc2731_charger_info, usb_notify);
3618ac1091eSBaolin Wang 
3628ac1091eSBaolin Wang 	info->limit = limit;
3638ac1091eSBaolin Wang 
3648ac1091eSBaolin Wang 	schedule_work(&info->work);
3658ac1091eSBaolin Wang 
3668ac1091eSBaolin Wang 	return NOTIFY_OK;
3671c3d7b03SBaolin Wang }
3681c3d7b03SBaolin Wang 
sc2731_charger_hw_init(struct sc2731_charger_info * info)3691c3d7b03SBaolin Wang static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
3701c3d7b03SBaolin Wang {
371*25fd3303SLinus Walleij 	struct power_supply_battery_info *bat_info;
3721c3d7b03SBaolin Wang 	u32 term_currrent, term_voltage, cur_val, vol_val;
3731c3d7b03SBaolin Wang 	int ret;
3741c3d7b03SBaolin Wang 
3751c3d7b03SBaolin Wang 	/* Enable charger module */
3761c3d7b03SBaolin Wang 	ret = regmap_update_bits(info->regmap, SC2731_MODULE_EN1,
3771c3d7b03SBaolin Wang 				 SC2731_CHARGE_EN, SC2731_CHARGE_EN);
3781c3d7b03SBaolin Wang 	if (ret)
3791c3d7b03SBaolin Wang 		return ret;
3801c3d7b03SBaolin Wang 
3811c3d7b03SBaolin Wang 	ret = power_supply_get_battery_info(info->psy_usb, &bat_info);
3821c3d7b03SBaolin Wang 	if (ret) {
3831c3d7b03SBaolin Wang 		dev_warn(info->dev, "no battery information is supplied\n");
3841c3d7b03SBaolin Wang 
3851c3d7b03SBaolin Wang 		/*
3861c3d7b03SBaolin Wang 		 * If no battery information is supplied, we should set
3871c3d7b03SBaolin Wang 		 * default charge termination current to 120 mA, and default
3881c3d7b03SBaolin Wang 		 * charge termination voltage to 4.35V.
3891c3d7b03SBaolin Wang 		 */
3901c3d7b03SBaolin Wang 		cur_val = 0x2;
3911c3d7b03SBaolin Wang 		vol_val = 0x1;
3921c3d7b03SBaolin Wang 	} else {
393*25fd3303SLinus Walleij 		term_currrent = bat_info->charge_term_current_ua / 1000;
3941c3d7b03SBaolin Wang 
3951c3d7b03SBaolin Wang 		if (term_currrent <= 90)
3961c3d7b03SBaolin Wang 			cur_val = 0;
3971c3d7b03SBaolin Wang 		else if (term_currrent >= 265)
3981c3d7b03SBaolin Wang 			cur_val = 0x7;
3991c3d7b03SBaolin Wang 		else
4001c3d7b03SBaolin Wang 			cur_val = ((term_currrent - 90) / 25) + 1;
4011c3d7b03SBaolin Wang 
402*25fd3303SLinus Walleij 		term_voltage = bat_info->constant_charge_voltage_max_uv / 1000;
4031c3d7b03SBaolin Wang 
4041c3d7b03SBaolin Wang 		if (term_voltage > 4500)
4051c3d7b03SBaolin Wang 			term_voltage = 4500;
4061c3d7b03SBaolin Wang 
4071c3d7b03SBaolin Wang 		if (term_voltage > 4200)
4081c3d7b03SBaolin Wang 			vol_val = (term_voltage - 4200) / 100;
4091c3d7b03SBaolin Wang 		else
4101c3d7b03SBaolin Wang 			vol_val = 0;
411872bcf83SBaolin Wang 
412*25fd3303SLinus Walleij 		power_supply_put_battery_info(info->psy_usb, bat_info);
4131c3d7b03SBaolin Wang 	}
4141c3d7b03SBaolin Wang 
4151c3d7b03SBaolin Wang 	/* Set charge termination current */
4161c3d7b03SBaolin Wang 	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG2,
4171c3d7b03SBaolin Wang 				 SC2731_TERMINATION_CUR_MASK, cur_val);
4181c3d7b03SBaolin Wang 	if (ret)
4191c3d7b03SBaolin Wang 		goto error;
4201c3d7b03SBaolin Wang 
4211c3d7b03SBaolin Wang 	/* Set charge termination voltage */
4221c3d7b03SBaolin Wang 	ret = regmap_update_bits(info->regmap, info->base + SC2731_CHG_CFG0,
4231c3d7b03SBaolin Wang 				 SC2731_TERMINATION_VOL_MASK |
4241c3d7b03SBaolin Wang 				 SC2731_TERMINATION_VOL_CAL_MASK,
4251c3d7b03SBaolin Wang 				 (vol_val << SC2731_TERMINATION_VOL_SHIFT) |
4261c3d7b03SBaolin Wang 				 (0x6 << SC2731_TERMINATION_VOL_CAL_SHIFT));
4271c3d7b03SBaolin Wang 	if (ret)
4281c3d7b03SBaolin Wang 		goto error;
4291c3d7b03SBaolin Wang 
4301c3d7b03SBaolin Wang 	return 0;
4311c3d7b03SBaolin Wang 
4321c3d7b03SBaolin Wang error:
4331c3d7b03SBaolin Wang 	regmap_update_bits(info->regmap, SC2731_MODULE_EN1, SC2731_CHARGE_EN, 0);
4341c3d7b03SBaolin Wang 	return ret;
4351c3d7b03SBaolin Wang }
4361c3d7b03SBaolin Wang 
sc2731_charger_detect_status(struct sc2731_charger_info * info)43718c807dbSBaolin Wang static void sc2731_charger_detect_status(struct sc2731_charger_info *info)
43818c807dbSBaolin Wang {
43918c807dbSBaolin Wang 	unsigned int min, max;
44018c807dbSBaolin Wang 
44118c807dbSBaolin Wang 	/*
44218c807dbSBaolin Wang 	 * If the USB charger status has been USB_CHARGER_PRESENT before
44318c807dbSBaolin Wang 	 * registering the notifier, we should start to charge with getting
44418c807dbSBaolin Wang 	 * the charge current.
44518c807dbSBaolin Wang 	 */
44618c807dbSBaolin Wang 	if (info->usb_phy->chg_state != USB_CHARGER_PRESENT)
44718c807dbSBaolin Wang 		return;
44818c807dbSBaolin Wang 
44918c807dbSBaolin Wang 	usb_phy_get_charger_current(info->usb_phy, &min, &max);
45018c807dbSBaolin Wang 	info->limit = min;
45118c807dbSBaolin Wang 
45218c807dbSBaolin Wang 	schedule_work(&info->work);
45318c807dbSBaolin Wang }
45418c807dbSBaolin Wang 
sc2731_charger_probe(struct platform_device * pdev)4551c3d7b03SBaolin Wang static int sc2731_charger_probe(struct platform_device *pdev)
4561c3d7b03SBaolin Wang {
4571c3d7b03SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
4581c3d7b03SBaolin Wang 	struct sc2731_charger_info *info;
4591c3d7b03SBaolin Wang 	struct power_supply_config charger_cfg = { };
4601c3d7b03SBaolin Wang 	int ret;
4611c3d7b03SBaolin Wang 
4621c3d7b03SBaolin Wang 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
4631c3d7b03SBaolin Wang 	if (!info)
4641c3d7b03SBaolin Wang 		return -ENOMEM;
4651c3d7b03SBaolin Wang 
4661c3d7b03SBaolin Wang 	mutex_init(&info->lock);
4671c3d7b03SBaolin Wang 	info->dev = &pdev->dev;
4688ac1091eSBaolin Wang 	INIT_WORK(&info->work, sc2731_charger_work);
4691c3d7b03SBaolin Wang 
4701c3d7b03SBaolin Wang 	info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
4711c3d7b03SBaolin Wang 	if (!info->regmap) {
4721c3d7b03SBaolin Wang 		dev_err(&pdev->dev, "failed to get charger regmap\n");
4731c3d7b03SBaolin Wang 		return -ENODEV;
4741c3d7b03SBaolin Wang 	}
4751c3d7b03SBaolin Wang 
4761c3d7b03SBaolin Wang 	ret = of_property_read_u32(np, "reg", &info->base);
4771c3d7b03SBaolin Wang 	if (ret) {
4781c3d7b03SBaolin Wang 		dev_err(&pdev->dev, "failed to get register address\n");
4791c3d7b03SBaolin Wang 		return -ENODEV;
4801c3d7b03SBaolin Wang 	}
4811c3d7b03SBaolin Wang 
4821c3d7b03SBaolin Wang 	charger_cfg.drv_data = info;
4831c3d7b03SBaolin Wang 	charger_cfg.of_node = np;
4841c3d7b03SBaolin Wang 	info->psy_usb = devm_power_supply_register(&pdev->dev,
4851c3d7b03SBaolin Wang 						   &sc2731_charger_desc,
4861c3d7b03SBaolin Wang 						   &charger_cfg);
4871c3d7b03SBaolin Wang 	if (IS_ERR(info->psy_usb)) {
4881c3d7b03SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
4891c3d7b03SBaolin Wang 		return PTR_ERR(info->psy_usb);
4901c3d7b03SBaolin Wang 	}
4911c3d7b03SBaolin Wang 
4921c3d7b03SBaolin Wang 	ret = sc2731_charger_hw_init(info);
4931c3d7b03SBaolin Wang 	if (ret)
4941c3d7b03SBaolin Wang 		return ret;
4951c3d7b03SBaolin Wang 
4961c3d7b03SBaolin Wang 	info->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0);
4971c3d7b03SBaolin Wang 	if (IS_ERR(info->usb_phy)) {
4981c3d7b03SBaolin Wang 		dev_err(&pdev->dev, "failed to find USB phy\n");
4991c3d7b03SBaolin Wang 		return PTR_ERR(info->usb_phy);
5001c3d7b03SBaolin Wang 	}
5011c3d7b03SBaolin Wang 
5021c3d7b03SBaolin Wang 	info->usb_notify.notifier_call = sc2731_charger_usb_change;
5031c3d7b03SBaolin Wang 	ret = usb_register_notifier(info->usb_phy, &info->usb_notify);
5041c3d7b03SBaolin Wang 	if (ret) {
5051c3d7b03SBaolin Wang 		dev_err(&pdev->dev, "failed to register notifier: %d\n", ret);
5061c3d7b03SBaolin Wang 		return ret;
5071c3d7b03SBaolin Wang 	}
5081c3d7b03SBaolin Wang 
50918c807dbSBaolin Wang 	sc2731_charger_detect_status(info);
51018c807dbSBaolin Wang 
5111c3d7b03SBaolin Wang 	return 0;
5121c3d7b03SBaolin Wang }
5131c3d7b03SBaolin Wang 
sc2731_charger_remove(struct platform_device * pdev)5141c3d7b03SBaolin Wang static int sc2731_charger_remove(struct platform_device *pdev)
5151c3d7b03SBaolin Wang {
5161c3d7b03SBaolin Wang 	struct sc2731_charger_info *info = platform_get_drvdata(pdev);
5171c3d7b03SBaolin Wang 
5181c3d7b03SBaolin Wang 	usb_unregister_notifier(info->usb_phy, &info->usb_notify);
5191c3d7b03SBaolin Wang 
5201c3d7b03SBaolin Wang 	return 0;
5211c3d7b03SBaolin Wang }
5221c3d7b03SBaolin Wang 
5231c3d7b03SBaolin Wang static const struct of_device_id sc2731_charger_of_match[] = {
5241c3d7b03SBaolin Wang 	{ .compatible = "sprd,sc2731-charger", },
5251c3d7b03SBaolin Wang 	{ }
5261c3d7b03SBaolin Wang };
5272aac79d1SZou Wei MODULE_DEVICE_TABLE(of, sc2731_charger_of_match);
5281c3d7b03SBaolin Wang 
5291c3d7b03SBaolin Wang static struct platform_driver sc2731_charger_driver = {
5301c3d7b03SBaolin Wang 	.driver = {
5311c3d7b03SBaolin Wang 		.name = "sc2731-charger",
5321c3d7b03SBaolin Wang 		.of_match_table = sc2731_charger_of_match,
5331c3d7b03SBaolin Wang 	},
5341c3d7b03SBaolin Wang 	.probe = sc2731_charger_probe,
5351c3d7b03SBaolin Wang 	.remove = sc2731_charger_remove,
5361c3d7b03SBaolin Wang };
5371c3d7b03SBaolin Wang 
5381c3d7b03SBaolin Wang module_platform_driver(sc2731_charger_driver);
5391c3d7b03SBaolin Wang 
5401c3d7b03SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC2731 Charger Driver");
5411c3d7b03SBaolin Wang MODULE_LICENSE("GPL v2");
542