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 {
37125fd3303SLinus 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 {
39325fd3303SLinus 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
40225fd3303SLinus 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
41225fd3303SLinus 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)514*0569d4cfSUwe Kleine-König static void 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
5211c3d7b03SBaolin Wang static const struct of_device_id sc2731_charger_of_match[] = {
5221c3d7b03SBaolin Wang { .compatible = "sprd,sc2731-charger", },
5231c3d7b03SBaolin Wang { }
5241c3d7b03SBaolin Wang };
5252aac79d1SZou Wei MODULE_DEVICE_TABLE(of, sc2731_charger_of_match);
5261c3d7b03SBaolin Wang
5271c3d7b03SBaolin Wang static struct platform_driver sc2731_charger_driver = {
5281c3d7b03SBaolin Wang .driver = {
5291c3d7b03SBaolin Wang .name = "sc2731-charger",
5301c3d7b03SBaolin Wang .of_match_table = sc2731_charger_of_match,
5311c3d7b03SBaolin Wang },
5321c3d7b03SBaolin Wang .probe = sc2731_charger_probe,
533*0569d4cfSUwe Kleine-König .remove_new = sc2731_charger_remove,
5341c3d7b03SBaolin Wang };
5351c3d7b03SBaolin Wang
5361c3d7b03SBaolin Wang module_platform_driver(sc2731_charger_driver);
5371c3d7b03SBaolin Wang
5381c3d7b03SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC2731 Charger Driver");
5391c3d7b03SBaolin Wang MODULE_LICENSE("GPL v2");
540