xref: /linux/drivers/power/supply/charger-manager.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
48c0984e5SSebastian Reichel  * MyungJoo Ham <myungjoo.ham@samsung.com>
58c0984e5SSebastian Reichel  *
68c0984e5SSebastian Reichel  * This driver enables to monitor battery health and control charger
78c0984e5SSebastian Reichel  * during suspend-to-mem.
802276af2SKrzysztof Kozlowski  * Charger manager depends on other devices. Register this later than
98c0984e5SSebastian Reichel  * the depending devices.
108c0984e5SSebastian Reichel  *
118c0984e5SSebastian Reichel **/
128c0984e5SSebastian Reichel 
138c0984e5SSebastian Reichel #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
148c0984e5SSebastian Reichel 
158c0984e5SSebastian Reichel #include <linux/io.h>
168c0984e5SSebastian Reichel #include <linux/module.h>
178c0984e5SSebastian Reichel #include <linux/irq.h>
188c0984e5SSebastian Reichel #include <linux/interrupt.h>
198c0984e5SSebastian Reichel #include <linux/rtc.h>
208c0984e5SSebastian Reichel #include <linux/slab.h>
218c0984e5SSebastian Reichel #include <linux/workqueue.h>
228c0984e5SSebastian Reichel #include <linux/platform_device.h>
238c0984e5SSebastian Reichel #include <linux/power/charger-manager.h>
248c0984e5SSebastian Reichel #include <linux/regulator/consumer.h>
258c0984e5SSebastian Reichel #include <linux/sysfs.h>
268c0984e5SSebastian Reichel #include <linux/of.h>
278c0984e5SSebastian Reichel #include <linux/thermal.h>
288c0984e5SSebastian Reichel 
29c1f73028SJonathan Bakker static struct {
30c1f73028SJonathan Bakker 	const char *name;
31c1f73028SJonathan Bakker 	u64 extcon_type;
32c1f73028SJonathan Bakker } extcon_mapping[] = {
33c1f73028SJonathan Bakker 	/* Current textual representations */
34c1f73028SJonathan Bakker 	{ "USB", EXTCON_USB },
35c1f73028SJonathan Bakker 	{ "USB-HOST", EXTCON_USB_HOST },
36c1f73028SJonathan Bakker 	{ "SDP", EXTCON_CHG_USB_SDP },
37c1f73028SJonathan Bakker 	{ "DCP", EXTCON_CHG_USB_DCP },
38c1f73028SJonathan Bakker 	{ "CDP", EXTCON_CHG_USB_CDP },
39c1f73028SJonathan Bakker 	{ "ACA", EXTCON_CHG_USB_ACA },
40c1f73028SJonathan Bakker 	{ "FAST-CHARGER", EXTCON_CHG_USB_FAST },
41c1f73028SJonathan Bakker 	{ "SLOW-CHARGER", EXTCON_CHG_USB_SLOW },
42c1f73028SJonathan Bakker 	{ "WPT", EXTCON_CHG_WPT },
43c1f73028SJonathan Bakker 	{ "PD", EXTCON_CHG_USB_PD },
44c1f73028SJonathan Bakker 	{ "DOCK", EXTCON_DOCK },
45c1f73028SJonathan Bakker 	{ "JIG", EXTCON_JIG },
46c1f73028SJonathan Bakker 	{ "MECHANICAL", EXTCON_MECHANICAL },
47c1f73028SJonathan Bakker 	/* Deprecated textual representations */
48c1f73028SJonathan Bakker 	{ "TA", EXTCON_CHG_USB_SDP },
49c1f73028SJonathan Bakker 	{ "CHARGE-DOWNSTREAM", EXTCON_CHG_USB_CDP },
50c1f73028SJonathan Bakker };
51c1f73028SJonathan Bakker 
528c0984e5SSebastian Reichel /*
5302276af2SKrzysztof Kozlowski  * Default temperature threshold for charging.
548c0984e5SSebastian Reichel  * Every temperature units are in tenth of centigrade.
558c0984e5SSebastian Reichel  */
568c0984e5SSebastian Reichel #define CM_DEFAULT_RECHARGE_TEMP_DIFF	50
578c0984e5SSebastian Reichel #define CM_DEFAULT_CHARGE_TEMP_MAX	500
588c0984e5SSebastian Reichel 
598c0984e5SSebastian Reichel /*
608c0984e5SSebastian Reichel  * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
618c0984e5SSebastian Reichel  * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
628c0984e5SSebastian Reichel  * without any delays.
638c0984e5SSebastian Reichel  */
648c0984e5SSebastian Reichel #define	CM_JIFFIES_SMALL	(2)
658c0984e5SSebastian Reichel 
668c0984e5SSebastian Reichel /* If y is valid (> 0) and smaller than x, do x = y */
678c0984e5SSebastian Reichel #define CM_MIN_VALID(x, y)	x = (((y > 0) && ((x) > (y))) ? (y) : (x))
688c0984e5SSebastian Reichel 
698c0984e5SSebastian Reichel /*
708c0984e5SSebastian Reichel  * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
718c0984e5SSebastian Reichel  * rtc alarm. It should be 2 or larger
728c0984e5SSebastian Reichel  */
738c0984e5SSebastian Reichel #define CM_RTC_SMALL		(2)
748c0984e5SSebastian Reichel 
758c0984e5SSebastian Reichel static LIST_HEAD(cm_list);
768c0984e5SSebastian Reichel static DEFINE_MUTEX(cm_list_mtx);
778c0984e5SSebastian Reichel 
788c0984e5SSebastian Reichel /* About in-suspend (suspend-again) monitoring */
798c0984e5SSebastian Reichel static struct alarm *cm_timer;
808c0984e5SSebastian Reichel 
818c0984e5SSebastian Reichel static bool cm_suspended;
828c0984e5SSebastian Reichel static bool cm_timer_set;
838c0984e5SSebastian Reichel static unsigned long cm_suspend_duration_ms;
848c0984e5SSebastian Reichel 
858c0984e5SSebastian Reichel /* About normal (not suspended) monitoring */
868c0984e5SSebastian Reichel static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
878c0984e5SSebastian Reichel static unsigned long next_polling; /* Next appointed polling time */
888c0984e5SSebastian Reichel static struct workqueue_struct *cm_wq; /* init at driver add */
898c0984e5SSebastian Reichel static struct delayed_work cm_monitor_work; /* init at driver add */
908c0984e5SSebastian Reichel 
918c0984e5SSebastian Reichel /**
928c0984e5SSebastian Reichel  * is_batt_present - See if the battery presents in place.
938c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
948c0984e5SSebastian Reichel  */
is_batt_present(struct charger_manager * cm)958c0984e5SSebastian Reichel static bool is_batt_present(struct charger_manager *cm)
968c0984e5SSebastian Reichel {
978c0984e5SSebastian Reichel 	union power_supply_propval val;
988c0984e5SSebastian Reichel 	struct power_supply *psy;
998c0984e5SSebastian Reichel 	bool present = false;
1008c0984e5SSebastian Reichel 	int i, ret;
1018c0984e5SSebastian Reichel 
1028c0984e5SSebastian Reichel 	switch (cm->desc->battery_present) {
1038c0984e5SSebastian Reichel 	case CM_BATTERY_PRESENT:
1048c0984e5SSebastian Reichel 		present = true;
1058c0984e5SSebastian Reichel 		break;
1068c0984e5SSebastian Reichel 	case CM_NO_BATTERY:
1078c0984e5SSebastian Reichel 		break;
1088c0984e5SSebastian Reichel 	case CM_FUEL_GAUGE:
1098c0984e5SSebastian Reichel 		psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
1108c0984e5SSebastian Reichel 		if (!psy)
1118c0984e5SSebastian Reichel 			break;
1128c0984e5SSebastian Reichel 
1138c0984e5SSebastian Reichel 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
1148c0984e5SSebastian Reichel 				&val);
1158c0984e5SSebastian Reichel 		if (ret == 0 && val.intval)
1168c0984e5SSebastian Reichel 			present = true;
1178c0984e5SSebastian Reichel 		power_supply_put(psy);
1188c0984e5SSebastian Reichel 		break;
1198c0984e5SSebastian Reichel 	case CM_CHARGER_STAT:
1208c0984e5SSebastian Reichel 		for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
1218c0984e5SSebastian Reichel 			psy = power_supply_get_by_name(
1228c0984e5SSebastian Reichel 					cm->desc->psy_charger_stat[i]);
1238c0984e5SSebastian Reichel 			if (!psy) {
1248c0984e5SSebastian Reichel 				dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
1258c0984e5SSebastian Reichel 					cm->desc->psy_charger_stat[i]);
1268c0984e5SSebastian Reichel 				continue;
1278c0984e5SSebastian Reichel 			}
1288c0984e5SSebastian Reichel 
1298c0984e5SSebastian Reichel 			ret = power_supply_get_property(psy,
1308c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_PRESENT, &val);
1318c0984e5SSebastian Reichel 			power_supply_put(psy);
1328c0984e5SSebastian Reichel 			if (ret == 0 && val.intval) {
1338c0984e5SSebastian Reichel 				present = true;
1348c0984e5SSebastian Reichel 				break;
1358c0984e5SSebastian Reichel 			}
1368c0984e5SSebastian Reichel 		}
1378c0984e5SSebastian Reichel 		break;
1388c0984e5SSebastian Reichel 	}
1398c0984e5SSebastian Reichel 
1408c0984e5SSebastian Reichel 	return present;
1418c0984e5SSebastian Reichel }
1428c0984e5SSebastian Reichel 
1438c0984e5SSebastian Reichel /**
1448c0984e5SSebastian Reichel  * is_ext_pwr_online - See if an external power source is attached to charge
1458c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
1468c0984e5SSebastian Reichel  *
1478c0984e5SSebastian Reichel  * Returns true if at least one of the chargers of the battery has an external
1488c0984e5SSebastian Reichel  * power source attached to charge the battery regardless of whether it is
1498c0984e5SSebastian Reichel  * actually charging or not.
1508c0984e5SSebastian Reichel  */
is_ext_pwr_online(struct charger_manager * cm)1518c0984e5SSebastian Reichel static bool is_ext_pwr_online(struct charger_manager *cm)
1528c0984e5SSebastian Reichel {
1538c0984e5SSebastian Reichel 	union power_supply_propval val;
1548c0984e5SSebastian Reichel 	struct power_supply *psy;
1558c0984e5SSebastian Reichel 	bool online = false;
1568c0984e5SSebastian Reichel 	int i, ret;
1578c0984e5SSebastian Reichel 
1588c0984e5SSebastian Reichel 	/* If at least one of them has one, it's yes. */
1598c0984e5SSebastian Reichel 	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
1608c0984e5SSebastian Reichel 		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
1618c0984e5SSebastian Reichel 		if (!psy) {
1628c0984e5SSebastian Reichel 			dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
1638c0984e5SSebastian Reichel 					cm->desc->psy_charger_stat[i]);
1648c0984e5SSebastian Reichel 			continue;
1658c0984e5SSebastian Reichel 		}
1668c0984e5SSebastian Reichel 
1678c0984e5SSebastian Reichel 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
1688c0984e5SSebastian Reichel 				&val);
1698c0984e5SSebastian Reichel 		power_supply_put(psy);
1708c0984e5SSebastian Reichel 		if (ret == 0 && val.intval) {
1718c0984e5SSebastian Reichel 			online = true;
1728c0984e5SSebastian Reichel 			break;
1738c0984e5SSebastian Reichel 		}
1748c0984e5SSebastian Reichel 	}
1758c0984e5SSebastian Reichel 
1768c0984e5SSebastian Reichel 	return online;
1778c0984e5SSebastian Reichel }
1788c0984e5SSebastian Reichel 
1798c0984e5SSebastian Reichel /**
1808c0984e5SSebastian Reichel  * get_batt_uV - Get the voltage level of the battery
1818c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
1828c0984e5SSebastian Reichel  * @uV: the voltage level returned.
1838c0984e5SSebastian Reichel  *
1848c0984e5SSebastian Reichel  * Returns 0 if there is no error.
1858c0984e5SSebastian Reichel  * Returns a negative value on error.
1868c0984e5SSebastian Reichel  */
get_batt_uV(struct charger_manager * cm,int * uV)1878c0984e5SSebastian Reichel static int get_batt_uV(struct charger_manager *cm, int *uV)
1888c0984e5SSebastian Reichel {
1898c0984e5SSebastian Reichel 	union power_supply_propval val;
1908c0984e5SSebastian Reichel 	struct power_supply *fuel_gauge;
1918c0984e5SSebastian Reichel 	int ret;
1928c0984e5SSebastian Reichel 
1938c0984e5SSebastian Reichel 	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
1948c0984e5SSebastian Reichel 	if (!fuel_gauge)
1958c0984e5SSebastian Reichel 		return -ENODEV;
1968c0984e5SSebastian Reichel 
1978c0984e5SSebastian Reichel 	ret = power_supply_get_property(fuel_gauge,
1988c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
1998c0984e5SSebastian Reichel 	power_supply_put(fuel_gauge);
2008c0984e5SSebastian Reichel 	if (ret)
2018c0984e5SSebastian Reichel 		return ret;
2028c0984e5SSebastian Reichel 
2038c0984e5SSebastian Reichel 	*uV = val.intval;
2048c0984e5SSebastian Reichel 	return 0;
2058c0984e5SSebastian Reichel }
2068c0984e5SSebastian Reichel 
2078c0984e5SSebastian Reichel /**
2088c0984e5SSebastian Reichel  * is_charging - Returns true if the battery is being charged.
2098c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
2108c0984e5SSebastian Reichel  */
is_charging(struct charger_manager * cm)2118c0984e5SSebastian Reichel static bool is_charging(struct charger_manager *cm)
2128c0984e5SSebastian Reichel {
2138c0984e5SSebastian Reichel 	int i, ret;
2148c0984e5SSebastian Reichel 	bool charging = false;
2158c0984e5SSebastian Reichel 	struct power_supply *psy;
2168c0984e5SSebastian Reichel 	union power_supply_propval val;
2178c0984e5SSebastian Reichel 
2188c0984e5SSebastian Reichel 	/* If there is no battery, it cannot be charged */
2198c0984e5SSebastian Reichel 	if (!is_batt_present(cm))
2208c0984e5SSebastian Reichel 		return false;
2218c0984e5SSebastian Reichel 
2228c0984e5SSebastian Reichel 	/* If at least one of the charger is charging, return yes */
2238c0984e5SSebastian Reichel 	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
2248c0984e5SSebastian Reichel 		/* 1. The charger sholuld not be DISABLED */
2258c0984e5SSebastian Reichel 		if (cm->emergency_stop)
2268c0984e5SSebastian Reichel 			continue;
2278c0984e5SSebastian Reichel 		if (!cm->charger_enabled)
2288c0984e5SSebastian Reichel 			continue;
2298c0984e5SSebastian Reichel 
2308c0984e5SSebastian Reichel 		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
2318c0984e5SSebastian Reichel 		if (!psy) {
2328c0984e5SSebastian Reichel 			dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
2338c0984e5SSebastian Reichel 					cm->desc->psy_charger_stat[i]);
2348c0984e5SSebastian Reichel 			continue;
2358c0984e5SSebastian Reichel 		}
2368c0984e5SSebastian Reichel 
2378c0984e5SSebastian Reichel 		/* 2. The charger should be online (ext-power) */
2388c0984e5SSebastian Reichel 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
2398c0984e5SSebastian Reichel 				&val);
2408c0984e5SSebastian Reichel 		if (ret) {
2418c0984e5SSebastian Reichel 			dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
2428c0984e5SSebastian Reichel 				 cm->desc->psy_charger_stat[i]);
2438c0984e5SSebastian Reichel 			power_supply_put(psy);
2448c0984e5SSebastian Reichel 			continue;
2458c0984e5SSebastian Reichel 		}
2468c0984e5SSebastian Reichel 		if (val.intval == 0) {
2478c0984e5SSebastian Reichel 			power_supply_put(psy);
2488c0984e5SSebastian Reichel 			continue;
2498c0984e5SSebastian Reichel 		}
2508c0984e5SSebastian Reichel 
2518c0984e5SSebastian Reichel 		/*
2528c0984e5SSebastian Reichel 		 * 3. The charger should not be FULL, DISCHARGING,
2538c0984e5SSebastian Reichel 		 * or NOT_CHARGING.
2548c0984e5SSebastian Reichel 		 */
2558c0984e5SSebastian Reichel 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
2568c0984e5SSebastian Reichel 				&val);
2578c0984e5SSebastian Reichel 		power_supply_put(psy);
2588c0984e5SSebastian Reichel 		if (ret) {
2598c0984e5SSebastian Reichel 			dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
2608c0984e5SSebastian Reichel 				 cm->desc->psy_charger_stat[i]);
2618c0984e5SSebastian Reichel 			continue;
2628c0984e5SSebastian Reichel 		}
2638c0984e5SSebastian Reichel 		if (val.intval == POWER_SUPPLY_STATUS_FULL ||
2648c0984e5SSebastian Reichel 				val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
2658c0984e5SSebastian Reichel 				val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
2668c0984e5SSebastian Reichel 			continue;
2678c0984e5SSebastian Reichel 
2688c0984e5SSebastian Reichel 		/* Then, this is charging. */
2698c0984e5SSebastian Reichel 		charging = true;
2708c0984e5SSebastian Reichel 		break;
2718c0984e5SSebastian Reichel 	}
2728c0984e5SSebastian Reichel 
2738c0984e5SSebastian Reichel 	return charging;
2748c0984e5SSebastian Reichel }
2758c0984e5SSebastian Reichel 
2768c0984e5SSebastian Reichel /**
2778c0984e5SSebastian Reichel  * is_full_charged - Returns true if the battery is fully charged.
2788c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
2798c0984e5SSebastian Reichel  */
is_full_charged(struct charger_manager * cm)2808c0984e5SSebastian Reichel static bool is_full_charged(struct charger_manager *cm)
2818c0984e5SSebastian Reichel {
2828c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
2838c0984e5SSebastian Reichel 	union power_supply_propval val;
2848c0984e5SSebastian Reichel 	struct power_supply *fuel_gauge;
2858c0984e5SSebastian Reichel 	bool is_full = false;
2868c0984e5SSebastian Reichel 	int ret = 0;
2878c0984e5SSebastian Reichel 	int uV;
2888c0984e5SSebastian Reichel 
2898c0984e5SSebastian Reichel 	/* If there is no battery, it cannot be charged */
2908c0984e5SSebastian Reichel 	if (!is_batt_present(cm))
2918c0984e5SSebastian Reichel 		return false;
2928c0984e5SSebastian Reichel 
2938c0984e5SSebastian Reichel 	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
2948c0984e5SSebastian Reichel 	if (!fuel_gauge)
2958c0984e5SSebastian Reichel 		return false;
2968c0984e5SSebastian Reichel 
297e132fc6bSJonghwa Lee 	/* Full, if it's over the fullbatt voltage */
298e132fc6bSJonghwa Lee 	if (desc->fullbatt_uV > 0) {
299e132fc6bSJonghwa Lee 		ret = get_batt_uV(cm, &uV);
300e132fc6bSJonghwa Lee 		if (!ret) {
301e132fc6bSJonghwa Lee 			/* Battery is already full, checks voltage drop. */
302e132fc6bSJonghwa Lee 			if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
303e132fc6bSJonghwa Lee 					&& desc->fullbatt_vchkdrop_uV)
304e132fc6bSJonghwa Lee 				uV += desc->fullbatt_vchkdrop_uV;
305e132fc6bSJonghwa Lee 			if (uV >= desc->fullbatt_uV)
306e132fc6bSJonghwa Lee 				return true;
307e132fc6bSJonghwa Lee 		}
308e132fc6bSJonghwa Lee 	}
309e132fc6bSJonghwa Lee 
3108c0984e5SSebastian Reichel 	if (desc->fullbatt_full_capacity > 0) {
3118c0984e5SSebastian Reichel 		val.intval = 0;
3128c0984e5SSebastian Reichel 
3138c0984e5SSebastian Reichel 		/* Not full if capacity of fuel gauge isn't full */
3148c0984e5SSebastian Reichel 		ret = power_supply_get_property(fuel_gauge,
3158c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_CHARGE_FULL, &val);
3168c0984e5SSebastian Reichel 		if (!ret && val.intval > desc->fullbatt_full_capacity) {
3178c0984e5SSebastian Reichel 			is_full = true;
3188c0984e5SSebastian Reichel 			goto out;
3198c0984e5SSebastian Reichel 		}
3208c0984e5SSebastian Reichel 	}
3218c0984e5SSebastian Reichel 
3228c0984e5SSebastian Reichel 	/* Full, if the capacity is more than fullbatt_soc */
3238c0984e5SSebastian Reichel 	if (desc->fullbatt_soc > 0) {
3248c0984e5SSebastian Reichel 		val.intval = 0;
3258c0984e5SSebastian Reichel 
3268c0984e5SSebastian Reichel 		ret = power_supply_get_property(fuel_gauge,
3278c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_CAPACITY, &val);
3288c0984e5SSebastian Reichel 		if (!ret && val.intval >= desc->fullbatt_soc) {
3298c0984e5SSebastian Reichel 			is_full = true;
3308c0984e5SSebastian Reichel 			goto out;
3318c0984e5SSebastian Reichel 		}
3328c0984e5SSebastian Reichel 	}
3338c0984e5SSebastian Reichel 
3348c0984e5SSebastian Reichel out:
3358c0984e5SSebastian Reichel 	power_supply_put(fuel_gauge);
3368c0984e5SSebastian Reichel 	return is_full;
3378c0984e5SSebastian Reichel }
3388c0984e5SSebastian Reichel 
3398c0984e5SSebastian Reichel /**
3408c0984e5SSebastian Reichel  * is_polling_required - Return true if need to continue polling for this CM.
3418c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
3428c0984e5SSebastian Reichel  */
is_polling_required(struct charger_manager * cm)3438c0984e5SSebastian Reichel static bool is_polling_required(struct charger_manager *cm)
3448c0984e5SSebastian Reichel {
3458c0984e5SSebastian Reichel 	switch (cm->desc->polling_mode) {
3468c0984e5SSebastian Reichel 	case CM_POLL_DISABLE:
3478c0984e5SSebastian Reichel 		return false;
3488c0984e5SSebastian Reichel 	case CM_POLL_ALWAYS:
3498c0984e5SSebastian Reichel 		return true;
3508c0984e5SSebastian Reichel 	case CM_POLL_EXTERNAL_POWER_ONLY:
3518c0984e5SSebastian Reichel 		return is_ext_pwr_online(cm);
3528c0984e5SSebastian Reichel 	case CM_POLL_CHARGING_ONLY:
3538c0984e5SSebastian Reichel 		return is_charging(cm);
3548c0984e5SSebastian Reichel 	default:
3558c0984e5SSebastian Reichel 		dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
3568c0984e5SSebastian Reichel 			 cm->desc->polling_mode);
3578c0984e5SSebastian Reichel 	}
3588c0984e5SSebastian Reichel 
3598c0984e5SSebastian Reichel 	return false;
3608c0984e5SSebastian Reichel }
3618c0984e5SSebastian Reichel 
3628c0984e5SSebastian Reichel /**
3638c0984e5SSebastian Reichel  * try_charger_enable - Enable/Disable chargers altogether
3648c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
3658c0984e5SSebastian Reichel  * @enable: true: enable / false: disable
3668c0984e5SSebastian Reichel  *
3678c0984e5SSebastian Reichel  * Note that Charger Manager keeps the charger enabled regardless whether
3688c0984e5SSebastian Reichel  * the charger is charging or not (because battery is full or no external
3698c0984e5SSebastian Reichel  * power source exists) except when CM needs to disable chargers forcibly
37002276af2SKrzysztof Kozlowski  * because of emergency causes; when the battery is overheated or too cold.
3718c0984e5SSebastian Reichel  */
try_charger_enable(struct charger_manager * cm,bool enable)3728c0984e5SSebastian Reichel static int try_charger_enable(struct charger_manager *cm, bool enable)
3738c0984e5SSebastian Reichel {
3748c0984e5SSebastian Reichel 	int err = 0, i;
3758c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
3768c0984e5SSebastian Reichel 
3778c13b6f1SBaolin Wang 	/* Ignore if it's redundant command */
3788c0984e5SSebastian Reichel 	if (enable == cm->charger_enabled)
3798c0984e5SSebastian Reichel 		return 0;
3808c0984e5SSebastian Reichel 
3818c0984e5SSebastian Reichel 	if (enable) {
3828c0984e5SSebastian Reichel 		if (cm->emergency_stop)
3838c0984e5SSebastian Reichel 			return -EAGAIN;
3848c0984e5SSebastian Reichel 
3858c0984e5SSebastian Reichel 		/*
3868c0984e5SSebastian Reichel 		 * Save start time of charging to limit
3878c0984e5SSebastian Reichel 		 * maximum possible charging time.
3888c0984e5SSebastian Reichel 		 */
3898c0984e5SSebastian Reichel 		cm->charging_start_time = ktime_to_ms(ktime_get());
3908c0984e5SSebastian Reichel 		cm->charging_end_time = 0;
3918c0984e5SSebastian Reichel 
3928c0984e5SSebastian Reichel 		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
3938c0984e5SSebastian Reichel 			if (desc->charger_regulators[i].externally_control)
3948c0984e5SSebastian Reichel 				continue;
3958c0984e5SSebastian Reichel 
3968c0984e5SSebastian Reichel 			err = regulator_enable(desc->charger_regulators[i].consumer);
3978c0984e5SSebastian Reichel 			if (err < 0) {
3988c0984e5SSebastian Reichel 				dev_warn(cm->dev, "Cannot enable %s regulator\n",
3998c0984e5SSebastian Reichel 					 desc->charger_regulators[i].regulator_name);
4008c0984e5SSebastian Reichel 			}
4018c0984e5SSebastian Reichel 		}
4028c0984e5SSebastian Reichel 	} else {
4038c0984e5SSebastian Reichel 		/*
4048c0984e5SSebastian Reichel 		 * Save end time of charging to maintain fully charged state
4058c0984e5SSebastian Reichel 		 * of battery after full-batt.
4068c0984e5SSebastian Reichel 		 */
4078c0984e5SSebastian Reichel 		cm->charging_start_time = 0;
4088c0984e5SSebastian Reichel 		cm->charging_end_time = ktime_to_ms(ktime_get());
4098c0984e5SSebastian Reichel 
4108c0984e5SSebastian Reichel 		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
4118c0984e5SSebastian Reichel 			if (desc->charger_regulators[i].externally_control)
4128c0984e5SSebastian Reichel 				continue;
4138c0984e5SSebastian Reichel 
4148c0984e5SSebastian Reichel 			err = regulator_disable(desc->charger_regulators[i].consumer);
4158c0984e5SSebastian Reichel 			if (err < 0) {
4168c0984e5SSebastian Reichel 				dev_warn(cm->dev, "Cannot disable %s regulator\n",
4178c0984e5SSebastian Reichel 					 desc->charger_regulators[i].regulator_name);
4188c0984e5SSebastian Reichel 			}
4198c0984e5SSebastian Reichel 		}
4208c0984e5SSebastian Reichel 
4218c0984e5SSebastian Reichel 		/*
4228c0984e5SSebastian Reichel 		 * Abnormal battery state - Stop charging forcibly,
4238c0984e5SSebastian Reichel 		 * even if charger was enabled at the other places
4248c0984e5SSebastian Reichel 		 */
4258c0984e5SSebastian Reichel 		for (i = 0; i < desc->num_charger_regulators; i++) {
4268c0984e5SSebastian Reichel 			if (regulator_is_enabled(
4278c0984e5SSebastian Reichel 				    desc->charger_regulators[i].consumer)) {
4288c0984e5SSebastian Reichel 				regulator_force_disable(
4298c0984e5SSebastian Reichel 					desc->charger_regulators[i].consumer);
4308c0984e5SSebastian Reichel 				dev_warn(cm->dev, "Disable regulator(%s) forcibly\n",
4318c0984e5SSebastian Reichel 					 desc->charger_regulators[i].regulator_name);
4328c0984e5SSebastian Reichel 			}
4338c0984e5SSebastian Reichel 		}
4348c0984e5SSebastian Reichel 	}
4358c0984e5SSebastian Reichel 
436e132fc6bSJonghwa Lee 	if (!err)
4378c0984e5SSebastian Reichel 		cm->charger_enabled = enable;
4388c0984e5SSebastian Reichel 
4398c0984e5SSebastian Reichel 	return err;
4408c0984e5SSebastian Reichel }
4418c0984e5SSebastian Reichel 
4428c0984e5SSebastian Reichel /**
4438c0984e5SSebastian Reichel  * check_charging_duration - Monitor charging/discharging duration
4448c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
4458c0984e5SSebastian Reichel  *
4468c0984e5SSebastian Reichel  * If whole charging duration exceed 'charging_max_duration_ms',
4478c0984e5SSebastian Reichel  * cm stop charging to prevent overcharge/overheat. If discharging
4488c0984e5SSebastian Reichel  * duration exceed 'discharging _max_duration_ms', charger cable is
4498c0984e5SSebastian Reichel  * attached, after full-batt, cm start charging to maintain fully
4508c0984e5SSebastian Reichel  * charged state for battery.
4518c0984e5SSebastian Reichel  */
check_charging_duration(struct charger_manager * cm)4528c0984e5SSebastian Reichel static int check_charging_duration(struct charger_manager *cm)
4538c0984e5SSebastian Reichel {
4548c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
4558c0984e5SSebastian Reichel 	u64 curr = ktime_to_ms(ktime_get());
4568c0984e5SSebastian Reichel 	u64 duration;
4578c0984e5SSebastian Reichel 	int ret = false;
4588c0984e5SSebastian Reichel 
4598c0984e5SSebastian Reichel 	if (!desc->charging_max_duration_ms &&
4608c0984e5SSebastian Reichel 			!desc->discharging_max_duration_ms)
4618c0984e5SSebastian Reichel 		return ret;
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel 	if (cm->charger_enabled) {
4648c0984e5SSebastian Reichel 		duration = curr - cm->charging_start_time;
4658c0984e5SSebastian Reichel 
4668c0984e5SSebastian Reichel 		if (duration > desc->charging_max_duration_ms) {
4678c0984e5SSebastian Reichel 			dev_info(cm->dev, "Charging duration exceed %ums\n",
4688c0984e5SSebastian Reichel 				 desc->charging_max_duration_ms);
4698c0984e5SSebastian Reichel 			ret = true;
4708c0984e5SSebastian Reichel 		}
471e132fc6bSJonghwa Lee 	} else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
4728c0984e5SSebastian Reichel 		duration = curr - cm->charging_end_time;
4738c0984e5SSebastian Reichel 
47410a4357fSColin Ian King 		if (duration > desc->discharging_max_duration_ms) {
4758c0984e5SSebastian Reichel 			dev_info(cm->dev, "Discharging duration exceed %ums\n",
4768c0984e5SSebastian Reichel 				 desc->discharging_max_duration_ms);
4778c0984e5SSebastian Reichel 			ret = true;
4788c0984e5SSebastian Reichel 		}
4798c0984e5SSebastian Reichel 	}
4808c0984e5SSebastian Reichel 
4818c0984e5SSebastian Reichel 	return ret;
4828c0984e5SSebastian Reichel }
4838c0984e5SSebastian Reichel 
cm_get_battery_temperature_by_psy(struct charger_manager * cm,int * temp)4848c0984e5SSebastian Reichel static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
4858c0984e5SSebastian Reichel 					int *temp)
4868c0984e5SSebastian Reichel {
4878c0984e5SSebastian Reichel 	struct power_supply *fuel_gauge;
4888c0984e5SSebastian Reichel 	int ret;
4898c0984e5SSebastian Reichel 
4908c0984e5SSebastian Reichel 	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
4918c0984e5SSebastian Reichel 	if (!fuel_gauge)
4928c0984e5SSebastian Reichel 		return -ENODEV;
4938c0984e5SSebastian Reichel 
4948c0984e5SSebastian Reichel 	ret = power_supply_get_property(fuel_gauge,
4958c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_TEMP,
4968c0984e5SSebastian Reichel 				(union power_supply_propval *)temp);
4978c0984e5SSebastian Reichel 	power_supply_put(fuel_gauge);
4988c0984e5SSebastian Reichel 
4998c0984e5SSebastian Reichel 	return ret;
5008c0984e5SSebastian Reichel }
5018c0984e5SSebastian Reichel 
cm_get_battery_temperature(struct charger_manager * cm,int * temp)5028c0984e5SSebastian Reichel static int cm_get_battery_temperature(struct charger_manager *cm,
5038c0984e5SSebastian Reichel 					int *temp)
5048c0984e5SSebastian Reichel {
5058c0984e5SSebastian Reichel 	int ret;
5068c0984e5SSebastian Reichel 
5078c0984e5SSebastian Reichel 	if (!cm->desc->measure_battery_temp)
5088c0984e5SSebastian Reichel 		return -ENODEV;
5098c0984e5SSebastian Reichel 
5108c0984e5SSebastian Reichel #ifdef CONFIG_THERMAL
5118c0984e5SSebastian Reichel 	if (cm->tzd_batt) {
5128c0984e5SSebastian Reichel 		ret = thermal_zone_get_temp(cm->tzd_batt, temp);
5138c0984e5SSebastian Reichel 		if (!ret)
5148c0984e5SSebastian Reichel 			/* Calibrate temperature unit */
5158c0984e5SSebastian Reichel 			*temp /= 100;
5168c0984e5SSebastian Reichel 	} else
5178c0984e5SSebastian Reichel #endif
5188c0984e5SSebastian Reichel 	{
5198c0984e5SSebastian Reichel 		/* if-else continued from CONFIG_THERMAL */
5208c0984e5SSebastian Reichel 		ret = cm_get_battery_temperature_by_psy(cm, temp);
5218c0984e5SSebastian Reichel 	}
5228c0984e5SSebastian Reichel 
5238c0984e5SSebastian Reichel 	return ret;
5248c0984e5SSebastian Reichel }
5258c0984e5SSebastian Reichel 
cm_check_thermal_status(struct charger_manager * cm)5268c0984e5SSebastian Reichel static int cm_check_thermal_status(struct charger_manager *cm)
5278c0984e5SSebastian Reichel {
5288c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
5298c0984e5SSebastian Reichel 	int temp, upper_limit, lower_limit;
5308c0984e5SSebastian Reichel 	int ret = 0;
5318c0984e5SSebastian Reichel 
5328c0984e5SSebastian Reichel 	ret = cm_get_battery_temperature(cm, &temp);
5338c0984e5SSebastian Reichel 	if (ret) {
5348c0984e5SSebastian Reichel 		/* FIXME:
5358c0984e5SSebastian Reichel 		 * No information of battery temperature might
53602276af2SKrzysztof Kozlowski 		 * occur hazardous result. We have to handle it
5378c0984e5SSebastian Reichel 		 * depending on battery type.
5388c0984e5SSebastian Reichel 		 */
5398c0984e5SSebastian Reichel 		dev_err(cm->dev, "Failed to get battery temperature\n");
5408c0984e5SSebastian Reichel 		return 0;
5418c0984e5SSebastian Reichel 	}
5428c0984e5SSebastian Reichel 
5438c0984e5SSebastian Reichel 	upper_limit = desc->temp_max;
5448c0984e5SSebastian Reichel 	lower_limit = desc->temp_min;
5458c0984e5SSebastian Reichel 
5468c0984e5SSebastian Reichel 	if (cm->emergency_stop) {
5478c0984e5SSebastian Reichel 		upper_limit -= desc->temp_diff;
5488c0984e5SSebastian Reichel 		lower_limit += desc->temp_diff;
5498c0984e5SSebastian Reichel 	}
5508c0984e5SSebastian Reichel 
5518c0984e5SSebastian Reichel 	if (temp > upper_limit)
5529584051fSJonghwa Lee 		ret = CM_BATT_OVERHEAT;
5538c0984e5SSebastian Reichel 	else if (temp < lower_limit)
5549584051fSJonghwa Lee 		ret = CM_BATT_COLD;
5559584051fSJonghwa Lee 	else
5569584051fSJonghwa Lee 		ret = CM_BATT_OK;
5578c0984e5SSebastian Reichel 
558e132fc6bSJonghwa Lee 	cm->emergency_stop = ret;
559e132fc6bSJonghwa Lee 
5608c0984e5SSebastian Reichel 	return ret;
5618c0984e5SSebastian Reichel }
5628c0984e5SSebastian Reichel 
5638c0984e5SSebastian Reichel /**
564e132fc6bSJonghwa Lee  * cm_get_target_status - Check current status and get next target status.
565e132fc6bSJonghwa Lee  * @cm: the Charger Manager representing the battery.
566e132fc6bSJonghwa Lee  */
cm_get_target_status(struct charger_manager * cm)567e132fc6bSJonghwa Lee static int cm_get_target_status(struct charger_manager *cm)
568e132fc6bSJonghwa Lee {
569e132fc6bSJonghwa Lee 	if (!is_ext_pwr_online(cm))
570e132fc6bSJonghwa Lee 		return POWER_SUPPLY_STATUS_DISCHARGING;
571e132fc6bSJonghwa Lee 
572e132fc6bSJonghwa Lee 	if (cm_check_thermal_status(cm)) {
5732a0aa0faSJunlin Yang 		/* Check if discharging duration exceeds limit. */
574e132fc6bSJonghwa Lee 		if (check_charging_duration(cm))
575e132fc6bSJonghwa Lee 			goto charging_ok;
576e132fc6bSJonghwa Lee 		return POWER_SUPPLY_STATUS_NOT_CHARGING;
577e132fc6bSJonghwa Lee 	}
578e132fc6bSJonghwa Lee 
579e132fc6bSJonghwa Lee 	switch (cm->battery_status) {
580e132fc6bSJonghwa Lee 	case POWER_SUPPLY_STATUS_CHARGING:
5812a0aa0faSJunlin Yang 		/* Check if charging duration exceeds limit. */
582e132fc6bSJonghwa Lee 		if (check_charging_duration(cm))
583e132fc6bSJonghwa Lee 			return POWER_SUPPLY_STATUS_FULL;
584e132fc6bSJonghwa Lee 		fallthrough;
585e132fc6bSJonghwa Lee 	case POWER_SUPPLY_STATUS_FULL:
586e132fc6bSJonghwa Lee 		if (is_full_charged(cm))
587e132fc6bSJonghwa Lee 			return POWER_SUPPLY_STATUS_FULL;
588e132fc6bSJonghwa Lee 		fallthrough;
589e132fc6bSJonghwa Lee 	default:
590e132fc6bSJonghwa Lee 		break;
591e132fc6bSJonghwa Lee 	}
592e132fc6bSJonghwa Lee 
593e132fc6bSJonghwa Lee charging_ok:
594e132fc6bSJonghwa Lee 	/* Charging is allowed. */
595e132fc6bSJonghwa Lee 	return POWER_SUPPLY_STATUS_CHARGING;
596e132fc6bSJonghwa Lee }
597e132fc6bSJonghwa Lee 
598e132fc6bSJonghwa Lee /**
5998c0984e5SSebastian Reichel  * _cm_monitor - Monitor the temperature and return true for exceptions.
6008c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
6018c0984e5SSebastian Reichel  *
6028c0984e5SSebastian Reichel  * Returns true if there is an event to notify for the battery.
6038c0984e5SSebastian Reichel  * (True if the status of "emergency_stop" changes)
6048c0984e5SSebastian Reichel  */
_cm_monitor(struct charger_manager * cm)6058c0984e5SSebastian Reichel static bool _cm_monitor(struct charger_manager *cm)
6068c0984e5SSebastian Reichel {
607e132fc6bSJonghwa Lee 	int target;
6088c0984e5SSebastian Reichel 
609e132fc6bSJonghwa Lee 	target = cm_get_target_status(cm);
6108c0984e5SSebastian Reichel 
611e132fc6bSJonghwa Lee 	try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING));
6128c0984e5SSebastian Reichel 
613e132fc6bSJonghwa Lee 	if (cm->battery_status != target) {
614e132fc6bSJonghwa Lee 		cm->battery_status = target;
615e132fc6bSJonghwa Lee 		power_supply_changed(cm->charger_psy);
6168c0984e5SSebastian Reichel 	}
6178c0984e5SSebastian Reichel 
618e132fc6bSJonghwa Lee 	return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING);
6198c0984e5SSebastian Reichel }
6208c0984e5SSebastian Reichel 
6218c0984e5SSebastian Reichel /**
6228c0984e5SSebastian Reichel  * cm_monitor - Monitor every battery.
6238c0984e5SSebastian Reichel  *
6248c0984e5SSebastian Reichel  * Returns true if there is an event to notify from any of the batteries.
6258c0984e5SSebastian Reichel  * (True if the status of "emergency_stop" changes)
6268c0984e5SSebastian Reichel  */
cm_monitor(void)6278c0984e5SSebastian Reichel static bool cm_monitor(void)
6288c0984e5SSebastian Reichel {
6298c0984e5SSebastian Reichel 	bool stop = false;
6308c0984e5SSebastian Reichel 	struct charger_manager *cm;
6318c0984e5SSebastian Reichel 
6328c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
6338c0984e5SSebastian Reichel 
6348c0984e5SSebastian Reichel 	list_for_each_entry(cm, &cm_list, entry) {
6358c0984e5SSebastian Reichel 		if (_cm_monitor(cm))
6368c0984e5SSebastian Reichel 			stop = true;
6378c0984e5SSebastian Reichel 	}
6388c0984e5SSebastian Reichel 
6398c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
6408c0984e5SSebastian Reichel 
6418c0984e5SSebastian Reichel 	return stop;
6428c0984e5SSebastian Reichel }
6438c0984e5SSebastian Reichel 
6448c0984e5SSebastian Reichel /**
6458c0984e5SSebastian Reichel  * _setup_polling - Setup the next instance of polling.
6468c0984e5SSebastian Reichel  * @work: work_struct of the function _setup_polling.
6478c0984e5SSebastian Reichel  */
_setup_polling(struct work_struct * work)6488c0984e5SSebastian Reichel static void _setup_polling(struct work_struct *work)
6498c0984e5SSebastian Reichel {
6508c0984e5SSebastian Reichel 	unsigned long min = ULONG_MAX;
6518c0984e5SSebastian Reichel 	struct charger_manager *cm;
6528c0984e5SSebastian Reichel 	bool keep_polling = false;
6538c0984e5SSebastian Reichel 	unsigned long _next_polling;
6548c0984e5SSebastian Reichel 
6558c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
6568c0984e5SSebastian Reichel 
6578c0984e5SSebastian Reichel 	list_for_each_entry(cm, &cm_list, entry) {
6588c0984e5SSebastian Reichel 		if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
6598c0984e5SSebastian Reichel 			keep_polling = true;
6608c0984e5SSebastian Reichel 
6618c0984e5SSebastian Reichel 			if (min > cm->desc->polling_interval_ms)
6628c0984e5SSebastian Reichel 				min = cm->desc->polling_interval_ms;
6638c0984e5SSebastian Reichel 		}
6648c0984e5SSebastian Reichel 	}
6658c0984e5SSebastian Reichel 
6668c0984e5SSebastian Reichel 	polling_jiffy = msecs_to_jiffies(min);
6678c0984e5SSebastian Reichel 	if (polling_jiffy <= CM_JIFFIES_SMALL)
6688c0984e5SSebastian Reichel 		polling_jiffy = CM_JIFFIES_SMALL + 1;
6698c0984e5SSebastian Reichel 
6708c0984e5SSebastian Reichel 	if (!keep_polling)
6718c0984e5SSebastian Reichel 		polling_jiffy = ULONG_MAX;
6728c0984e5SSebastian Reichel 	if (polling_jiffy == ULONG_MAX)
6738c0984e5SSebastian Reichel 		goto out;
6748c0984e5SSebastian Reichel 
6758c0984e5SSebastian Reichel 	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
6768c0984e5SSebastian Reichel 			    ". try it later. %s\n", __func__);
6778c0984e5SSebastian Reichel 
6788c0984e5SSebastian Reichel 	/*
6798c0984e5SSebastian Reichel 	 * Use mod_delayed_work() iff the next polling interval should
6808c0984e5SSebastian Reichel 	 * occur before the currently scheduled one.  If @cm_monitor_work
6818c0984e5SSebastian Reichel 	 * isn't active, the end result is the same, so no need to worry
6828c0984e5SSebastian Reichel 	 * about stale @next_polling.
6838c0984e5SSebastian Reichel 	 */
6848c0984e5SSebastian Reichel 	_next_polling = jiffies + polling_jiffy;
6858c0984e5SSebastian Reichel 
6868c0984e5SSebastian Reichel 	if (time_before(_next_polling, next_polling)) {
6878c0984e5SSebastian Reichel 		mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
6888c0984e5SSebastian Reichel 		next_polling = _next_polling;
6898c0984e5SSebastian Reichel 	} else {
6908c0984e5SSebastian Reichel 		if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
6918c0984e5SSebastian Reichel 			next_polling = _next_polling;
6928c0984e5SSebastian Reichel 	}
6938c0984e5SSebastian Reichel out:
6948c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
6958c0984e5SSebastian Reichel }
6968c0984e5SSebastian Reichel static DECLARE_WORK(setup_polling, _setup_polling);
6978c0984e5SSebastian Reichel 
6988c0984e5SSebastian Reichel /**
6998c0984e5SSebastian Reichel  * cm_monitor_poller - The Monitor / Poller.
7008c0984e5SSebastian Reichel  * @work: work_struct of the function cm_monitor_poller
7018c0984e5SSebastian Reichel  *
7028c0984e5SSebastian Reichel  * During non-suspended state, cm_monitor_poller is used to poll and monitor
7038c0984e5SSebastian Reichel  * the batteries.
7048c0984e5SSebastian Reichel  */
cm_monitor_poller(struct work_struct * work)7058c0984e5SSebastian Reichel static void cm_monitor_poller(struct work_struct *work)
7068c0984e5SSebastian Reichel {
7078c0984e5SSebastian Reichel 	cm_monitor();
7088c0984e5SSebastian Reichel 	schedule_work(&setup_polling);
7098c0984e5SSebastian Reichel }
7108c0984e5SSebastian Reichel 
charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)7118c0984e5SSebastian Reichel static int charger_get_property(struct power_supply *psy,
7128c0984e5SSebastian Reichel 		enum power_supply_property psp,
7138c0984e5SSebastian Reichel 		union power_supply_propval *val)
7148c0984e5SSebastian Reichel {
7158c0984e5SSebastian Reichel 	struct charger_manager *cm = power_supply_get_drvdata(psy);
7168c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
7178c0984e5SSebastian Reichel 	struct power_supply *fuel_gauge = NULL;
7188c0984e5SSebastian Reichel 	int ret = 0;
7198c0984e5SSebastian Reichel 	int uV;
7208c0984e5SSebastian Reichel 
7218c0984e5SSebastian Reichel 	switch (psp) {
7228c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
723e132fc6bSJonghwa Lee 		val->intval = cm->battery_status;
7248c0984e5SSebastian Reichel 		break;
7258c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
72695b78d53SJunlin Yang 		if (cm->emergency_stop == CM_BATT_OVERHEAT)
7278c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
72895b78d53SJunlin Yang 		else if (cm->emergency_stop == CM_BATT_COLD)
7298c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_COLD;
7308c0984e5SSebastian Reichel 		else
7318c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
7328c0984e5SSebastian Reichel 		break;
7338c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
7348c0984e5SSebastian Reichel 		if (is_batt_present(cm))
7358c0984e5SSebastian Reichel 			val->intval = 1;
7368c0984e5SSebastian Reichel 		else
7378c0984e5SSebastian Reichel 			val->intval = 0;
7388c0984e5SSebastian Reichel 		break;
7398c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
7408c0984e5SSebastian Reichel 		ret = get_batt_uV(cm, &val->intval);
7418c0984e5SSebastian Reichel 		break;
7428c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CURRENT_NOW:
7438c0984e5SSebastian Reichel 		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
7448c0984e5SSebastian Reichel 		if (!fuel_gauge) {
7458c0984e5SSebastian Reichel 			ret = -ENODEV;
7468c0984e5SSebastian Reichel 			break;
7478c0984e5SSebastian Reichel 		}
7488c0984e5SSebastian Reichel 		ret = power_supply_get_property(fuel_gauge,
7498c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_CURRENT_NOW, val);
7508c0984e5SSebastian Reichel 		break;
7518c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
7528c0984e5SSebastian Reichel 		return cm_get_battery_temperature(cm, &val->intval);
7538c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CAPACITY:
7548c0984e5SSebastian Reichel 		if (!is_batt_present(cm)) {
7558c0984e5SSebastian Reichel 			/* There is no battery. Assume 100% */
7568c0984e5SSebastian Reichel 			val->intval = 100;
7578c0984e5SSebastian Reichel 			break;
7588c0984e5SSebastian Reichel 		}
7598c0984e5SSebastian Reichel 
7608c0984e5SSebastian Reichel 		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
7618c0984e5SSebastian Reichel 		if (!fuel_gauge) {
7628c0984e5SSebastian Reichel 			ret = -ENODEV;
7638c0984e5SSebastian Reichel 			break;
7648c0984e5SSebastian Reichel 		}
7658c0984e5SSebastian Reichel 
7668c0984e5SSebastian Reichel 		ret = power_supply_get_property(fuel_gauge,
7678c0984e5SSebastian Reichel 					POWER_SUPPLY_PROP_CAPACITY, val);
7688c0984e5SSebastian Reichel 		if (ret)
7698c0984e5SSebastian Reichel 			break;
7708c0984e5SSebastian Reichel 
7718c0984e5SSebastian Reichel 		if (val->intval > 100) {
7728c0984e5SSebastian Reichel 			val->intval = 100;
7738c0984e5SSebastian Reichel 			break;
7748c0984e5SSebastian Reichel 		}
7758c0984e5SSebastian Reichel 		if (val->intval < 0)
7768c0984e5SSebastian Reichel 			val->intval = 0;
7778c0984e5SSebastian Reichel 
7788c0984e5SSebastian Reichel 		/* Do not adjust SOC when charging: voltage is overrated */
7798c0984e5SSebastian Reichel 		if (is_charging(cm))
7808c0984e5SSebastian Reichel 			break;
7818c0984e5SSebastian Reichel 
7828c0984e5SSebastian Reichel 		/*
7838c0984e5SSebastian Reichel 		 * If the capacity value is inconsistent, calibrate it base on
7848c0984e5SSebastian Reichel 		 * the battery voltage values and the thresholds given as desc
7858c0984e5SSebastian Reichel 		 */
7868c0984e5SSebastian Reichel 		ret = get_batt_uV(cm, &uV);
7878c0984e5SSebastian Reichel 		if (ret) {
7888c0984e5SSebastian Reichel 			/* Voltage information not available. No calibration */
7898c0984e5SSebastian Reichel 			ret = 0;
7908c0984e5SSebastian Reichel 			break;
7918c0984e5SSebastian Reichel 		}
7928c0984e5SSebastian Reichel 
7938c0984e5SSebastian Reichel 		if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
7948c0984e5SSebastian Reichel 		    !is_charging(cm)) {
7958c0984e5SSebastian Reichel 			val->intval = 100;
7968c0984e5SSebastian Reichel 			break;
7978c0984e5SSebastian Reichel 		}
7988c0984e5SSebastian Reichel 
7998c0984e5SSebastian Reichel 		break;
8008c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
8018c0984e5SSebastian Reichel 		if (is_ext_pwr_online(cm))
8028c0984e5SSebastian Reichel 			val->intval = 1;
8038c0984e5SSebastian Reichel 		else
8048c0984e5SSebastian Reichel 			val->intval = 0;
8058c0984e5SSebastian Reichel 		break;
8068c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_FULL:
8078c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_NOW:
8080a9e0f94SJonghwa Lee 		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
8098c0984e5SSebastian Reichel 		if (!fuel_gauge) {
8108c0984e5SSebastian Reichel 			ret = -ENODEV;
8118c0984e5SSebastian Reichel 			break;
8128c0984e5SSebastian Reichel 		}
8130a9e0f94SJonghwa Lee 		ret = power_supply_get_property(fuel_gauge, psp, val);
8148c0984e5SSebastian Reichel 		break;
8158c0984e5SSebastian Reichel 	default:
8168c0984e5SSebastian Reichel 		return -EINVAL;
8178c0984e5SSebastian Reichel 	}
8188c0984e5SSebastian Reichel 	if (fuel_gauge)
8198c0984e5SSebastian Reichel 		power_supply_put(fuel_gauge);
8208c0984e5SSebastian Reichel 	return ret;
8218c0984e5SSebastian Reichel }
8228c0984e5SSebastian Reichel 
8238c0984e5SSebastian Reichel #define NUM_CHARGER_PSY_OPTIONAL	(4)
8248c0984e5SSebastian Reichel static enum power_supply_property default_charger_props[] = {
8258c0984e5SSebastian Reichel 	/* Guaranteed to provide */
8268c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
8278c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH,
8288c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
8298c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
8308c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CAPACITY,
8318c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
8328c0984e5SSebastian Reichel 	/*
8338c0984e5SSebastian Reichel 	 * Optional properties are:
8340a9e0f94SJonghwa Lee 	 * POWER_SUPPLY_PROP_CHARGE_FULL,
8358c0984e5SSebastian Reichel 	 * POWER_SUPPLY_PROP_CHARGE_NOW,
8368c0984e5SSebastian Reichel 	 * POWER_SUPPLY_PROP_CURRENT_NOW,
837cdaeb151SJonathan Bakker 	 * POWER_SUPPLY_PROP_TEMP,
8388c0984e5SSebastian Reichel 	 */
8398c0984e5SSebastian Reichel };
8408c0984e5SSebastian Reichel 
8418c0984e5SSebastian Reichel static const struct power_supply_desc psy_default = {
8428c0984e5SSebastian Reichel 	.name = "battery",
8438c0984e5SSebastian Reichel 	.type = POWER_SUPPLY_TYPE_BATTERY,
8448c0984e5SSebastian Reichel 	.properties = default_charger_props,
8458c0984e5SSebastian Reichel 	.num_properties = ARRAY_SIZE(default_charger_props),
8468c0984e5SSebastian Reichel 	.get_property = charger_get_property,
8478c0984e5SSebastian Reichel 	.no_thermal = true,
8488c0984e5SSebastian Reichel };
8498c0984e5SSebastian Reichel 
8508c0984e5SSebastian Reichel /**
8518c0984e5SSebastian Reichel  * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
8528c0984e5SSebastian Reichel  *		    for suspend_again.
8538c0984e5SSebastian Reichel  *
8548c0984e5SSebastian Reichel  * Returns true if the alarm is set for Charger Manager to use.
8558c0984e5SSebastian Reichel  * Returns false if
8568c0984e5SSebastian Reichel  *	cm_setup_timer fails to set an alarm,
8578c0984e5SSebastian Reichel  *	cm_setup_timer does not need to set an alarm for Charger Manager,
8588c0984e5SSebastian Reichel  *	or an alarm previously configured is to be used.
8598c0984e5SSebastian Reichel  */
cm_setup_timer(void)8608c0984e5SSebastian Reichel static bool cm_setup_timer(void)
8618c0984e5SSebastian Reichel {
8628c0984e5SSebastian Reichel 	struct charger_manager *cm;
8638c0984e5SSebastian Reichel 	unsigned int wakeup_ms = UINT_MAX;
8648c0984e5SSebastian Reichel 	int timer_req = 0;
8658c0984e5SSebastian Reichel 
8668c0984e5SSebastian Reichel 	if (time_after(next_polling, jiffies))
8678c0984e5SSebastian Reichel 		CM_MIN_VALID(wakeup_ms,
8688c0984e5SSebastian Reichel 			jiffies_to_msecs(next_polling - jiffies));
8698c0984e5SSebastian Reichel 
8708c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
8718c0984e5SSebastian Reichel 	list_for_each_entry(cm, &cm_list, entry) {
8728c0984e5SSebastian Reichel 		/* Skip if polling is not required for this CM */
8738c0984e5SSebastian Reichel 		if (!is_polling_required(cm) && !cm->emergency_stop)
8748c0984e5SSebastian Reichel 			continue;
8758c0984e5SSebastian Reichel 		timer_req++;
8768c0984e5SSebastian Reichel 		if (cm->desc->polling_interval_ms == 0)
8778c0984e5SSebastian Reichel 			continue;
8788c0984e5SSebastian Reichel 		CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
8798c0984e5SSebastian Reichel 	}
8808c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
8818c0984e5SSebastian Reichel 
8828c0984e5SSebastian Reichel 	if (timer_req && cm_timer) {
8838c0984e5SSebastian Reichel 		ktime_t now, add;
8848c0984e5SSebastian Reichel 
8858c0984e5SSebastian Reichel 		/*
8868c0984e5SSebastian Reichel 		 * Set alarm with the polling interval (wakeup_ms)
8878c0984e5SSebastian Reichel 		 * The alarm time should be NOW + CM_RTC_SMALL or later.
8888c0984e5SSebastian Reichel 		 */
8898c0984e5SSebastian Reichel 		if (wakeup_ms == UINT_MAX ||
8908c0984e5SSebastian Reichel 			wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
8918c0984e5SSebastian Reichel 			wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
8928c0984e5SSebastian Reichel 
8938c0984e5SSebastian Reichel 		pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
8948c0984e5SSebastian Reichel 
8958c0984e5SSebastian Reichel 		now = ktime_get_boottime();
8968c0984e5SSebastian Reichel 		add = ktime_set(wakeup_ms / MSEC_PER_SEC,
8978c0984e5SSebastian Reichel 				(wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
8988c0984e5SSebastian Reichel 		alarm_start(cm_timer, ktime_add(now, add));
8998c0984e5SSebastian Reichel 
9008c0984e5SSebastian Reichel 		cm_suspend_duration_ms = wakeup_ms;
9018c0984e5SSebastian Reichel 
9028c0984e5SSebastian Reichel 		return true;
9038c0984e5SSebastian Reichel 	}
9048c0984e5SSebastian Reichel 	return false;
9058c0984e5SSebastian Reichel }
9068c0984e5SSebastian Reichel 
9078c0984e5SSebastian Reichel /**
9088c0984e5SSebastian Reichel  * charger_extcon_work - enable/diable charger according to the state
9098c0984e5SSebastian Reichel  *			of charger cable
9108c0984e5SSebastian Reichel  *
9118c0984e5SSebastian Reichel  * @work: work_struct of the function charger_extcon_work.
9128c0984e5SSebastian Reichel  */
charger_extcon_work(struct work_struct * work)9138c0984e5SSebastian Reichel static void charger_extcon_work(struct work_struct *work)
9148c0984e5SSebastian Reichel {
9158c0984e5SSebastian Reichel 	struct charger_cable *cable =
9168c0984e5SSebastian Reichel 			container_of(work, struct charger_cable, wq);
9178c0984e5SSebastian Reichel 	int ret;
9188c0984e5SSebastian Reichel 
9198c0984e5SSebastian Reichel 	if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
9208c0984e5SSebastian Reichel 		ret = regulator_set_current_limit(cable->charger->consumer,
9218c0984e5SSebastian Reichel 					cable->min_uA, cable->max_uA);
9228c0984e5SSebastian Reichel 		if (ret < 0) {
9238c0984e5SSebastian Reichel 			pr_err("Cannot set current limit of %s (%s)\n",
9248c0984e5SSebastian Reichel 			       cable->charger->regulator_name, cable->name);
9258c0984e5SSebastian Reichel 			return;
9268c0984e5SSebastian Reichel 		}
9278c0984e5SSebastian Reichel 
9288c0984e5SSebastian Reichel 		pr_info("Set current limit of %s : %duA ~ %duA\n",
9298c0984e5SSebastian Reichel 			cable->charger->regulator_name,
9308c0984e5SSebastian Reichel 			cable->min_uA, cable->max_uA);
9318c0984e5SSebastian Reichel 	}
9328c0984e5SSebastian Reichel 
9339434e453SJonghwa Lee 	cancel_delayed_work(&cm_monitor_work);
9349434e453SJonghwa Lee 	queue_delayed_work(cm_wq, &cm_monitor_work, 0);
9358c0984e5SSebastian Reichel }
9368c0984e5SSebastian Reichel 
9378c0984e5SSebastian Reichel /**
9388c0984e5SSebastian Reichel  * charger_extcon_notifier - receive the state of charger cable
9398c0984e5SSebastian Reichel  *			when registered cable is attached or detached.
9408c0984e5SSebastian Reichel  *
9418c0984e5SSebastian Reichel  * @self: the notifier block of the charger_extcon_notifier.
9428c0984e5SSebastian Reichel  * @event: the cable state.
9438c0984e5SSebastian Reichel  * @ptr: the data pointer of notifier block.
9448c0984e5SSebastian Reichel  */
charger_extcon_notifier(struct notifier_block * self,unsigned long event,void * ptr)9458c0984e5SSebastian Reichel static int charger_extcon_notifier(struct notifier_block *self,
9468c0984e5SSebastian Reichel 			unsigned long event, void *ptr)
9478c0984e5SSebastian Reichel {
9488c0984e5SSebastian Reichel 	struct charger_cable *cable =
9498c0984e5SSebastian Reichel 		container_of(self, struct charger_cable, nb);
9508c0984e5SSebastian Reichel 
9518c0984e5SSebastian Reichel 	/*
9528c0984e5SSebastian Reichel 	 * The newly state of charger cable.
9538c0984e5SSebastian Reichel 	 * If cable is attached, cable->attached is true.
9548c0984e5SSebastian Reichel 	 */
9558c0984e5SSebastian Reichel 	cable->attached = event;
9568c0984e5SSebastian Reichel 
9578c0984e5SSebastian Reichel 	/*
9588c0984e5SSebastian Reichel 	 * Setup work for controlling charger(regulator)
9598c0984e5SSebastian Reichel 	 * according to charger cable.
9608c0984e5SSebastian Reichel 	 */
9618c0984e5SSebastian Reichel 	schedule_work(&cable->wq);
9628c0984e5SSebastian Reichel 
9638c0984e5SSebastian Reichel 	return NOTIFY_DONE;
9648c0984e5SSebastian Reichel }
9658c0984e5SSebastian Reichel 
9668c0984e5SSebastian Reichel /**
9678c0984e5SSebastian Reichel  * charger_extcon_init - register external connector to use it
9688c0984e5SSebastian Reichel  *			as the charger cable
9698c0984e5SSebastian Reichel  *
9708c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
9718c0984e5SSebastian Reichel  * @cable: the Charger cable representing the external connector.
9728c0984e5SSebastian Reichel  */
charger_extcon_init(struct charger_manager * cm,struct charger_cable * cable)9738c0984e5SSebastian Reichel static int charger_extcon_init(struct charger_manager *cm,
9748c0984e5SSebastian Reichel 		struct charger_cable *cable)
9758c0984e5SSebastian Reichel {
976c1f73028SJonathan Bakker 	int ret, i;
977c1f73028SJonathan Bakker 	u64 extcon_type = EXTCON_NONE;
9788c0984e5SSebastian Reichel 
9798c0984e5SSebastian Reichel 	/*
9808c0984e5SSebastian Reichel 	 * Charger manager use Extcon framework to identify
9818c0984e5SSebastian Reichel 	 * the charger cable among various external connector
9828c0984e5SSebastian Reichel 	 * cable (e.g., TA, USB, MHL, Dock).
9838c0984e5SSebastian Reichel 	 */
9848c0984e5SSebastian Reichel 	INIT_WORK(&cable->wq, charger_extcon_work);
9858c0984e5SSebastian Reichel 	cable->nb.notifier_call = charger_extcon_notifier;
986c1f73028SJonathan Bakker 
987c1f73028SJonathan Bakker 	cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name);
98858e4a2d2SDan Carpenter 	if (IS_ERR(cable->extcon_dev)) {
989c1f73028SJonathan Bakker 		pr_err("Cannot find extcon_dev for %s (cable: %s)\n",
9908c0984e5SSebastian Reichel 			cable->extcon_name, cable->name);
991c1f73028SJonathan Bakker 		return PTR_ERR(cable->extcon_dev);
9928c0984e5SSebastian Reichel 	}
9938c0984e5SSebastian Reichel 
994c1f73028SJonathan Bakker 	for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) {
995c1f73028SJonathan Bakker 		if (!strcmp(cable->name, extcon_mapping[i].name)) {
996c1f73028SJonathan Bakker 			extcon_type = extcon_mapping[i].extcon_type;
997c1f73028SJonathan Bakker 			break;
998c1f73028SJonathan Bakker 		}
999c1f73028SJonathan Bakker 	}
1000c1f73028SJonathan Bakker 	if (extcon_type == EXTCON_NONE) {
1001c1f73028SJonathan Bakker 		pr_err("Cannot find cable for type %s", cable->name);
1002c1f73028SJonathan Bakker 		return -EINVAL;
1003c1f73028SJonathan Bakker 	}
1004c1f73028SJonathan Bakker 
1005c1f73028SJonathan Bakker 	cable->extcon_type = extcon_type;
1006c1f73028SJonathan Bakker 
1007c1f73028SJonathan Bakker 	ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev,
1008c1f73028SJonathan Bakker 		cable->extcon_type, &cable->nb);
1009c1f73028SJonathan Bakker 	if (ret < 0) {
1010c1f73028SJonathan Bakker 		pr_err("Cannot register extcon_dev for %s (cable: %s)\n",
1011c1f73028SJonathan Bakker 			cable->extcon_name, cable->name);
10128c0984e5SSebastian Reichel 		return ret;
10138c0984e5SSebastian Reichel 	}
10148c0984e5SSebastian Reichel 
1015c1f73028SJonathan Bakker 	return 0;
1016c1f73028SJonathan Bakker }
1017c1f73028SJonathan Bakker 
10188c0984e5SSebastian Reichel /**
101902276af2SKrzysztof Kozlowski  * charger_manager_register_extcon - Register extcon device to receive state
10208c0984e5SSebastian Reichel  *				     of charger cable.
10218c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
10228c0984e5SSebastian Reichel  *
10238c0984e5SSebastian Reichel  * This function support EXTCON(External Connector) subsystem to detect the
10248c0984e5SSebastian Reichel  * state of charger cables for enabling or disabling charger(regulator) and
10258c0984e5SSebastian Reichel  * select the charger cable for charging among a number of external cable
10268c0984e5SSebastian Reichel  * according to policy of H/W board.
10278c0984e5SSebastian Reichel  */
charger_manager_register_extcon(struct charger_manager * cm)10288c0984e5SSebastian Reichel static int charger_manager_register_extcon(struct charger_manager *cm)
10298c0984e5SSebastian Reichel {
10308c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
10318c0984e5SSebastian Reichel 	struct charger_regulator *charger;
1032c1f73028SJonathan Bakker 	unsigned long event;
1033dc6ea7d4SAndi Shyti 	int ret;
10348c0984e5SSebastian Reichel 	int i;
10358c0984e5SSebastian Reichel 	int j;
10368c0984e5SSebastian Reichel 
10378c0984e5SSebastian Reichel 	for (i = 0; i < desc->num_charger_regulators; i++) {
10388c0984e5SSebastian Reichel 		charger = &desc->charger_regulators[i];
10398c0984e5SSebastian Reichel 
10408c0984e5SSebastian Reichel 		charger->consumer = regulator_get(cm->dev,
10418c0984e5SSebastian Reichel 					charger->regulator_name);
10428c0984e5SSebastian Reichel 		if (IS_ERR(charger->consumer)) {
10438c0984e5SSebastian Reichel 			dev_err(cm->dev, "Cannot find charger(%s)\n",
10448c0984e5SSebastian Reichel 				charger->regulator_name);
10458c0984e5SSebastian Reichel 			return PTR_ERR(charger->consumer);
10468c0984e5SSebastian Reichel 		}
10478c0984e5SSebastian Reichel 		charger->cm = cm;
10488c0984e5SSebastian Reichel 
10498c0984e5SSebastian Reichel 		for (j = 0; j < charger->num_cables; j++) {
10508c0984e5SSebastian Reichel 			struct charger_cable *cable = &charger->cables[j];
10518c0984e5SSebastian Reichel 
10528c0984e5SSebastian Reichel 			ret = charger_extcon_init(cm, cable);
10538c0984e5SSebastian Reichel 			if (ret < 0) {
10548c0984e5SSebastian Reichel 				dev_err(cm->dev, "Cannot initialize charger(%s)\n",
10558c0984e5SSebastian Reichel 					charger->regulator_name);
1056dc6ea7d4SAndi Shyti 				return ret;
10578c0984e5SSebastian Reichel 			}
10588c0984e5SSebastian Reichel 			cable->charger = charger;
10598c0984e5SSebastian Reichel 			cable->cm = cm;
1060c1f73028SJonathan Bakker 
1061c1f73028SJonathan Bakker 			event = extcon_get_state(cable->extcon_dev,
1062c1f73028SJonathan Bakker 				cable->extcon_type);
1063c1f73028SJonathan Bakker 			charger_extcon_notifier(&cable->nb,
1064c1f73028SJonathan Bakker 				event, NULL);
10658c0984e5SSebastian Reichel 		}
10668c0984e5SSebastian Reichel 	}
10678c0984e5SSebastian Reichel 
1068dc6ea7d4SAndi Shyti 	return 0;
10698c0984e5SSebastian Reichel }
10708c0984e5SSebastian Reichel 
10718c0984e5SSebastian Reichel /* help function of sysfs node to control charger(regulator) */
charger_name_show(struct device * dev,struct device_attribute * attr,char * buf)10728c0984e5SSebastian Reichel static ssize_t charger_name_show(struct device *dev,
10738c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
10748c0984e5SSebastian Reichel {
10758c0984e5SSebastian Reichel 	struct charger_regulator *charger
10768c0984e5SSebastian Reichel 		= container_of(attr, struct charger_regulator, attr_name);
10778c0984e5SSebastian Reichel 
1078a441f3b9Sye xingchen 	return sysfs_emit(buf, "%s\n", charger->regulator_name);
10798c0984e5SSebastian Reichel }
10808c0984e5SSebastian Reichel 
charger_state_show(struct device * dev,struct device_attribute * attr,char * buf)10818c0984e5SSebastian Reichel static ssize_t charger_state_show(struct device *dev,
10828c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
10838c0984e5SSebastian Reichel {
10848c0984e5SSebastian Reichel 	struct charger_regulator *charger
10858c0984e5SSebastian Reichel 		= container_of(attr, struct charger_regulator, attr_state);
10868c0984e5SSebastian Reichel 	int state = 0;
10878c0984e5SSebastian Reichel 
10888c0984e5SSebastian Reichel 	if (!charger->externally_control)
10898c0984e5SSebastian Reichel 		state = regulator_is_enabled(charger->consumer);
10908c0984e5SSebastian Reichel 
1091a441f3b9Sye xingchen 	return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled");
10928c0984e5SSebastian Reichel }
10938c0984e5SSebastian Reichel 
charger_externally_control_show(struct device * dev,struct device_attribute * attr,char * buf)10948c0984e5SSebastian Reichel static ssize_t charger_externally_control_show(struct device *dev,
10958c0984e5SSebastian Reichel 				struct device_attribute *attr, char *buf)
10968c0984e5SSebastian Reichel {
10978c0984e5SSebastian Reichel 	struct charger_regulator *charger = container_of(attr,
10988c0984e5SSebastian Reichel 			struct charger_regulator, attr_externally_control);
10998c0984e5SSebastian Reichel 
1100a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d\n", charger->externally_control);
11018c0984e5SSebastian Reichel }
11028c0984e5SSebastian Reichel 
charger_externally_control_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)11038c0984e5SSebastian Reichel static ssize_t charger_externally_control_store(struct device *dev,
11048c0984e5SSebastian Reichel 				struct device_attribute *attr, const char *buf,
11058c0984e5SSebastian Reichel 				size_t count)
11068c0984e5SSebastian Reichel {
11078c0984e5SSebastian Reichel 	struct charger_regulator *charger
11088c0984e5SSebastian Reichel 		= container_of(attr, struct charger_regulator,
11098c0984e5SSebastian Reichel 					attr_externally_control);
11108c0984e5SSebastian Reichel 	struct charger_manager *cm = charger->cm;
11118c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
11128c0984e5SSebastian Reichel 	int i;
11138c0984e5SSebastian Reichel 	int ret;
11148c0984e5SSebastian Reichel 	int externally_control;
11158c0984e5SSebastian Reichel 	int chargers_externally_control = 1;
11168c0984e5SSebastian Reichel 
11178c0984e5SSebastian Reichel 	ret = sscanf(buf, "%d", &externally_control);
11188c0984e5SSebastian Reichel 	if (ret == 0) {
11198c0984e5SSebastian Reichel 		ret = -EINVAL;
11208c0984e5SSebastian Reichel 		return ret;
11218c0984e5SSebastian Reichel 	}
11228c0984e5SSebastian Reichel 
11238c0984e5SSebastian Reichel 	if (!externally_control) {
11248c0984e5SSebastian Reichel 		charger->externally_control = 0;
11258c0984e5SSebastian Reichel 		return count;
11268c0984e5SSebastian Reichel 	}
11278c0984e5SSebastian Reichel 
11288c0984e5SSebastian Reichel 	for (i = 0; i < desc->num_charger_regulators; i++) {
11298c0984e5SSebastian Reichel 		if (&desc->charger_regulators[i] != charger &&
11308c0984e5SSebastian Reichel 			!desc->charger_regulators[i].externally_control) {
11318c0984e5SSebastian Reichel 			/*
11328c0984e5SSebastian Reichel 			 * At least, one charger is controlled by
11338c0984e5SSebastian Reichel 			 * charger-manager
11348c0984e5SSebastian Reichel 			 */
11358c0984e5SSebastian Reichel 			chargers_externally_control = 0;
11368c0984e5SSebastian Reichel 			break;
11378c0984e5SSebastian Reichel 		}
11388c0984e5SSebastian Reichel 	}
11398c0984e5SSebastian Reichel 
11408c0984e5SSebastian Reichel 	if (!chargers_externally_control) {
11418c0984e5SSebastian Reichel 		if (cm->charger_enabled) {
11428c0984e5SSebastian Reichel 			try_charger_enable(charger->cm, false);
11438c0984e5SSebastian Reichel 			charger->externally_control = externally_control;
11448c0984e5SSebastian Reichel 			try_charger_enable(charger->cm, true);
11458c0984e5SSebastian Reichel 		} else {
11468c0984e5SSebastian Reichel 			charger->externally_control = externally_control;
11478c0984e5SSebastian Reichel 		}
11488c0984e5SSebastian Reichel 	} else {
11498c0984e5SSebastian Reichel 		dev_warn(cm->dev,
11508c0984e5SSebastian Reichel 			 "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n",
11518c0984e5SSebastian Reichel 			 charger->regulator_name);
11528c0984e5SSebastian Reichel 	}
11538c0984e5SSebastian Reichel 
11548c0984e5SSebastian Reichel 	return count;
11558c0984e5SSebastian Reichel }
11568c0984e5SSebastian Reichel 
11578c0984e5SSebastian Reichel /**
1158157ba1bbSSebastian Reichel  * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger
11598c0984e5SSebastian Reichel  * @cm: the Charger Manager representing the battery.
11608c0984e5SSebastian Reichel  *
11618c0984e5SSebastian Reichel  * This function add sysfs entry for charger(regulator) to control charger from
11628c0984e5SSebastian Reichel  * user-space. If some development board use one more chargers for charging
11638c0984e5SSebastian Reichel  * but only need one charger on specific case which is dependent on user
11648c0984e5SSebastian Reichel  * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
11658c0984e5SSebastian Reichel  * class/power_supply/battery/charger.[index]/externally_control'. For example,
11668c0984e5SSebastian Reichel  * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
11678c0984e5SSebastian Reichel  * externally_control, this charger isn't controlled from charger-manager and
11688c0984e5SSebastian Reichel  * always stay off state of regulator.
11698c0984e5SSebastian Reichel  */
charger_manager_prepare_sysfs(struct charger_manager * cm)1170157ba1bbSSebastian Reichel static int charger_manager_prepare_sysfs(struct charger_manager *cm)
11718c0984e5SSebastian Reichel {
11728c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
11738c0984e5SSebastian Reichel 	struct charger_regulator *charger;
11748c0984e5SSebastian Reichel 	int chargers_externally_control = 1;
1175efcca6bdSSebastian Reichel 	char *name;
11768c0984e5SSebastian Reichel 	int i;
11778c0984e5SSebastian Reichel 
11788c0984e5SSebastian Reichel 	/* Create sysfs entry to control charger(regulator) */
11798c0984e5SSebastian Reichel 	for (i = 0; i < desc->num_charger_regulators; i++) {
11808c0984e5SSebastian Reichel 		charger = &desc->charger_regulators[i];
11818c0984e5SSebastian Reichel 
1182efcca6bdSSebastian Reichel 		name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i);
1183efcca6bdSSebastian Reichel 		if (!name)
1184dc6ea7d4SAndi Shyti 			return -ENOMEM;
1185dc6ea7d4SAndi Shyti 
11868c0984e5SSebastian Reichel 		charger->attrs[0] = &charger->attr_name.attr;
11878c0984e5SSebastian Reichel 		charger->attrs[1] = &charger->attr_state.attr;
11888c0984e5SSebastian Reichel 		charger->attrs[2] = &charger->attr_externally_control.attr;
11898c0984e5SSebastian Reichel 		charger->attrs[3] = NULL;
1190157ba1bbSSebastian Reichel 
1191157ba1bbSSebastian Reichel 		charger->attr_grp.name = name;
1192157ba1bbSSebastian Reichel 		charger->attr_grp.attrs = charger->attrs;
1193157ba1bbSSebastian Reichel 		desc->sysfs_groups[i] = &charger->attr_grp;
11948c0984e5SSebastian Reichel 
11958c0984e5SSebastian Reichel 		sysfs_attr_init(&charger->attr_name.attr);
11968c0984e5SSebastian Reichel 		charger->attr_name.attr.name = "name";
11978c0984e5SSebastian Reichel 		charger->attr_name.attr.mode = 0444;
11988c0984e5SSebastian Reichel 		charger->attr_name.show = charger_name_show;
11998c0984e5SSebastian Reichel 
12008c0984e5SSebastian Reichel 		sysfs_attr_init(&charger->attr_state.attr);
12018c0984e5SSebastian Reichel 		charger->attr_state.attr.name = "state";
12028c0984e5SSebastian Reichel 		charger->attr_state.attr.mode = 0444;
12038c0984e5SSebastian Reichel 		charger->attr_state.show = charger_state_show;
12048c0984e5SSebastian Reichel 
12058c0984e5SSebastian Reichel 		sysfs_attr_init(&charger->attr_externally_control.attr);
12068c0984e5SSebastian Reichel 		charger->attr_externally_control.attr.name
12078c0984e5SSebastian Reichel 				= "externally_control";
12088c0984e5SSebastian Reichel 		charger->attr_externally_control.attr.mode = 0644;
12098c0984e5SSebastian Reichel 		charger->attr_externally_control.show
12108c0984e5SSebastian Reichel 				= charger_externally_control_show;
12118c0984e5SSebastian Reichel 		charger->attr_externally_control.store
12128c0984e5SSebastian Reichel 				= charger_externally_control_store;
12138c0984e5SSebastian Reichel 
12148c0984e5SSebastian Reichel 		if (!desc->charger_regulators[i].externally_control ||
12158c0984e5SSebastian Reichel 				!chargers_externally_control)
12168c0984e5SSebastian Reichel 			chargers_externally_control = 0;
12178c0984e5SSebastian Reichel 
12188c0984e5SSebastian Reichel 		dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
12198c0984e5SSebastian Reichel 			 charger->regulator_name, charger->externally_control);
12208c0984e5SSebastian Reichel 	}
12218c0984e5SSebastian Reichel 
12228c0984e5SSebastian Reichel 	if (chargers_externally_control) {
12238c0984e5SSebastian Reichel 		dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n");
1224dc6ea7d4SAndi Shyti 		return -EINVAL;
12258c0984e5SSebastian Reichel 	}
12268c0984e5SSebastian Reichel 
1227dc6ea7d4SAndi Shyti 	return 0;
12288c0984e5SSebastian Reichel }
12298c0984e5SSebastian Reichel 
cm_init_thermal_data(struct charger_manager * cm,struct power_supply * fuel_gauge,enum power_supply_property * properties,size_t * num_properties)12308c0984e5SSebastian Reichel static int cm_init_thermal_data(struct charger_manager *cm,
12314cb38258SSebastian Reichel 		struct power_supply *fuel_gauge,
12324cb38258SSebastian Reichel 		enum power_supply_property *properties,
12334cb38258SSebastian Reichel 		size_t *num_properties)
12348c0984e5SSebastian Reichel {
12358c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
12368c0984e5SSebastian Reichel 	union power_supply_propval val;
12378c0984e5SSebastian Reichel 	int ret;
12388c0984e5SSebastian Reichel 
12398c0984e5SSebastian Reichel 	/* Verify whether fuel gauge provides battery temperature */
12408c0984e5SSebastian Reichel 	ret = power_supply_get_property(fuel_gauge,
12418c0984e5SSebastian Reichel 					POWER_SUPPLY_PROP_TEMP, &val);
12428c0984e5SSebastian Reichel 
12438c0984e5SSebastian Reichel 	if (!ret) {
12444cb38258SSebastian Reichel 		properties[*num_properties] = POWER_SUPPLY_PROP_TEMP;
12454cb38258SSebastian Reichel 		(*num_properties)++;
12468c0984e5SSebastian Reichel 		cm->desc->measure_battery_temp = true;
12478c0984e5SSebastian Reichel 	}
12488c0984e5SSebastian Reichel #ifdef CONFIG_THERMAL
12498c0984e5SSebastian Reichel 	if (ret && desc->thermal_zone) {
12508c0984e5SSebastian Reichel 		cm->tzd_batt =
12518c0984e5SSebastian Reichel 			thermal_zone_get_zone_by_name(desc->thermal_zone);
12528c0984e5SSebastian Reichel 		if (IS_ERR(cm->tzd_batt))
12538c0984e5SSebastian Reichel 			return PTR_ERR(cm->tzd_batt);
12548c0984e5SSebastian Reichel 
12558c0984e5SSebastian Reichel 		/* Use external thermometer */
1256cdaeb151SJonathan Bakker 		properties[*num_properties] = POWER_SUPPLY_PROP_TEMP;
12574cb38258SSebastian Reichel 		(*num_properties)++;
12588c0984e5SSebastian Reichel 		cm->desc->measure_battery_temp = true;
12598c0984e5SSebastian Reichel 		ret = 0;
12608c0984e5SSebastian Reichel 	}
12618c0984e5SSebastian Reichel #endif
12628c0984e5SSebastian Reichel 	if (cm->desc->measure_battery_temp) {
12638c0984e5SSebastian Reichel 		/* NOTICE : Default allowable minimum charge temperature is 0 */
12648c0984e5SSebastian Reichel 		if (!desc->temp_max)
12658c0984e5SSebastian Reichel 			desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
12668c0984e5SSebastian Reichel 		if (!desc->temp_diff)
12678c0984e5SSebastian Reichel 			desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
12688c0984e5SSebastian Reichel 	}
12698c0984e5SSebastian Reichel 
12708c0984e5SSebastian Reichel 	return ret;
12718c0984e5SSebastian Reichel }
12728c0984e5SSebastian Reichel 
12738c0984e5SSebastian Reichel static const struct of_device_id charger_manager_match[] = {
12748c0984e5SSebastian Reichel 	{
12758c0984e5SSebastian Reichel 		.compatible = "charger-manager",
12768c0984e5SSebastian Reichel 	},
12778c0984e5SSebastian Reichel 	{},
12788c0984e5SSebastian Reichel };
1279073b5d5bSZou Wei MODULE_DEVICE_TABLE(of, charger_manager_match);
12808c0984e5SSebastian Reichel 
of_cm_parse_desc(struct device * dev)12818c0984e5SSebastian Reichel static struct charger_desc *of_cm_parse_desc(struct device *dev)
12828c0984e5SSebastian Reichel {
12838c0984e5SSebastian Reichel 	struct charger_desc *desc;
12848c0984e5SSebastian Reichel 	struct device_node *np = dev->of_node;
12858c0984e5SSebastian Reichel 	u32 poll_mode = CM_POLL_DISABLE;
12868c0984e5SSebastian Reichel 	u32 battery_stat = CM_NO_BATTERY;
12878c0984e5SSebastian Reichel 	int num_chgs = 0;
12888c0984e5SSebastian Reichel 
12898c0984e5SSebastian Reichel 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
12908c0984e5SSebastian Reichel 	if (!desc)
12918c0984e5SSebastian Reichel 		return ERR_PTR(-ENOMEM);
12928c0984e5SSebastian Reichel 
12938c0984e5SSebastian Reichel 	of_property_read_string(np, "cm-name", &desc->psy_name);
12948c0984e5SSebastian Reichel 
12958c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-poll-mode", &poll_mode);
12968c0984e5SSebastian Reichel 	desc->polling_mode = poll_mode;
12978c0984e5SSebastian Reichel 
12988c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-poll-interval",
12998c0984e5SSebastian Reichel 				&desc->polling_interval_ms);
13008c0984e5SSebastian Reichel 
13018c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt",
13028c0984e5SSebastian Reichel 					&desc->fullbatt_vchkdrop_uV);
13038c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV);
13048c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc);
13058c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-fullbatt-capacity",
13068c0984e5SSebastian Reichel 					&desc->fullbatt_full_capacity);
13078c0984e5SSebastian Reichel 
13088c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-battery-stat", &battery_stat);
13098c0984e5SSebastian Reichel 	desc->battery_present = battery_stat;
13108c0984e5SSebastian Reichel 
13118c0984e5SSebastian Reichel 	/* chargers */
1312683aa86eSJonathan Bakker 	num_chgs = of_property_count_strings(np, "cm-chargers");
1313683aa86eSJonathan Bakker 	if (num_chgs > 0) {
1314a53a68ceSBaolin Wang 		int i;
1315a53a68ceSBaolin Wang 
13168c0984e5SSebastian Reichel 		/* Allocate empty bin at the tail of array */
1317a86854d0SKees Cook 		desc->psy_charger_stat = devm_kcalloc(dev,
1318a86854d0SKees Cook 						      num_chgs + 1,
1319a86854d0SKees Cook 						      sizeof(char *),
1320a86854d0SKees Cook 						      GFP_KERNEL);
1321a53a68ceSBaolin Wang 		if (!desc->psy_charger_stat)
1322a53a68ceSBaolin Wang 			return ERR_PTR(-ENOMEM);
1323a53a68ceSBaolin Wang 
13248c0984e5SSebastian Reichel 		for (i = 0; i < num_chgs; i++)
13258c0984e5SSebastian Reichel 			of_property_read_string_index(np, "cm-chargers",
13268c0984e5SSebastian Reichel 						      i, &desc->psy_charger_stat[i]);
13278c0984e5SSebastian Reichel 	}
13288c0984e5SSebastian Reichel 
13298c0984e5SSebastian Reichel 	of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
13308c0984e5SSebastian Reichel 
13318c0984e5SSebastian Reichel 	of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone);
13328c0984e5SSebastian Reichel 
13338c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-battery-cold", &desc->temp_min);
133483425c83SRob Herring 	if (of_property_read_bool(np, "cm-battery-cold-in-minus"))
13358c0984e5SSebastian Reichel 		desc->temp_min *= -1;
13368c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-battery-hot", &desc->temp_max);
13378c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff);
13388c0984e5SSebastian Reichel 
13398c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-charging-max",
13408c0984e5SSebastian Reichel 				&desc->charging_max_duration_ms);
13418c0984e5SSebastian Reichel 	of_property_read_u32(np, "cm-discharging-max",
13428c0984e5SSebastian Reichel 				&desc->discharging_max_duration_ms);
13438c0984e5SSebastian Reichel 
134402276af2SKrzysztof Kozlowski 	/* battery charger regulators */
13458c0984e5SSebastian Reichel 	desc->num_charger_regulators = of_get_child_count(np);
13468c0984e5SSebastian Reichel 	if (desc->num_charger_regulators) {
13478c0984e5SSebastian Reichel 		struct charger_regulator *chg_regs;
13488c0984e5SSebastian Reichel 		struct device_node *child;
13498c0984e5SSebastian Reichel 
1350a86854d0SKees Cook 		chg_regs = devm_kcalloc(dev,
1351a86854d0SKees Cook 					desc->num_charger_regulators,
1352a86854d0SKees Cook 					sizeof(*chg_regs),
13538c0984e5SSebastian Reichel 					GFP_KERNEL);
13548c0984e5SSebastian Reichel 		if (!chg_regs)
13558c0984e5SSebastian Reichel 			return ERR_PTR(-ENOMEM);
13568c0984e5SSebastian Reichel 
13578c0984e5SSebastian Reichel 		desc->charger_regulators = chg_regs;
13588c0984e5SSebastian Reichel 
1359157ba1bbSSebastian Reichel 		desc->sysfs_groups = devm_kcalloc(dev,
1360157ba1bbSSebastian Reichel 					desc->num_charger_regulators + 1,
1361157ba1bbSSebastian Reichel 					sizeof(*desc->sysfs_groups),
1362157ba1bbSSebastian Reichel 					GFP_KERNEL);
1363157ba1bbSSebastian Reichel 		if (!desc->sysfs_groups)
1364157ba1bbSSebastian Reichel 			return ERR_PTR(-ENOMEM);
1365157ba1bbSSebastian Reichel 
13668c0984e5SSebastian Reichel 		for_each_child_of_node(np, child) {
13678c0984e5SSebastian Reichel 			struct charger_cable *cables;
13688c0984e5SSebastian Reichel 			struct device_node *_child;
13698c0984e5SSebastian Reichel 
13708c0984e5SSebastian Reichel 			of_property_read_string(child, "cm-regulator-name",
13718c0984e5SSebastian Reichel 					&chg_regs->regulator_name);
13728c0984e5SSebastian Reichel 
13738c0984e5SSebastian Reichel 			/* charger cables */
13748c0984e5SSebastian Reichel 			chg_regs->num_cables = of_get_child_count(child);
13758c0984e5SSebastian Reichel 			if (chg_regs->num_cables) {
1376a86854d0SKees Cook 				cables = devm_kcalloc(dev,
1377a86854d0SKees Cook 						      chg_regs->num_cables,
1378a86854d0SKees Cook 						      sizeof(*cables),
13798c0984e5SSebastian Reichel 						      GFP_KERNEL);
13808c0984e5SSebastian Reichel 				if (!cables) {
13818c0984e5SSebastian Reichel 					of_node_put(child);
13828c0984e5SSebastian Reichel 					return ERR_PTR(-ENOMEM);
13838c0984e5SSebastian Reichel 				}
13848c0984e5SSebastian Reichel 
13858c0984e5SSebastian Reichel 				chg_regs->cables = cables;
13868c0984e5SSebastian Reichel 
13878c0984e5SSebastian Reichel 				for_each_child_of_node(child, _child) {
13888c0984e5SSebastian Reichel 					of_property_read_string(_child,
13898c0984e5SSebastian Reichel 					"cm-cable-name", &cables->name);
13908c0984e5SSebastian Reichel 					of_property_read_string(_child,
13918c0984e5SSebastian Reichel 					"cm-cable-extcon",
13928c0984e5SSebastian Reichel 					&cables->extcon_name);
13938c0984e5SSebastian Reichel 					of_property_read_u32(_child,
13948c0984e5SSebastian Reichel 					"cm-cable-min",
13958c0984e5SSebastian Reichel 					&cables->min_uA);
13968c0984e5SSebastian Reichel 					of_property_read_u32(_child,
13978c0984e5SSebastian Reichel 					"cm-cable-max",
13988c0984e5SSebastian Reichel 					&cables->max_uA);
13998c0984e5SSebastian Reichel 					cables++;
14008c0984e5SSebastian Reichel 				}
14018c0984e5SSebastian Reichel 			}
14028c0984e5SSebastian Reichel 			chg_regs++;
14038c0984e5SSebastian Reichel 		}
14048c0984e5SSebastian Reichel 	}
14058c0984e5SSebastian Reichel 	return desc;
14068c0984e5SSebastian Reichel }
14078c0984e5SSebastian Reichel 
cm_get_drv_data(struct platform_device * pdev)14088c0984e5SSebastian Reichel static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
14098c0984e5SSebastian Reichel {
14108c0984e5SSebastian Reichel 	if (pdev->dev.of_node)
14118c0984e5SSebastian Reichel 		return of_cm_parse_desc(&pdev->dev);
14128c0984e5SSebastian Reichel 	return dev_get_platdata(&pdev->dev);
14138c0984e5SSebastian Reichel }
14148c0984e5SSebastian Reichel 
cm_timer_func(struct alarm * alarm,ktime_t now)14158c0984e5SSebastian Reichel static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
14168c0984e5SSebastian Reichel {
14178c0984e5SSebastian Reichel 	cm_timer_set = false;
14188c0984e5SSebastian Reichel 	return ALARMTIMER_NORESTART;
14198c0984e5SSebastian Reichel }
14208c0984e5SSebastian Reichel 
charger_manager_probe(struct platform_device * pdev)14218c0984e5SSebastian Reichel static int charger_manager_probe(struct platform_device *pdev)
14228c0984e5SSebastian Reichel {
14238c0984e5SSebastian Reichel 	struct charger_desc *desc = cm_get_drv_data(pdev);
14248c0984e5SSebastian Reichel 	struct charger_manager *cm;
1425dc6ea7d4SAndi Shyti 	int ret, i = 0;
14268c0984e5SSebastian Reichel 	union power_supply_propval val;
14278c0984e5SSebastian Reichel 	struct power_supply *fuel_gauge;
14284cb38258SSebastian Reichel 	enum power_supply_property *properties;
14294cb38258SSebastian Reichel 	size_t num_properties;
14308c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
14318c0984e5SSebastian Reichel 
14328c0984e5SSebastian Reichel 	if (IS_ERR(desc)) {
14338c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "No platform data (desc) found\n");
1434f25a646fSBaolin Wang 		return PTR_ERR(desc);
14358c0984e5SSebastian Reichel 	}
14368c0984e5SSebastian Reichel 
143718a89d5cSChristophe JAILLET 	cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
14388c0984e5SSebastian Reichel 	if (!cm)
14398c0984e5SSebastian Reichel 		return -ENOMEM;
14408c0984e5SSebastian Reichel 
14418c0984e5SSebastian Reichel 	/* Basic Values. Unspecified are Null or 0 */
14428c0984e5SSebastian Reichel 	cm->dev = &pdev->dev;
14438c0984e5SSebastian Reichel 	cm->desc = desc;
14448c0984e5SSebastian Reichel 	psy_cfg.drv_data = cm;
14458c0984e5SSebastian Reichel 
14468c0984e5SSebastian Reichel 	/* Initialize alarm timer */
14478c0984e5SSebastian Reichel 	if (alarmtimer_get_rtcdev()) {
14488c0984e5SSebastian Reichel 		cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
1449096fc160SChristophe JAILLET 		if (!cm_timer)
1450096fc160SChristophe JAILLET 			return -ENOMEM;
14518c0984e5SSebastian Reichel 		alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
14528c0984e5SSebastian Reichel 	}
14538c0984e5SSebastian Reichel 
14548c0984e5SSebastian Reichel 	/*
14550299484eSChristophe JAILLET 	 * Some of the following do not need to be errors.
14560299484eSChristophe JAILLET 	 * Users may intentionally ignore those features.
14578c0984e5SSebastian Reichel 	 */
14588c0984e5SSebastian Reichel 	if (desc->fullbatt_uV == 0) {
14598c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
14608c0984e5SSebastian Reichel 	}
14619584051fSJonghwa Lee 	if (!desc->fullbatt_vchkdrop_uV) {
14628c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n");
14638c0984e5SSebastian Reichel 		desc->fullbatt_vchkdrop_uV = 0;
14648c0984e5SSebastian Reichel 	}
14658c0984e5SSebastian Reichel 	if (desc->fullbatt_soc == 0) {
14668c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n");
14678c0984e5SSebastian Reichel 	}
14688c0984e5SSebastian Reichel 	if (desc->fullbatt_full_capacity == 0) {
14698c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n");
14708c0984e5SSebastian Reichel 	}
14718c0984e5SSebastian Reichel 
14728c0984e5SSebastian Reichel 	if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
14738c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "charger_regulators undefined\n");
14748c0984e5SSebastian Reichel 		return -EINVAL;
14758c0984e5SSebastian Reichel 	}
14768c0984e5SSebastian Reichel 
14778c0984e5SSebastian Reichel 	if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
14788c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "No power supply defined\n");
14798c0984e5SSebastian Reichel 		return -EINVAL;
14808c0984e5SSebastian Reichel 	}
14818c0984e5SSebastian Reichel 
14828c0984e5SSebastian Reichel 	if (!desc->psy_fuel_gauge) {
14838c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
14848c0984e5SSebastian Reichel 		return -EINVAL;
14858c0984e5SSebastian Reichel 	}
14868c0984e5SSebastian Reichel 
14878c0984e5SSebastian Reichel 	/* Check if charger's supplies are present at probe */
14888c0984e5SSebastian Reichel 	for (i = 0; desc->psy_charger_stat[i]; i++) {
14898c0984e5SSebastian Reichel 		struct power_supply *psy;
14908c0984e5SSebastian Reichel 
14918c0984e5SSebastian Reichel 		psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
14928c0984e5SSebastian Reichel 		if (!psy) {
14938c0984e5SSebastian Reichel 			dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
14948c0984e5SSebastian Reichel 				desc->psy_charger_stat[i]);
14958c0984e5SSebastian Reichel 			return -ENODEV;
14968c0984e5SSebastian Reichel 		}
14978c0984e5SSebastian Reichel 		power_supply_put(psy);
14988c0984e5SSebastian Reichel 	}
14998c0984e5SSebastian Reichel 
1500111242d6SLadislav Michl 	if (cm->desc->polling_mode != CM_POLL_DISABLE &&
1501111242d6SLadislav Michl 	    (desc->polling_interval_ms == 0 ||
1502111242d6SLadislav Michl 	     msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL)) {
15038c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "polling_interval_ms is too small\n");
15048c0984e5SSebastian Reichel 		return -EINVAL;
15058c0984e5SSebastian Reichel 	}
15068c0984e5SSebastian Reichel 
15078c0984e5SSebastian Reichel 	if (!desc->charging_max_duration_ms ||
15088c0984e5SSebastian Reichel 			!desc->discharging_max_duration_ms) {
15098c0984e5SSebastian Reichel 		dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n");
15108c0984e5SSebastian Reichel 		desc->charging_max_duration_ms = 0;
15118c0984e5SSebastian Reichel 		desc->discharging_max_duration_ms = 0;
15128c0984e5SSebastian Reichel 	}
15138c0984e5SSebastian Reichel 
15148c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, cm);
15158c0984e5SSebastian Reichel 
15168c0984e5SSebastian Reichel 	memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
15178c0984e5SSebastian Reichel 
15188c0984e5SSebastian Reichel 	if (!desc->psy_name)
1519*e1402bd2SJustin Stitt 		strscpy(cm->psy_name_buf, psy_default.name,
1520*e1402bd2SJustin Stitt 			sizeof(cm->psy_name_buf));
15218c0984e5SSebastian Reichel 	else
1522*e1402bd2SJustin Stitt 		strscpy(cm->psy_name_buf, desc->psy_name,
1523*e1402bd2SJustin Stitt 			sizeof(cm->psy_name_buf));
15248c0984e5SSebastian Reichel 	cm->charger_psy_desc.name = cm->psy_name_buf;
15258c0984e5SSebastian Reichel 
15268c0984e5SSebastian Reichel 	/* Allocate for psy properties because they may vary */
15274cb38258SSebastian Reichel 	properties = devm_kcalloc(&pdev->dev,
1528a86854d0SKees Cook 			     ARRAY_SIZE(default_charger_props) +
1529a86854d0SKees Cook 				NUM_CHARGER_PSY_OPTIONAL,
15304cb38258SSebastian Reichel 			     sizeof(*properties), GFP_KERNEL);
15314cb38258SSebastian Reichel 	if (!properties)
15328c0984e5SSebastian Reichel 		return -ENOMEM;
15338c0984e5SSebastian Reichel 
15344cb38258SSebastian Reichel 	memcpy(properties, default_charger_props,
15358c0984e5SSebastian Reichel 		sizeof(enum power_supply_property) *
15368c0984e5SSebastian Reichel 		ARRAY_SIZE(default_charger_props));
153797ed79f4SMichał Mirosław 	num_properties = ARRAY_SIZE(default_charger_props);
15388c0984e5SSebastian Reichel 
15398c0984e5SSebastian Reichel 	/* Find which optional psy-properties are available */
15408c0984e5SSebastian Reichel 	fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
15418c0984e5SSebastian Reichel 	if (!fuel_gauge) {
15428c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
15438c0984e5SSebastian Reichel 			desc->psy_fuel_gauge);
15448c0984e5SSebastian Reichel 		return -ENODEV;
15458c0984e5SSebastian Reichel 	}
15468c0984e5SSebastian Reichel 	if (!power_supply_get_property(fuel_gauge,
15470a9e0f94SJonghwa Lee 					POWER_SUPPLY_PROP_CHARGE_FULL, &val)) {
15480a9e0f94SJonghwa Lee 		properties[num_properties] =
15490a9e0f94SJonghwa Lee 				POWER_SUPPLY_PROP_CHARGE_FULL;
15500a9e0f94SJonghwa Lee 		num_properties++;
15510a9e0f94SJonghwa Lee 	}
15520a9e0f94SJonghwa Lee 	if (!power_supply_get_property(fuel_gauge,
15538c0984e5SSebastian Reichel 					  POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
15544cb38258SSebastian Reichel 		properties[num_properties] =
15558c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_CHARGE_NOW;
15564cb38258SSebastian Reichel 		num_properties++;
15578c0984e5SSebastian Reichel 	}
15588c0984e5SSebastian Reichel 	if (!power_supply_get_property(fuel_gauge,
15598c0984e5SSebastian Reichel 					  POWER_SUPPLY_PROP_CURRENT_NOW,
15608c0984e5SSebastian Reichel 					  &val)) {
15614cb38258SSebastian Reichel 		properties[num_properties] =
15628c0984e5SSebastian Reichel 				POWER_SUPPLY_PROP_CURRENT_NOW;
15634cb38258SSebastian Reichel 		num_properties++;
15648c0984e5SSebastian Reichel 	}
15658c0984e5SSebastian Reichel 
15664cb38258SSebastian Reichel 	ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties);
15678c0984e5SSebastian Reichel 	if (ret) {
15688c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "Failed to initialize thermal data\n");
15698c0984e5SSebastian Reichel 		cm->desc->measure_battery_temp = false;
15708c0984e5SSebastian Reichel 	}
15718c0984e5SSebastian Reichel 	power_supply_put(fuel_gauge);
15728c0984e5SSebastian Reichel 
15734cb38258SSebastian Reichel 	cm->charger_psy_desc.properties = properties;
15744cb38258SSebastian Reichel 	cm->charger_psy_desc.num_properties = num_properties;
15754cb38258SSebastian Reichel 
1576157ba1bbSSebastian Reichel 	/* Register sysfs entry for charger(regulator) */
1577157ba1bbSSebastian Reichel 	ret = charger_manager_prepare_sysfs(cm);
1578157ba1bbSSebastian Reichel 	if (ret < 0) {
1579157ba1bbSSebastian Reichel 		dev_err(&pdev->dev,
1580157ba1bbSSebastian Reichel 			"Cannot prepare sysfs entry of regulators\n");
1581157ba1bbSSebastian Reichel 		return ret;
1582157ba1bbSSebastian Reichel 	}
1583157ba1bbSSebastian Reichel 	psy_cfg.attr_grp = desc->sysfs_groups;
1584157ba1bbSSebastian Reichel 
15858c0984e5SSebastian Reichel 	cm->charger_psy = power_supply_register(&pdev->dev,
15868c0984e5SSebastian Reichel 						&cm->charger_psy_desc,
15878c0984e5SSebastian Reichel 						&psy_cfg);
15888c0984e5SSebastian Reichel 	if (IS_ERR(cm->charger_psy)) {
15898c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
15908c0984e5SSebastian Reichel 			cm->charger_psy_desc.name);
15918c0984e5SSebastian Reichel 		return PTR_ERR(cm->charger_psy);
15928c0984e5SSebastian Reichel 	}
15938c0984e5SSebastian Reichel 
15948c0984e5SSebastian Reichel 	/* Register extcon device for charger cable */
15958c0984e5SSebastian Reichel 	ret = charger_manager_register_extcon(cm);
15968c0984e5SSebastian Reichel 	if (ret < 0) {
15978c0984e5SSebastian Reichel 		dev_err(&pdev->dev, "Cannot initialize extcon device\n");
15988c0984e5SSebastian Reichel 		goto err_reg_extcon;
15998c0984e5SSebastian Reichel 	}
16008c0984e5SSebastian Reichel 
16018c0984e5SSebastian Reichel 	/* Add to the list */
16028c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
16038c0984e5SSebastian Reichel 	list_add(&cm->entry, &cm_list);
16048c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
16058c0984e5SSebastian Reichel 
16068c0984e5SSebastian Reichel 	/*
160731ba6fadSBhaskar Chowdhury 	 * Charger-manager is capable of waking up the system from sleep
16088c13b6f1SBaolin Wang 	 * when event is happened through cm_notify_event()
16098c0984e5SSebastian Reichel 	 */
16108c0984e5SSebastian Reichel 	device_init_wakeup(&pdev->dev, true);
16118c0984e5SSebastian Reichel 	device_set_wakeup_capable(&pdev->dev, false);
16128c0984e5SSebastian Reichel 
16138c0984e5SSebastian Reichel 	/*
16148c0984e5SSebastian Reichel 	 * Charger-manager have to check the charging state right after
161502276af2SKrzysztof Kozlowski 	 * initialization of charger-manager and then update current charging
16168c0984e5SSebastian Reichel 	 * state.
16178c0984e5SSebastian Reichel 	 */
16188c0984e5SSebastian Reichel 	cm_monitor();
16198c0984e5SSebastian Reichel 
16208c0984e5SSebastian Reichel 	schedule_work(&setup_polling);
16218c0984e5SSebastian Reichel 
16228c0984e5SSebastian Reichel 	return 0;
16238c0984e5SSebastian Reichel 
16248c0984e5SSebastian Reichel err_reg_extcon:
1625c22b90dbSKrzysztof Kozlowski 	for (i = 0; i < desc->num_charger_regulators; i++)
16268c0984e5SSebastian Reichel 		regulator_put(desc->charger_regulators[i].consumer);
16278c0984e5SSebastian Reichel 
16288c0984e5SSebastian Reichel 	power_supply_unregister(cm->charger_psy);
16298c0984e5SSebastian Reichel 
16308c0984e5SSebastian Reichel 	return ret;
16318c0984e5SSebastian Reichel }
16328c0984e5SSebastian Reichel 
charger_manager_remove(struct platform_device * pdev)1633403eebf9SUwe Kleine-König static void charger_manager_remove(struct platform_device *pdev)
16348c0984e5SSebastian Reichel {
16358c0984e5SSebastian Reichel 	struct charger_manager *cm = platform_get_drvdata(pdev);
16368c0984e5SSebastian Reichel 	struct charger_desc *desc = cm->desc;
16378c0984e5SSebastian Reichel 	int i = 0;
16388c0984e5SSebastian Reichel 
16398c0984e5SSebastian Reichel 	/* Remove from the list */
16408c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
16418c0984e5SSebastian Reichel 	list_del(&cm->entry);
16428c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
16438c0984e5SSebastian Reichel 
16448c0984e5SSebastian Reichel 	cancel_work_sync(&setup_polling);
16458c0984e5SSebastian Reichel 	cancel_delayed_work_sync(&cm_monitor_work);
16468c0984e5SSebastian Reichel 
16478c0984e5SSebastian Reichel 	for (i = 0 ; i < desc->num_charger_regulators ; i++)
16488c0984e5SSebastian Reichel 		regulator_put(desc->charger_regulators[i].consumer);
16498c0984e5SSebastian Reichel 
16508c0984e5SSebastian Reichel 	power_supply_unregister(cm->charger_psy);
16518c0984e5SSebastian Reichel 
16528c0984e5SSebastian Reichel 	try_charger_enable(cm, false);
16538c0984e5SSebastian Reichel }
16548c0984e5SSebastian Reichel 
16558c0984e5SSebastian Reichel static const struct platform_device_id charger_manager_id[] = {
16568c0984e5SSebastian Reichel 	{ "charger-manager", 0 },
16578c0984e5SSebastian Reichel 	{ },
16588c0984e5SSebastian Reichel };
16598c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(platform, charger_manager_id);
16608c0984e5SSebastian Reichel 
cm_suspend_noirq(struct device * dev)16618c0984e5SSebastian Reichel static int cm_suspend_noirq(struct device *dev)
16628c0984e5SSebastian Reichel {
16638c0984e5SSebastian Reichel 	if (device_may_wakeup(dev)) {
16648c0984e5SSebastian Reichel 		device_set_wakeup_capable(dev, false);
1665dc6ea7d4SAndi Shyti 		return -EAGAIN;
16668c0984e5SSebastian Reichel 	}
16678c0984e5SSebastian Reichel 
1668dc6ea7d4SAndi Shyti 	return 0;
16698c0984e5SSebastian Reichel }
16708c0984e5SSebastian Reichel 
cm_need_to_awake(void)16718c0984e5SSebastian Reichel static bool cm_need_to_awake(void)
16728c0984e5SSebastian Reichel {
16738c0984e5SSebastian Reichel 	struct charger_manager *cm;
16748c0984e5SSebastian Reichel 
16758c0984e5SSebastian Reichel 	if (cm_timer)
16768c0984e5SSebastian Reichel 		return false;
16778c0984e5SSebastian Reichel 
16788c0984e5SSebastian Reichel 	mutex_lock(&cm_list_mtx);
16798c0984e5SSebastian Reichel 	list_for_each_entry(cm, &cm_list, entry) {
16808c0984e5SSebastian Reichel 		if (is_charging(cm)) {
16818c0984e5SSebastian Reichel 			mutex_unlock(&cm_list_mtx);
16828c0984e5SSebastian Reichel 			return true;
16838c0984e5SSebastian Reichel 		}
16848c0984e5SSebastian Reichel 	}
16858c0984e5SSebastian Reichel 	mutex_unlock(&cm_list_mtx);
16868c0984e5SSebastian Reichel 
16878c0984e5SSebastian Reichel 	return false;
16888c0984e5SSebastian Reichel }
16898c0984e5SSebastian Reichel 
cm_suspend_prepare(struct device * dev)16908c0984e5SSebastian Reichel static int cm_suspend_prepare(struct device *dev)
16918c0984e5SSebastian Reichel {
16928c0984e5SSebastian Reichel 	if (cm_need_to_awake())
16938c0984e5SSebastian Reichel 		return -EBUSY;
16948c0984e5SSebastian Reichel 
16958c0984e5SSebastian Reichel 	if (!cm_suspended)
16968c0984e5SSebastian Reichel 		cm_suspended = true;
16978c0984e5SSebastian Reichel 
16988c0984e5SSebastian Reichel 	cm_timer_set = cm_setup_timer();
16998c0984e5SSebastian Reichel 
17008c0984e5SSebastian Reichel 	if (cm_timer_set) {
17018c0984e5SSebastian Reichel 		cancel_work_sync(&setup_polling);
17028c0984e5SSebastian Reichel 		cancel_delayed_work_sync(&cm_monitor_work);
17038c0984e5SSebastian Reichel 	}
17048c0984e5SSebastian Reichel 
17058c0984e5SSebastian Reichel 	return 0;
17068c0984e5SSebastian Reichel }
17078c0984e5SSebastian Reichel 
cm_suspend_complete(struct device * dev)17088c0984e5SSebastian Reichel static void cm_suspend_complete(struct device *dev)
17098c0984e5SSebastian Reichel {
17108c0984e5SSebastian Reichel 	struct charger_manager *cm = dev_get_drvdata(dev);
17118c0984e5SSebastian Reichel 
17128c0984e5SSebastian Reichel 	if (cm_suspended)
17138c0984e5SSebastian Reichel 		cm_suspended = false;
17148c0984e5SSebastian Reichel 
17158c0984e5SSebastian Reichel 	if (cm_timer_set) {
17168c0984e5SSebastian Reichel 		ktime_t remain;
17178c0984e5SSebastian Reichel 
17188c0984e5SSebastian Reichel 		alarm_cancel(cm_timer);
17198c0984e5SSebastian Reichel 		cm_timer_set = false;
17208c0984e5SSebastian Reichel 		remain = alarm_expires_remaining(cm_timer);
17218c0984e5SSebastian Reichel 		cm_suspend_duration_ms -= ktime_to_ms(remain);
17228c0984e5SSebastian Reichel 		schedule_work(&setup_polling);
17238c0984e5SSebastian Reichel 	}
17248c0984e5SSebastian Reichel 
17258c0984e5SSebastian Reichel 	_cm_monitor(cm);
17268c0984e5SSebastian Reichel 
17278c0984e5SSebastian Reichel 	device_set_wakeup_capable(cm->dev, false);
17288c0984e5SSebastian Reichel }
17298c0984e5SSebastian Reichel 
17308c0984e5SSebastian Reichel static const struct dev_pm_ops charger_manager_pm = {
17318c0984e5SSebastian Reichel 	.prepare	= cm_suspend_prepare,
17328c0984e5SSebastian Reichel 	.suspend_noirq	= cm_suspend_noirq,
17338c0984e5SSebastian Reichel 	.complete	= cm_suspend_complete,
17348c0984e5SSebastian Reichel };
17358c0984e5SSebastian Reichel 
17368c0984e5SSebastian Reichel static struct platform_driver charger_manager_driver = {
17378c0984e5SSebastian Reichel 	.driver = {
17388c0984e5SSebastian Reichel 		.name = "charger-manager",
17398c0984e5SSebastian Reichel 		.pm = &charger_manager_pm,
17408c0984e5SSebastian Reichel 		.of_match_table = charger_manager_match,
17418c0984e5SSebastian Reichel 	},
17428c0984e5SSebastian Reichel 	.probe = charger_manager_probe,
1743403eebf9SUwe Kleine-König 	.remove_new = charger_manager_remove,
17448c0984e5SSebastian Reichel 	.id_table = charger_manager_id,
17458c0984e5SSebastian Reichel };
17468c0984e5SSebastian Reichel 
charger_manager_init(void)17478c0984e5SSebastian Reichel static int __init charger_manager_init(void)
17488c0984e5SSebastian Reichel {
17498c0984e5SSebastian Reichel 	cm_wq = create_freezable_workqueue("charger_manager");
175075cf4f5aSKangjie Lu 	if (unlikely(!cm_wq))
175175cf4f5aSKangjie Lu 		return -ENOMEM;
175275cf4f5aSKangjie Lu 
17538c0984e5SSebastian Reichel 	INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
17548c0984e5SSebastian Reichel 
17558c0984e5SSebastian Reichel 	return platform_driver_register(&charger_manager_driver);
17568c0984e5SSebastian Reichel }
17578c0984e5SSebastian Reichel late_initcall(charger_manager_init);
17588c0984e5SSebastian Reichel 
charger_manager_cleanup(void)17598c0984e5SSebastian Reichel static void __exit charger_manager_cleanup(void)
17608c0984e5SSebastian Reichel {
17618c0984e5SSebastian Reichel 	destroy_workqueue(cm_wq);
17628c0984e5SSebastian Reichel 	cm_wq = NULL;
17638c0984e5SSebastian Reichel 
17648c0984e5SSebastian Reichel 	platform_driver_unregister(&charger_manager_driver);
17658c0984e5SSebastian Reichel }
17668c0984e5SSebastian Reichel module_exit(charger_manager_cleanup);
17678c0984e5SSebastian Reichel 
17688c0984e5SSebastian Reichel MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
17698c0984e5SSebastian Reichel MODULE_DESCRIPTION("Charger Manager");
17708c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
1771