162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This driver enables to monitor battery health and control charger
762306a36Sopenharmony_ci * during suspend-to-mem.
862306a36Sopenharmony_ci * Charger manager depends on other devices. Register this later than
962306a36Sopenharmony_ci * the depending devices.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci**/
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/rtc.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/workqueue.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/power/charger-manager.h>
2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2562306a36Sopenharmony_ci#include <linux/sysfs.h>
2662306a36Sopenharmony_ci#include <linux/of.h>
2762306a36Sopenharmony_ci#include <linux/thermal.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct {
3062306a36Sopenharmony_ci	const char *name;
3162306a36Sopenharmony_ci	u64 extcon_type;
3262306a36Sopenharmony_ci} extcon_mapping[] = {
3362306a36Sopenharmony_ci	/* Current textual representations */
3462306a36Sopenharmony_ci	{ "USB", EXTCON_USB },
3562306a36Sopenharmony_ci	{ "USB-HOST", EXTCON_USB_HOST },
3662306a36Sopenharmony_ci	{ "SDP", EXTCON_CHG_USB_SDP },
3762306a36Sopenharmony_ci	{ "DCP", EXTCON_CHG_USB_DCP },
3862306a36Sopenharmony_ci	{ "CDP", EXTCON_CHG_USB_CDP },
3962306a36Sopenharmony_ci	{ "ACA", EXTCON_CHG_USB_ACA },
4062306a36Sopenharmony_ci	{ "FAST-CHARGER", EXTCON_CHG_USB_FAST },
4162306a36Sopenharmony_ci	{ "SLOW-CHARGER", EXTCON_CHG_USB_SLOW },
4262306a36Sopenharmony_ci	{ "WPT", EXTCON_CHG_WPT },
4362306a36Sopenharmony_ci	{ "PD", EXTCON_CHG_USB_PD },
4462306a36Sopenharmony_ci	{ "DOCK", EXTCON_DOCK },
4562306a36Sopenharmony_ci	{ "JIG", EXTCON_JIG },
4662306a36Sopenharmony_ci	{ "MECHANICAL", EXTCON_MECHANICAL },
4762306a36Sopenharmony_ci	/* Deprecated textual representations */
4862306a36Sopenharmony_ci	{ "TA", EXTCON_CHG_USB_SDP },
4962306a36Sopenharmony_ci	{ "CHARGE-DOWNSTREAM", EXTCON_CHG_USB_CDP },
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * Default temperature threshold for charging.
5462306a36Sopenharmony_ci * Every temperature units are in tenth of centigrade.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci#define CM_DEFAULT_RECHARGE_TEMP_DIFF	50
5762306a36Sopenharmony_ci#define CM_DEFAULT_CHARGE_TEMP_MAX	500
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
6162306a36Sopenharmony_ci * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
6262306a36Sopenharmony_ci * without any delays.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci#define	CM_JIFFIES_SMALL	(2)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* If y is valid (> 0) and smaller than x, do x = y */
6762306a36Sopenharmony_ci#define CM_MIN_VALID(x, y)	x = (((y > 0) && ((x) > (y))) ? (y) : (x))
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
7162306a36Sopenharmony_ci * rtc alarm. It should be 2 or larger
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci#define CM_RTC_SMALL		(2)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic LIST_HEAD(cm_list);
7662306a36Sopenharmony_cistatic DEFINE_MUTEX(cm_list_mtx);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* About in-suspend (suspend-again) monitoring */
7962306a36Sopenharmony_cistatic struct alarm *cm_timer;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic bool cm_suspended;
8262306a36Sopenharmony_cistatic bool cm_timer_set;
8362306a36Sopenharmony_cistatic unsigned long cm_suspend_duration_ms;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* About normal (not suspended) monitoring */
8662306a36Sopenharmony_cistatic unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
8762306a36Sopenharmony_cistatic unsigned long next_polling; /* Next appointed polling time */
8862306a36Sopenharmony_cistatic struct workqueue_struct *cm_wq; /* init at driver add */
8962306a36Sopenharmony_cistatic struct delayed_work cm_monitor_work; /* init at driver add */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/**
9262306a36Sopenharmony_ci * is_batt_present - See if the battery presents in place.
9362306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_cistatic bool is_batt_present(struct charger_manager *cm)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	union power_supply_propval val;
9862306a36Sopenharmony_ci	struct power_supply *psy;
9962306a36Sopenharmony_ci	bool present = false;
10062306a36Sopenharmony_ci	int i, ret;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	switch (cm->desc->battery_present) {
10362306a36Sopenharmony_ci	case CM_BATTERY_PRESENT:
10462306a36Sopenharmony_ci		present = true;
10562306a36Sopenharmony_ci		break;
10662306a36Sopenharmony_ci	case CM_NO_BATTERY:
10762306a36Sopenharmony_ci		break;
10862306a36Sopenharmony_ci	case CM_FUEL_GAUGE:
10962306a36Sopenharmony_ci		psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
11062306a36Sopenharmony_ci		if (!psy)
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
11462306a36Sopenharmony_ci				&val);
11562306a36Sopenharmony_ci		if (ret == 0 && val.intval)
11662306a36Sopenharmony_ci			present = true;
11762306a36Sopenharmony_ci		power_supply_put(psy);
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	case CM_CHARGER_STAT:
12062306a36Sopenharmony_ci		for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
12162306a36Sopenharmony_ci			psy = power_supply_get_by_name(
12262306a36Sopenharmony_ci					cm->desc->psy_charger_stat[i]);
12362306a36Sopenharmony_ci			if (!psy) {
12462306a36Sopenharmony_ci				dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
12562306a36Sopenharmony_ci					cm->desc->psy_charger_stat[i]);
12662306a36Sopenharmony_ci				continue;
12762306a36Sopenharmony_ci			}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci			ret = power_supply_get_property(psy,
13062306a36Sopenharmony_ci				POWER_SUPPLY_PROP_PRESENT, &val);
13162306a36Sopenharmony_ci			power_supply_put(psy);
13262306a36Sopenharmony_ci			if (ret == 0 && val.intval) {
13362306a36Sopenharmony_ci				present = true;
13462306a36Sopenharmony_ci				break;
13562306a36Sopenharmony_ci			}
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return present;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci * is_ext_pwr_online - See if an external power source is attached to charge
14562306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * Returns true if at least one of the chargers of the battery has an external
14862306a36Sopenharmony_ci * power source attached to charge the battery regardless of whether it is
14962306a36Sopenharmony_ci * actually charging or not.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic bool is_ext_pwr_online(struct charger_manager *cm)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	union power_supply_propval val;
15462306a36Sopenharmony_ci	struct power_supply *psy;
15562306a36Sopenharmony_ci	bool online = false;
15662306a36Sopenharmony_ci	int i, ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* If at least one of them has one, it's yes. */
15962306a36Sopenharmony_ci	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
16062306a36Sopenharmony_ci		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
16162306a36Sopenharmony_ci		if (!psy) {
16262306a36Sopenharmony_ci			dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
16362306a36Sopenharmony_ci					cm->desc->psy_charger_stat[i]);
16462306a36Sopenharmony_ci			continue;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
16862306a36Sopenharmony_ci				&val);
16962306a36Sopenharmony_ci		power_supply_put(psy);
17062306a36Sopenharmony_ci		if (ret == 0 && val.intval) {
17162306a36Sopenharmony_ci			online = true;
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return online;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/**
18062306a36Sopenharmony_ci * get_batt_uV - Get the voltage level of the battery
18162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
18262306a36Sopenharmony_ci * @uV: the voltage level returned.
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * Returns 0 if there is no error.
18562306a36Sopenharmony_ci * Returns a negative value on error.
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_cistatic int get_batt_uV(struct charger_manager *cm, int *uV)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	union power_supply_propval val;
19062306a36Sopenharmony_ci	struct power_supply *fuel_gauge;
19162306a36Sopenharmony_ci	int ret;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
19462306a36Sopenharmony_ci	if (!fuel_gauge)
19562306a36Sopenharmony_ci		return -ENODEV;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = power_supply_get_property(fuel_gauge,
19862306a36Sopenharmony_ci				POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
19962306a36Sopenharmony_ci	power_supply_put(fuel_gauge);
20062306a36Sopenharmony_ci	if (ret)
20162306a36Sopenharmony_ci		return ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	*uV = val.intval;
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/**
20862306a36Sopenharmony_ci * is_charging - Returns true if the battery is being charged.
20962306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cistatic bool is_charging(struct charger_manager *cm)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int i, ret;
21462306a36Sopenharmony_ci	bool charging = false;
21562306a36Sopenharmony_ci	struct power_supply *psy;
21662306a36Sopenharmony_ci	union power_supply_propval val;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* If there is no battery, it cannot be charged */
21962306a36Sopenharmony_ci	if (!is_batt_present(cm))
22062306a36Sopenharmony_ci		return false;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* If at least one of the charger is charging, return yes */
22362306a36Sopenharmony_ci	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
22462306a36Sopenharmony_ci		/* 1. The charger sholuld not be DISABLED */
22562306a36Sopenharmony_ci		if (cm->emergency_stop)
22662306a36Sopenharmony_ci			continue;
22762306a36Sopenharmony_ci		if (!cm->charger_enabled)
22862306a36Sopenharmony_ci			continue;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
23162306a36Sopenharmony_ci		if (!psy) {
23262306a36Sopenharmony_ci			dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
23362306a36Sopenharmony_ci					cm->desc->psy_charger_stat[i]);
23462306a36Sopenharmony_ci			continue;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		/* 2. The charger should be online (ext-power) */
23862306a36Sopenharmony_ci		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
23962306a36Sopenharmony_ci				&val);
24062306a36Sopenharmony_ci		if (ret) {
24162306a36Sopenharmony_ci			dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
24262306a36Sopenharmony_ci				 cm->desc->psy_charger_stat[i]);
24362306a36Sopenharmony_ci			power_supply_put(psy);
24462306a36Sopenharmony_ci			continue;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci		if (val.intval == 0) {
24762306a36Sopenharmony_ci			power_supply_put(psy);
24862306a36Sopenharmony_ci			continue;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		/*
25262306a36Sopenharmony_ci		 * 3. The charger should not be FULL, DISCHARGING,
25362306a36Sopenharmony_ci		 * or NOT_CHARGING.
25462306a36Sopenharmony_ci		 */
25562306a36Sopenharmony_ci		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
25662306a36Sopenharmony_ci				&val);
25762306a36Sopenharmony_ci		power_supply_put(psy);
25862306a36Sopenharmony_ci		if (ret) {
25962306a36Sopenharmony_ci			dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
26062306a36Sopenharmony_ci				 cm->desc->psy_charger_stat[i]);
26162306a36Sopenharmony_ci			continue;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci		if (val.intval == POWER_SUPPLY_STATUS_FULL ||
26462306a36Sopenharmony_ci				val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
26562306a36Sopenharmony_ci				val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
26662306a36Sopenharmony_ci			continue;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		/* Then, this is charging. */
26962306a36Sopenharmony_ci		charging = true;
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return charging;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/**
27762306a36Sopenharmony_ci * is_full_charged - Returns true if the battery is fully charged.
27862306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic bool is_full_charged(struct charger_manager *cm)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
28362306a36Sopenharmony_ci	union power_supply_propval val;
28462306a36Sopenharmony_ci	struct power_supply *fuel_gauge;
28562306a36Sopenharmony_ci	bool is_full = false;
28662306a36Sopenharmony_ci	int ret = 0;
28762306a36Sopenharmony_ci	int uV;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* If there is no battery, it cannot be charged */
29062306a36Sopenharmony_ci	if (!is_batt_present(cm))
29162306a36Sopenharmony_ci		return false;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
29462306a36Sopenharmony_ci	if (!fuel_gauge)
29562306a36Sopenharmony_ci		return false;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Full, if it's over the fullbatt voltage */
29862306a36Sopenharmony_ci	if (desc->fullbatt_uV > 0) {
29962306a36Sopenharmony_ci		ret = get_batt_uV(cm, &uV);
30062306a36Sopenharmony_ci		if (!ret) {
30162306a36Sopenharmony_ci			/* Battery is already full, checks voltage drop. */
30262306a36Sopenharmony_ci			if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
30362306a36Sopenharmony_ci					&& desc->fullbatt_vchkdrop_uV)
30462306a36Sopenharmony_ci				uV += desc->fullbatt_vchkdrop_uV;
30562306a36Sopenharmony_ci			if (uV >= desc->fullbatt_uV)
30662306a36Sopenharmony_ci				return true;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (desc->fullbatt_full_capacity > 0) {
31162306a36Sopenharmony_ci		val.intval = 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* Not full if capacity of fuel gauge isn't full */
31462306a36Sopenharmony_ci		ret = power_supply_get_property(fuel_gauge,
31562306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CHARGE_FULL, &val);
31662306a36Sopenharmony_ci		if (!ret && val.intval > desc->fullbatt_full_capacity) {
31762306a36Sopenharmony_ci			is_full = true;
31862306a36Sopenharmony_ci			goto out;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Full, if the capacity is more than fullbatt_soc */
32362306a36Sopenharmony_ci	if (desc->fullbatt_soc > 0) {
32462306a36Sopenharmony_ci		val.intval = 0;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		ret = power_supply_get_property(fuel_gauge,
32762306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CAPACITY, &val);
32862306a36Sopenharmony_ci		if (!ret && val.intval >= desc->fullbatt_soc) {
32962306a36Sopenharmony_ci			is_full = true;
33062306a36Sopenharmony_ci			goto out;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciout:
33562306a36Sopenharmony_ci	power_supply_put(fuel_gauge);
33662306a36Sopenharmony_ci	return is_full;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * is_polling_required - Return true if need to continue polling for this CM.
34162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic bool is_polling_required(struct charger_manager *cm)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	switch (cm->desc->polling_mode) {
34662306a36Sopenharmony_ci	case CM_POLL_DISABLE:
34762306a36Sopenharmony_ci		return false;
34862306a36Sopenharmony_ci	case CM_POLL_ALWAYS:
34962306a36Sopenharmony_ci		return true;
35062306a36Sopenharmony_ci	case CM_POLL_EXTERNAL_POWER_ONLY:
35162306a36Sopenharmony_ci		return is_ext_pwr_online(cm);
35262306a36Sopenharmony_ci	case CM_POLL_CHARGING_ONLY:
35362306a36Sopenharmony_ci		return is_charging(cm);
35462306a36Sopenharmony_ci	default:
35562306a36Sopenharmony_ci		dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
35662306a36Sopenharmony_ci			 cm->desc->polling_mode);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return false;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/**
36362306a36Sopenharmony_ci * try_charger_enable - Enable/Disable chargers altogether
36462306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
36562306a36Sopenharmony_ci * @enable: true: enable / false: disable
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * Note that Charger Manager keeps the charger enabled regardless whether
36862306a36Sopenharmony_ci * the charger is charging or not (because battery is full or no external
36962306a36Sopenharmony_ci * power source exists) except when CM needs to disable chargers forcibly
37062306a36Sopenharmony_ci * because of emergency causes; when the battery is overheated or too cold.
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic int try_charger_enable(struct charger_manager *cm, bool enable)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	int err = 0, i;
37562306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Ignore if it's redundant command */
37862306a36Sopenharmony_ci	if (enable == cm->charger_enabled)
37962306a36Sopenharmony_ci		return 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (enable) {
38262306a36Sopenharmony_ci		if (cm->emergency_stop)
38362306a36Sopenharmony_ci			return -EAGAIN;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		/*
38662306a36Sopenharmony_ci		 * Save start time of charging to limit
38762306a36Sopenharmony_ci		 * maximum possible charging time.
38862306a36Sopenharmony_ci		 */
38962306a36Sopenharmony_ci		cm->charging_start_time = ktime_to_ms(ktime_get());
39062306a36Sopenharmony_ci		cm->charging_end_time = 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
39362306a36Sopenharmony_ci			if (desc->charger_regulators[i].externally_control)
39462306a36Sopenharmony_ci				continue;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			err = regulator_enable(desc->charger_regulators[i].consumer);
39762306a36Sopenharmony_ci			if (err < 0) {
39862306a36Sopenharmony_ci				dev_warn(cm->dev, "Cannot enable %s regulator\n",
39962306a36Sopenharmony_ci					 desc->charger_regulators[i].regulator_name);
40062306a36Sopenharmony_ci			}
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci	} else {
40362306a36Sopenharmony_ci		/*
40462306a36Sopenharmony_ci		 * Save end time of charging to maintain fully charged state
40562306a36Sopenharmony_ci		 * of battery after full-batt.
40662306a36Sopenharmony_ci		 */
40762306a36Sopenharmony_ci		cm->charging_start_time = 0;
40862306a36Sopenharmony_ci		cm->charging_end_time = ktime_to_ms(ktime_get());
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
41162306a36Sopenharmony_ci			if (desc->charger_regulators[i].externally_control)
41262306a36Sopenharmony_ci				continue;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci			err = regulator_disable(desc->charger_regulators[i].consumer);
41562306a36Sopenharmony_ci			if (err < 0) {
41662306a36Sopenharmony_ci				dev_warn(cm->dev, "Cannot disable %s regulator\n",
41762306a36Sopenharmony_ci					 desc->charger_regulators[i].regulator_name);
41862306a36Sopenharmony_ci			}
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		/*
42262306a36Sopenharmony_ci		 * Abnormal battery state - Stop charging forcibly,
42362306a36Sopenharmony_ci		 * even if charger was enabled at the other places
42462306a36Sopenharmony_ci		 */
42562306a36Sopenharmony_ci		for (i = 0; i < desc->num_charger_regulators; i++) {
42662306a36Sopenharmony_ci			if (regulator_is_enabled(
42762306a36Sopenharmony_ci				    desc->charger_regulators[i].consumer)) {
42862306a36Sopenharmony_ci				regulator_force_disable(
42962306a36Sopenharmony_ci					desc->charger_regulators[i].consumer);
43062306a36Sopenharmony_ci				dev_warn(cm->dev, "Disable regulator(%s) forcibly\n",
43162306a36Sopenharmony_ci					 desc->charger_regulators[i].regulator_name);
43262306a36Sopenharmony_ci			}
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!err)
43762306a36Sopenharmony_ci		cm->charger_enabled = enable;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return err;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/**
44362306a36Sopenharmony_ci * check_charging_duration - Monitor charging/discharging duration
44462306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci * If whole charging duration exceed 'charging_max_duration_ms',
44762306a36Sopenharmony_ci * cm stop charging to prevent overcharge/overheat. If discharging
44862306a36Sopenharmony_ci * duration exceed 'discharging _max_duration_ms', charger cable is
44962306a36Sopenharmony_ci * attached, after full-batt, cm start charging to maintain fully
45062306a36Sopenharmony_ci * charged state for battery.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_cistatic int check_charging_duration(struct charger_manager *cm)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
45562306a36Sopenharmony_ci	u64 curr = ktime_to_ms(ktime_get());
45662306a36Sopenharmony_ci	u64 duration;
45762306a36Sopenharmony_ci	int ret = false;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (!desc->charging_max_duration_ms &&
46062306a36Sopenharmony_ci			!desc->discharging_max_duration_ms)
46162306a36Sopenharmony_ci		return ret;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (cm->charger_enabled) {
46462306a36Sopenharmony_ci		duration = curr - cm->charging_start_time;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		if (duration > desc->charging_max_duration_ms) {
46762306a36Sopenharmony_ci			dev_info(cm->dev, "Charging duration exceed %ums\n",
46862306a36Sopenharmony_ci				 desc->charging_max_duration_ms);
46962306a36Sopenharmony_ci			ret = true;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	} else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
47262306a36Sopenharmony_ci		duration = curr - cm->charging_end_time;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (duration > desc->discharging_max_duration_ms) {
47562306a36Sopenharmony_ci			dev_info(cm->dev, "Discharging duration exceed %ums\n",
47662306a36Sopenharmony_ci				 desc->discharging_max_duration_ms);
47762306a36Sopenharmony_ci			ret = true;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return ret;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
48562306a36Sopenharmony_ci					int *temp)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct power_supply *fuel_gauge;
48862306a36Sopenharmony_ci	int ret;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
49162306a36Sopenharmony_ci	if (!fuel_gauge)
49262306a36Sopenharmony_ci		return -ENODEV;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ret = power_supply_get_property(fuel_gauge,
49562306a36Sopenharmony_ci				POWER_SUPPLY_PROP_TEMP,
49662306a36Sopenharmony_ci				(union power_supply_propval *)temp);
49762306a36Sopenharmony_ci	power_supply_put(fuel_gauge);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return ret;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int cm_get_battery_temperature(struct charger_manager *cm,
50362306a36Sopenharmony_ci					int *temp)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	int ret;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!cm->desc->measure_battery_temp)
50862306a36Sopenharmony_ci		return -ENODEV;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci#ifdef CONFIG_THERMAL
51162306a36Sopenharmony_ci	if (cm->tzd_batt) {
51262306a36Sopenharmony_ci		ret = thermal_zone_get_temp(cm->tzd_batt, temp);
51362306a36Sopenharmony_ci		if (!ret)
51462306a36Sopenharmony_ci			/* Calibrate temperature unit */
51562306a36Sopenharmony_ci			*temp /= 100;
51662306a36Sopenharmony_ci	} else
51762306a36Sopenharmony_ci#endif
51862306a36Sopenharmony_ci	{
51962306a36Sopenharmony_ci		/* if-else continued from CONFIG_THERMAL */
52062306a36Sopenharmony_ci		ret = cm_get_battery_temperature_by_psy(cm, temp);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return ret;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int cm_check_thermal_status(struct charger_manager *cm)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
52962306a36Sopenharmony_ci	int temp, upper_limit, lower_limit;
53062306a36Sopenharmony_ci	int ret = 0;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ret = cm_get_battery_temperature(cm, &temp);
53362306a36Sopenharmony_ci	if (ret) {
53462306a36Sopenharmony_ci		/* FIXME:
53562306a36Sopenharmony_ci		 * No information of battery temperature might
53662306a36Sopenharmony_ci		 * occur hazardous result. We have to handle it
53762306a36Sopenharmony_ci		 * depending on battery type.
53862306a36Sopenharmony_ci		 */
53962306a36Sopenharmony_ci		dev_err(cm->dev, "Failed to get battery temperature\n");
54062306a36Sopenharmony_ci		return 0;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	upper_limit = desc->temp_max;
54462306a36Sopenharmony_ci	lower_limit = desc->temp_min;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (cm->emergency_stop) {
54762306a36Sopenharmony_ci		upper_limit -= desc->temp_diff;
54862306a36Sopenharmony_ci		lower_limit += desc->temp_diff;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (temp > upper_limit)
55262306a36Sopenharmony_ci		ret = CM_BATT_OVERHEAT;
55362306a36Sopenharmony_ci	else if (temp < lower_limit)
55462306a36Sopenharmony_ci		ret = CM_BATT_COLD;
55562306a36Sopenharmony_ci	else
55662306a36Sopenharmony_ci		ret = CM_BATT_OK;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	cm->emergency_stop = ret;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return ret;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/**
56462306a36Sopenharmony_ci * cm_get_target_status - Check current status and get next target status.
56562306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic int cm_get_target_status(struct charger_manager *cm)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	if (!is_ext_pwr_online(cm))
57062306a36Sopenharmony_ci		return POWER_SUPPLY_STATUS_DISCHARGING;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (cm_check_thermal_status(cm)) {
57362306a36Sopenharmony_ci		/* Check if discharging duration exceeds limit. */
57462306a36Sopenharmony_ci		if (check_charging_duration(cm))
57562306a36Sopenharmony_ci			goto charging_ok;
57662306a36Sopenharmony_ci		return POWER_SUPPLY_STATUS_NOT_CHARGING;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	switch (cm->battery_status) {
58062306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_CHARGING:
58162306a36Sopenharmony_ci		/* Check if charging duration exceeds limit. */
58262306a36Sopenharmony_ci		if (check_charging_duration(cm))
58362306a36Sopenharmony_ci			return POWER_SUPPLY_STATUS_FULL;
58462306a36Sopenharmony_ci		fallthrough;
58562306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_FULL:
58662306a36Sopenharmony_ci		if (is_full_charged(cm))
58762306a36Sopenharmony_ci			return POWER_SUPPLY_STATUS_FULL;
58862306a36Sopenharmony_ci		fallthrough;
58962306a36Sopenharmony_ci	default:
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cicharging_ok:
59462306a36Sopenharmony_ci	/* Charging is allowed. */
59562306a36Sopenharmony_ci	return POWER_SUPPLY_STATUS_CHARGING;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/**
59962306a36Sopenharmony_ci * _cm_monitor - Monitor the temperature and return true for exceptions.
60062306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
60162306a36Sopenharmony_ci *
60262306a36Sopenharmony_ci * Returns true if there is an event to notify for the battery.
60362306a36Sopenharmony_ci * (True if the status of "emergency_stop" changes)
60462306a36Sopenharmony_ci */
60562306a36Sopenharmony_cistatic bool _cm_monitor(struct charger_manager *cm)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	int target;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	target = cm_get_target_status(cm);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING));
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (cm->battery_status != target) {
61462306a36Sopenharmony_ci		cm->battery_status = target;
61562306a36Sopenharmony_ci		power_supply_changed(cm->charger_psy);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING);
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci * cm_monitor - Monitor every battery.
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Returns true if there is an event to notify from any of the batteries.
62562306a36Sopenharmony_ci * (True if the status of "emergency_stop" changes)
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic bool cm_monitor(void)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	bool stop = false;
63062306a36Sopenharmony_ci	struct charger_manager *cm;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	list_for_each_entry(cm, &cm_list, entry) {
63562306a36Sopenharmony_ci		if (_cm_monitor(cm))
63662306a36Sopenharmony_ci			stop = true;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return stop;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/**
64562306a36Sopenharmony_ci * _setup_polling - Setup the next instance of polling.
64662306a36Sopenharmony_ci * @work: work_struct of the function _setup_polling.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistatic void _setup_polling(struct work_struct *work)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	unsigned long min = ULONG_MAX;
65162306a36Sopenharmony_ci	struct charger_manager *cm;
65262306a36Sopenharmony_ci	bool keep_polling = false;
65362306a36Sopenharmony_ci	unsigned long _next_polling;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	list_for_each_entry(cm, &cm_list, entry) {
65862306a36Sopenharmony_ci		if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
65962306a36Sopenharmony_ci			keep_polling = true;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci			if (min > cm->desc->polling_interval_ms)
66262306a36Sopenharmony_ci				min = cm->desc->polling_interval_ms;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	polling_jiffy = msecs_to_jiffies(min);
66762306a36Sopenharmony_ci	if (polling_jiffy <= CM_JIFFIES_SMALL)
66862306a36Sopenharmony_ci		polling_jiffy = CM_JIFFIES_SMALL + 1;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (!keep_polling)
67162306a36Sopenharmony_ci		polling_jiffy = ULONG_MAX;
67262306a36Sopenharmony_ci	if (polling_jiffy == ULONG_MAX)
67362306a36Sopenharmony_ci		goto out;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
67662306a36Sopenharmony_ci			    ". try it later. %s\n", __func__);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/*
67962306a36Sopenharmony_ci	 * Use mod_delayed_work() iff the next polling interval should
68062306a36Sopenharmony_ci	 * occur before the currently scheduled one.  If @cm_monitor_work
68162306a36Sopenharmony_ci	 * isn't active, the end result is the same, so no need to worry
68262306a36Sopenharmony_ci	 * about stale @next_polling.
68362306a36Sopenharmony_ci	 */
68462306a36Sopenharmony_ci	_next_polling = jiffies + polling_jiffy;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (time_before(_next_polling, next_polling)) {
68762306a36Sopenharmony_ci		mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
68862306a36Sopenharmony_ci		next_polling = _next_polling;
68962306a36Sopenharmony_ci	} else {
69062306a36Sopenharmony_ci		if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
69162306a36Sopenharmony_ci			next_polling = _next_polling;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ciout:
69462306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_cistatic DECLARE_WORK(setup_polling, _setup_polling);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci/**
69962306a36Sopenharmony_ci * cm_monitor_poller - The Monitor / Poller.
70062306a36Sopenharmony_ci * @work: work_struct of the function cm_monitor_poller
70162306a36Sopenharmony_ci *
70262306a36Sopenharmony_ci * During non-suspended state, cm_monitor_poller is used to poll and monitor
70362306a36Sopenharmony_ci * the batteries.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_cistatic void cm_monitor_poller(struct work_struct *work)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	cm_monitor();
70862306a36Sopenharmony_ci	schedule_work(&setup_polling);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int charger_get_property(struct power_supply *psy,
71262306a36Sopenharmony_ci		enum power_supply_property psp,
71362306a36Sopenharmony_ci		union power_supply_propval *val)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct charger_manager *cm = power_supply_get_drvdata(psy);
71662306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
71762306a36Sopenharmony_ci	struct power_supply *fuel_gauge = NULL;
71862306a36Sopenharmony_ci	int ret = 0;
71962306a36Sopenharmony_ci	int uV;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	switch (psp) {
72262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
72362306a36Sopenharmony_ci		val->intval = cm->battery_status;
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
72662306a36Sopenharmony_ci		if (cm->emergency_stop == CM_BATT_OVERHEAT)
72762306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
72862306a36Sopenharmony_ci		else if (cm->emergency_stop == CM_BATT_COLD)
72962306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_COLD;
73062306a36Sopenharmony_ci		else
73162306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_GOOD;
73262306a36Sopenharmony_ci		break;
73362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
73462306a36Sopenharmony_ci		if (is_batt_present(cm))
73562306a36Sopenharmony_ci			val->intval = 1;
73662306a36Sopenharmony_ci		else
73762306a36Sopenharmony_ci			val->intval = 0;
73862306a36Sopenharmony_ci		break;
73962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
74062306a36Sopenharmony_ci		ret = get_batt_uV(cm, &val->intval);
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
74362306a36Sopenharmony_ci		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
74462306a36Sopenharmony_ci		if (!fuel_gauge) {
74562306a36Sopenharmony_ci			ret = -ENODEV;
74662306a36Sopenharmony_ci			break;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		ret = power_supply_get_property(fuel_gauge,
74962306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CURRENT_NOW, val);
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
75262306a36Sopenharmony_ci		return cm_get_battery_temperature(cm, &val->intval);
75362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
75462306a36Sopenharmony_ci		if (!is_batt_present(cm)) {
75562306a36Sopenharmony_ci			/* There is no battery. Assume 100% */
75662306a36Sopenharmony_ci			val->intval = 100;
75762306a36Sopenharmony_ci			break;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
76162306a36Sopenharmony_ci		if (!fuel_gauge) {
76262306a36Sopenharmony_ci			ret = -ENODEV;
76362306a36Sopenharmony_ci			break;
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		ret = power_supply_get_property(fuel_gauge,
76762306a36Sopenharmony_ci					POWER_SUPPLY_PROP_CAPACITY, val);
76862306a36Sopenharmony_ci		if (ret)
76962306a36Sopenharmony_ci			break;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci		if (val->intval > 100) {
77262306a36Sopenharmony_ci			val->intval = 100;
77362306a36Sopenharmony_ci			break;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci		if (val->intval < 0)
77662306a36Sopenharmony_ci			val->intval = 0;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		/* Do not adjust SOC when charging: voltage is overrated */
77962306a36Sopenharmony_ci		if (is_charging(cm))
78062306a36Sopenharmony_ci			break;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		/*
78362306a36Sopenharmony_ci		 * If the capacity value is inconsistent, calibrate it base on
78462306a36Sopenharmony_ci		 * the battery voltage values and the thresholds given as desc
78562306a36Sopenharmony_ci		 */
78662306a36Sopenharmony_ci		ret = get_batt_uV(cm, &uV);
78762306a36Sopenharmony_ci		if (ret) {
78862306a36Sopenharmony_ci			/* Voltage information not available. No calibration */
78962306a36Sopenharmony_ci			ret = 0;
79062306a36Sopenharmony_ci			break;
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
79462306a36Sopenharmony_ci		    !is_charging(cm)) {
79562306a36Sopenharmony_ci			val->intval = 100;
79662306a36Sopenharmony_ci			break;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
80162306a36Sopenharmony_ci		if (is_ext_pwr_online(cm))
80262306a36Sopenharmony_ci			val->intval = 1;
80362306a36Sopenharmony_ci		else
80462306a36Sopenharmony_ci			val->intval = 0;
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL:
80762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_NOW:
80862306a36Sopenharmony_ci		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
80962306a36Sopenharmony_ci		if (!fuel_gauge) {
81062306a36Sopenharmony_ci			ret = -ENODEV;
81162306a36Sopenharmony_ci			break;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci		ret = power_supply_get_property(fuel_gauge, psp, val);
81462306a36Sopenharmony_ci		break;
81562306a36Sopenharmony_ci	default:
81662306a36Sopenharmony_ci		return -EINVAL;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	if (fuel_gauge)
81962306a36Sopenharmony_ci		power_supply_put(fuel_gauge);
82062306a36Sopenharmony_ci	return ret;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci#define NUM_CHARGER_PSY_OPTIONAL	(4)
82462306a36Sopenharmony_cistatic enum power_supply_property default_charger_props[] = {
82562306a36Sopenharmony_ci	/* Guaranteed to provide */
82662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
82762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
82862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
82962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
83062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
83162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
83262306a36Sopenharmony_ci	/*
83362306a36Sopenharmony_ci	 * Optional properties are:
83462306a36Sopenharmony_ci	 * POWER_SUPPLY_PROP_CHARGE_FULL,
83562306a36Sopenharmony_ci	 * POWER_SUPPLY_PROP_CHARGE_NOW,
83662306a36Sopenharmony_ci	 * POWER_SUPPLY_PROP_CURRENT_NOW,
83762306a36Sopenharmony_ci	 * POWER_SUPPLY_PROP_TEMP,
83862306a36Sopenharmony_ci	 */
83962306a36Sopenharmony_ci};
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic const struct power_supply_desc psy_default = {
84262306a36Sopenharmony_ci	.name = "battery",
84362306a36Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_BATTERY,
84462306a36Sopenharmony_ci	.properties = default_charger_props,
84562306a36Sopenharmony_ci	.num_properties = ARRAY_SIZE(default_charger_props),
84662306a36Sopenharmony_ci	.get_property = charger_get_property,
84762306a36Sopenharmony_ci	.no_thermal = true,
84862306a36Sopenharmony_ci};
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/**
85162306a36Sopenharmony_ci * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
85262306a36Sopenharmony_ci *		    for suspend_again.
85362306a36Sopenharmony_ci *
85462306a36Sopenharmony_ci * Returns true if the alarm is set for Charger Manager to use.
85562306a36Sopenharmony_ci * Returns false if
85662306a36Sopenharmony_ci *	cm_setup_timer fails to set an alarm,
85762306a36Sopenharmony_ci *	cm_setup_timer does not need to set an alarm for Charger Manager,
85862306a36Sopenharmony_ci *	or an alarm previously configured is to be used.
85962306a36Sopenharmony_ci */
86062306a36Sopenharmony_cistatic bool cm_setup_timer(void)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct charger_manager *cm;
86362306a36Sopenharmony_ci	unsigned int wakeup_ms = UINT_MAX;
86462306a36Sopenharmony_ci	int timer_req = 0;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (time_after(next_polling, jiffies))
86762306a36Sopenharmony_ci		CM_MIN_VALID(wakeup_ms,
86862306a36Sopenharmony_ci			jiffies_to_msecs(next_polling - jiffies));
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
87162306a36Sopenharmony_ci	list_for_each_entry(cm, &cm_list, entry) {
87262306a36Sopenharmony_ci		/* Skip if polling is not required for this CM */
87362306a36Sopenharmony_ci		if (!is_polling_required(cm) && !cm->emergency_stop)
87462306a36Sopenharmony_ci			continue;
87562306a36Sopenharmony_ci		timer_req++;
87662306a36Sopenharmony_ci		if (cm->desc->polling_interval_ms == 0)
87762306a36Sopenharmony_ci			continue;
87862306a36Sopenharmony_ci		CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (timer_req && cm_timer) {
88362306a36Sopenharmony_ci		ktime_t now, add;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		/*
88662306a36Sopenharmony_ci		 * Set alarm with the polling interval (wakeup_ms)
88762306a36Sopenharmony_ci		 * The alarm time should be NOW + CM_RTC_SMALL or later.
88862306a36Sopenharmony_ci		 */
88962306a36Sopenharmony_ci		if (wakeup_ms == UINT_MAX ||
89062306a36Sopenharmony_ci			wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
89162306a36Sopenharmony_ci			wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		now = ktime_get_boottime();
89662306a36Sopenharmony_ci		add = ktime_set(wakeup_ms / MSEC_PER_SEC,
89762306a36Sopenharmony_ci				(wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
89862306a36Sopenharmony_ci		alarm_start(cm_timer, ktime_add(now, add));
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		cm_suspend_duration_ms = wakeup_ms;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci		return true;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	return false;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/**
90862306a36Sopenharmony_ci * charger_extcon_work - enable/diable charger according to the state
90962306a36Sopenharmony_ci *			of charger cable
91062306a36Sopenharmony_ci *
91162306a36Sopenharmony_ci * @work: work_struct of the function charger_extcon_work.
91262306a36Sopenharmony_ci */
91362306a36Sopenharmony_cistatic void charger_extcon_work(struct work_struct *work)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct charger_cable *cable =
91662306a36Sopenharmony_ci			container_of(work, struct charger_cable, wq);
91762306a36Sopenharmony_ci	int ret;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
92062306a36Sopenharmony_ci		ret = regulator_set_current_limit(cable->charger->consumer,
92162306a36Sopenharmony_ci					cable->min_uA, cable->max_uA);
92262306a36Sopenharmony_ci		if (ret < 0) {
92362306a36Sopenharmony_ci			pr_err("Cannot set current limit of %s (%s)\n",
92462306a36Sopenharmony_ci			       cable->charger->regulator_name, cable->name);
92562306a36Sopenharmony_ci			return;
92662306a36Sopenharmony_ci		}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		pr_info("Set current limit of %s : %duA ~ %duA\n",
92962306a36Sopenharmony_ci			cable->charger->regulator_name,
93062306a36Sopenharmony_ci			cable->min_uA, cable->max_uA);
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	cancel_delayed_work(&cm_monitor_work);
93462306a36Sopenharmony_ci	queue_delayed_work(cm_wq, &cm_monitor_work, 0);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/**
93862306a36Sopenharmony_ci * charger_extcon_notifier - receive the state of charger cable
93962306a36Sopenharmony_ci *			when registered cable is attached or detached.
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci * @self: the notifier block of the charger_extcon_notifier.
94262306a36Sopenharmony_ci * @event: the cable state.
94362306a36Sopenharmony_ci * @ptr: the data pointer of notifier block.
94462306a36Sopenharmony_ci */
94562306a36Sopenharmony_cistatic int charger_extcon_notifier(struct notifier_block *self,
94662306a36Sopenharmony_ci			unsigned long event, void *ptr)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct charger_cable *cable =
94962306a36Sopenharmony_ci		container_of(self, struct charger_cable, nb);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/*
95262306a36Sopenharmony_ci	 * The newly state of charger cable.
95362306a36Sopenharmony_ci	 * If cable is attached, cable->attached is true.
95462306a36Sopenharmony_ci	 */
95562306a36Sopenharmony_ci	cable->attached = event;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/*
95862306a36Sopenharmony_ci	 * Setup work for controlling charger(regulator)
95962306a36Sopenharmony_ci	 * according to charger cable.
96062306a36Sopenharmony_ci	 */
96162306a36Sopenharmony_ci	schedule_work(&cable->wq);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return NOTIFY_DONE;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/**
96762306a36Sopenharmony_ci * charger_extcon_init - register external connector to use it
96862306a36Sopenharmony_ci *			as the charger cable
96962306a36Sopenharmony_ci *
97062306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
97162306a36Sopenharmony_ci * @cable: the Charger cable representing the external connector.
97262306a36Sopenharmony_ci */
97362306a36Sopenharmony_cistatic int charger_extcon_init(struct charger_manager *cm,
97462306a36Sopenharmony_ci		struct charger_cable *cable)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	int ret, i;
97762306a36Sopenharmony_ci	u64 extcon_type = EXTCON_NONE;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/*
98062306a36Sopenharmony_ci	 * Charger manager use Extcon framework to identify
98162306a36Sopenharmony_ci	 * the charger cable among various external connector
98262306a36Sopenharmony_ci	 * cable (e.g., TA, USB, MHL, Dock).
98362306a36Sopenharmony_ci	 */
98462306a36Sopenharmony_ci	INIT_WORK(&cable->wq, charger_extcon_work);
98562306a36Sopenharmony_ci	cable->nb.notifier_call = charger_extcon_notifier;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name);
98862306a36Sopenharmony_ci	if (IS_ERR(cable->extcon_dev)) {
98962306a36Sopenharmony_ci		pr_err("Cannot find extcon_dev for %s (cable: %s)\n",
99062306a36Sopenharmony_ci			cable->extcon_name, cable->name);
99162306a36Sopenharmony_ci		return PTR_ERR(cable->extcon_dev);
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) {
99562306a36Sopenharmony_ci		if (!strcmp(cable->name, extcon_mapping[i].name)) {
99662306a36Sopenharmony_ci			extcon_type = extcon_mapping[i].extcon_type;
99762306a36Sopenharmony_ci			break;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci	if (extcon_type == EXTCON_NONE) {
100162306a36Sopenharmony_ci		pr_err("Cannot find cable for type %s", cable->name);
100262306a36Sopenharmony_ci		return -EINVAL;
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	cable->extcon_type = extcon_type;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev,
100862306a36Sopenharmony_ci		cable->extcon_type, &cable->nb);
100962306a36Sopenharmony_ci	if (ret < 0) {
101062306a36Sopenharmony_ci		pr_err("Cannot register extcon_dev for %s (cable: %s)\n",
101162306a36Sopenharmony_ci			cable->extcon_name, cable->name);
101262306a36Sopenharmony_ci		return ret;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return 0;
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci/**
101962306a36Sopenharmony_ci * charger_manager_register_extcon - Register extcon device to receive state
102062306a36Sopenharmony_ci *				     of charger cable.
102162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
102262306a36Sopenharmony_ci *
102362306a36Sopenharmony_ci * This function support EXTCON(External Connector) subsystem to detect the
102462306a36Sopenharmony_ci * state of charger cables for enabling or disabling charger(regulator) and
102562306a36Sopenharmony_ci * select the charger cable for charging among a number of external cable
102662306a36Sopenharmony_ci * according to policy of H/W board.
102762306a36Sopenharmony_ci */
102862306a36Sopenharmony_cistatic int charger_manager_register_extcon(struct charger_manager *cm)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
103162306a36Sopenharmony_ci	struct charger_regulator *charger;
103262306a36Sopenharmony_ci	unsigned long event;
103362306a36Sopenharmony_ci	int ret;
103462306a36Sopenharmony_ci	int i;
103562306a36Sopenharmony_ci	int j;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	for (i = 0; i < desc->num_charger_regulators; i++) {
103862306a36Sopenharmony_ci		charger = &desc->charger_regulators[i];
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		charger->consumer = regulator_get(cm->dev,
104162306a36Sopenharmony_ci					charger->regulator_name);
104262306a36Sopenharmony_ci		if (IS_ERR(charger->consumer)) {
104362306a36Sopenharmony_ci			dev_err(cm->dev, "Cannot find charger(%s)\n",
104462306a36Sopenharmony_ci				charger->regulator_name);
104562306a36Sopenharmony_ci			return PTR_ERR(charger->consumer);
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci		charger->cm = cm;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		for (j = 0; j < charger->num_cables; j++) {
105062306a36Sopenharmony_ci			struct charger_cable *cable = &charger->cables[j];
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci			ret = charger_extcon_init(cm, cable);
105362306a36Sopenharmony_ci			if (ret < 0) {
105462306a36Sopenharmony_ci				dev_err(cm->dev, "Cannot initialize charger(%s)\n",
105562306a36Sopenharmony_ci					charger->regulator_name);
105662306a36Sopenharmony_ci				return ret;
105762306a36Sopenharmony_ci			}
105862306a36Sopenharmony_ci			cable->charger = charger;
105962306a36Sopenharmony_ci			cable->cm = cm;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci			event = extcon_get_state(cable->extcon_dev,
106262306a36Sopenharmony_ci				cable->extcon_type);
106362306a36Sopenharmony_ci			charger_extcon_notifier(&cable->nb,
106462306a36Sopenharmony_ci				event, NULL);
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return 0;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci/* help function of sysfs node to control charger(regulator) */
107262306a36Sopenharmony_cistatic ssize_t charger_name_show(struct device *dev,
107362306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	struct charger_regulator *charger
107662306a36Sopenharmony_ci		= container_of(attr, struct charger_regulator, attr_name);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", charger->regulator_name);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic ssize_t charger_state_show(struct device *dev,
108262306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct charger_regulator *charger
108562306a36Sopenharmony_ci		= container_of(attr, struct charger_regulator, attr_state);
108662306a36Sopenharmony_ci	int state = 0;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (!charger->externally_control)
108962306a36Sopenharmony_ci		state = regulator_is_enabled(charger->consumer);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled");
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic ssize_t charger_externally_control_show(struct device *dev,
109562306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	struct charger_regulator *charger = container_of(attr,
109862306a36Sopenharmony_ci			struct charger_regulator, attr_externally_control);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", charger->externally_control);
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic ssize_t charger_externally_control_store(struct device *dev,
110462306a36Sopenharmony_ci				struct device_attribute *attr, const char *buf,
110562306a36Sopenharmony_ci				size_t count)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct charger_regulator *charger
110862306a36Sopenharmony_ci		= container_of(attr, struct charger_regulator,
110962306a36Sopenharmony_ci					attr_externally_control);
111062306a36Sopenharmony_ci	struct charger_manager *cm = charger->cm;
111162306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
111262306a36Sopenharmony_ci	int i;
111362306a36Sopenharmony_ci	int ret;
111462306a36Sopenharmony_ci	int externally_control;
111562306a36Sopenharmony_ci	int chargers_externally_control = 1;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	ret = sscanf(buf, "%d", &externally_control);
111862306a36Sopenharmony_ci	if (ret == 0) {
111962306a36Sopenharmony_ci		ret = -EINVAL;
112062306a36Sopenharmony_ci		return ret;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (!externally_control) {
112462306a36Sopenharmony_ci		charger->externally_control = 0;
112562306a36Sopenharmony_ci		return count;
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	for (i = 0; i < desc->num_charger_regulators; i++) {
112962306a36Sopenharmony_ci		if (&desc->charger_regulators[i] != charger &&
113062306a36Sopenharmony_ci			!desc->charger_regulators[i].externally_control) {
113162306a36Sopenharmony_ci			/*
113262306a36Sopenharmony_ci			 * At least, one charger is controlled by
113362306a36Sopenharmony_ci			 * charger-manager
113462306a36Sopenharmony_ci			 */
113562306a36Sopenharmony_ci			chargers_externally_control = 0;
113662306a36Sopenharmony_ci			break;
113762306a36Sopenharmony_ci		}
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	if (!chargers_externally_control) {
114162306a36Sopenharmony_ci		if (cm->charger_enabled) {
114262306a36Sopenharmony_ci			try_charger_enable(charger->cm, false);
114362306a36Sopenharmony_ci			charger->externally_control = externally_control;
114462306a36Sopenharmony_ci			try_charger_enable(charger->cm, true);
114562306a36Sopenharmony_ci		} else {
114662306a36Sopenharmony_ci			charger->externally_control = externally_control;
114762306a36Sopenharmony_ci		}
114862306a36Sopenharmony_ci	} else {
114962306a36Sopenharmony_ci		dev_warn(cm->dev,
115062306a36Sopenharmony_ci			 "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n",
115162306a36Sopenharmony_ci			 charger->regulator_name);
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	return count;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci/**
115862306a36Sopenharmony_ci * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger
115962306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery.
116062306a36Sopenharmony_ci *
116162306a36Sopenharmony_ci * This function add sysfs entry for charger(regulator) to control charger from
116262306a36Sopenharmony_ci * user-space. If some development board use one more chargers for charging
116362306a36Sopenharmony_ci * but only need one charger on specific case which is dependent on user
116462306a36Sopenharmony_ci * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
116562306a36Sopenharmony_ci * class/power_supply/battery/charger.[index]/externally_control'. For example,
116662306a36Sopenharmony_ci * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
116762306a36Sopenharmony_ci * externally_control, this charger isn't controlled from charger-manager and
116862306a36Sopenharmony_ci * always stay off state of regulator.
116962306a36Sopenharmony_ci */
117062306a36Sopenharmony_cistatic int charger_manager_prepare_sysfs(struct charger_manager *cm)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
117362306a36Sopenharmony_ci	struct charger_regulator *charger;
117462306a36Sopenharmony_ci	int chargers_externally_control = 1;
117562306a36Sopenharmony_ci	char *name;
117662306a36Sopenharmony_ci	int i;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Create sysfs entry to control charger(regulator) */
117962306a36Sopenharmony_ci	for (i = 0; i < desc->num_charger_regulators; i++) {
118062306a36Sopenharmony_ci		charger = &desc->charger_regulators[i];
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i);
118362306a36Sopenharmony_ci		if (!name)
118462306a36Sopenharmony_ci			return -ENOMEM;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci		charger->attrs[0] = &charger->attr_name.attr;
118762306a36Sopenharmony_ci		charger->attrs[1] = &charger->attr_state.attr;
118862306a36Sopenharmony_ci		charger->attrs[2] = &charger->attr_externally_control.attr;
118962306a36Sopenharmony_ci		charger->attrs[3] = NULL;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		charger->attr_grp.name = name;
119262306a36Sopenharmony_ci		charger->attr_grp.attrs = charger->attrs;
119362306a36Sopenharmony_ci		desc->sysfs_groups[i] = &charger->attr_grp;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		sysfs_attr_init(&charger->attr_name.attr);
119662306a36Sopenharmony_ci		charger->attr_name.attr.name = "name";
119762306a36Sopenharmony_ci		charger->attr_name.attr.mode = 0444;
119862306a36Sopenharmony_ci		charger->attr_name.show = charger_name_show;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		sysfs_attr_init(&charger->attr_state.attr);
120162306a36Sopenharmony_ci		charger->attr_state.attr.name = "state";
120262306a36Sopenharmony_ci		charger->attr_state.attr.mode = 0444;
120362306a36Sopenharmony_ci		charger->attr_state.show = charger_state_show;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		sysfs_attr_init(&charger->attr_externally_control.attr);
120662306a36Sopenharmony_ci		charger->attr_externally_control.attr.name
120762306a36Sopenharmony_ci				= "externally_control";
120862306a36Sopenharmony_ci		charger->attr_externally_control.attr.mode = 0644;
120962306a36Sopenharmony_ci		charger->attr_externally_control.show
121062306a36Sopenharmony_ci				= charger_externally_control_show;
121162306a36Sopenharmony_ci		charger->attr_externally_control.store
121262306a36Sopenharmony_ci				= charger_externally_control_store;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		if (!desc->charger_regulators[i].externally_control ||
121562306a36Sopenharmony_ci				!chargers_externally_control)
121662306a36Sopenharmony_ci			chargers_externally_control = 0;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
121962306a36Sopenharmony_ci			 charger->regulator_name, charger->externally_control);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (chargers_externally_control) {
122362306a36Sopenharmony_ci		dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n");
122462306a36Sopenharmony_ci		return -EINVAL;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return 0;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic int cm_init_thermal_data(struct charger_manager *cm,
123162306a36Sopenharmony_ci		struct power_supply *fuel_gauge,
123262306a36Sopenharmony_ci		enum power_supply_property *properties,
123362306a36Sopenharmony_ci		size_t *num_properties)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
123662306a36Sopenharmony_ci	union power_supply_propval val;
123762306a36Sopenharmony_ci	int ret;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	/* Verify whether fuel gauge provides battery temperature */
124062306a36Sopenharmony_ci	ret = power_supply_get_property(fuel_gauge,
124162306a36Sopenharmony_ci					POWER_SUPPLY_PROP_TEMP, &val);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (!ret) {
124462306a36Sopenharmony_ci		properties[*num_properties] = POWER_SUPPLY_PROP_TEMP;
124562306a36Sopenharmony_ci		(*num_properties)++;
124662306a36Sopenharmony_ci		cm->desc->measure_battery_temp = true;
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci#ifdef CONFIG_THERMAL
124962306a36Sopenharmony_ci	if (ret && desc->thermal_zone) {
125062306a36Sopenharmony_ci		cm->tzd_batt =
125162306a36Sopenharmony_ci			thermal_zone_get_zone_by_name(desc->thermal_zone);
125262306a36Sopenharmony_ci		if (IS_ERR(cm->tzd_batt))
125362306a36Sopenharmony_ci			return PTR_ERR(cm->tzd_batt);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		/* Use external thermometer */
125662306a36Sopenharmony_ci		properties[*num_properties] = POWER_SUPPLY_PROP_TEMP;
125762306a36Sopenharmony_ci		(*num_properties)++;
125862306a36Sopenharmony_ci		cm->desc->measure_battery_temp = true;
125962306a36Sopenharmony_ci		ret = 0;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci#endif
126262306a36Sopenharmony_ci	if (cm->desc->measure_battery_temp) {
126362306a36Sopenharmony_ci		/* NOTICE : Default allowable minimum charge temperature is 0 */
126462306a36Sopenharmony_ci		if (!desc->temp_max)
126562306a36Sopenharmony_ci			desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
126662306a36Sopenharmony_ci		if (!desc->temp_diff)
126762306a36Sopenharmony_ci			desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return ret;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic const struct of_device_id charger_manager_match[] = {
127462306a36Sopenharmony_ci	{
127562306a36Sopenharmony_ci		.compatible = "charger-manager",
127662306a36Sopenharmony_ci	},
127762306a36Sopenharmony_ci	{},
127862306a36Sopenharmony_ci};
127962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, charger_manager_match);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic struct charger_desc *of_cm_parse_desc(struct device *dev)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	struct charger_desc *desc;
128462306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
128562306a36Sopenharmony_ci	u32 poll_mode = CM_POLL_DISABLE;
128662306a36Sopenharmony_ci	u32 battery_stat = CM_NO_BATTERY;
128762306a36Sopenharmony_ci	int num_chgs = 0;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
129062306a36Sopenharmony_ci	if (!desc)
129162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	of_property_read_string(np, "cm-name", &desc->psy_name);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	of_property_read_u32(np, "cm-poll-mode", &poll_mode);
129662306a36Sopenharmony_ci	desc->polling_mode = poll_mode;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	of_property_read_u32(np, "cm-poll-interval",
129962306a36Sopenharmony_ci				&desc->polling_interval_ms);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt",
130262306a36Sopenharmony_ci					&desc->fullbatt_vchkdrop_uV);
130362306a36Sopenharmony_ci	of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV);
130462306a36Sopenharmony_ci	of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc);
130562306a36Sopenharmony_ci	of_property_read_u32(np, "cm-fullbatt-capacity",
130662306a36Sopenharmony_ci					&desc->fullbatt_full_capacity);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	of_property_read_u32(np, "cm-battery-stat", &battery_stat);
130962306a36Sopenharmony_ci	desc->battery_present = battery_stat;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	/* chargers */
131262306a36Sopenharmony_ci	num_chgs = of_property_count_strings(np, "cm-chargers");
131362306a36Sopenharmony_ci	if (num_chgs > 0) {
131462306a36Sopenharmony_ci		int i;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci		/* Allocate empty bin at the tail of array */
131762306a36Sopenharmony_ci		desc->psy_charger_stat = devm_kcalloc(dev,
131862306a36Sopenharmony_ci						      num_chgs + 1,
131962306a36Sopenharmony_ci						      sizeof(char *),
132062306a36Sopenharmony_ci						      GFP_KERNEL);
132162306a36Sopenharmony_ci		if (!desc->psy_charger_stat)
132262306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		for (i = 0; i < num_chgs; i++)
132562306a36Sopenharmony_ci			of_property_read_string_index(np, "cm-chargers",
132662306a36Sopenharmony_ci						      i, &desc->psy_charger_stat[i]);
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	of_property_read_u32(np, "cm-battery-cold", &desc->temp_min);
133462306a36Sopenharmony_ci	if (of_property_read_bool(np, "cm-battery-cold-in-minus"))
133562306a36Sopenharmony_ci		desc->temp_min *= -1;
133662306a36Sopenharmony_ci	of_property_read_u32(np, "cm-battery-hot", &desc->temp_max);
133762306a36Sopenharmony_ci	of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	of_property_read_u32(np, "cm-charging-max",
134062306a36Sopenharmony_ci				&desc->charging_max_duration_ms);
134162306a36Sopenharmony_ci	of_property_read_u32(np, "cm-discharging-max",
134262306a36Sopenharmony_ci				&desc->discharging_max_duration_ms);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	/* battery charger regulators */
134562306a36Sopenharmony_ci	desc->num_charger_regulators = of_get_child_count(np);
134662306a36Sopenharmony_ci	if (desc->num_charger_regulators) {
134762306a36Sopenharmony_ci		struct charger_regulator *chg_regs;
134862306a36Sopenharmony_ci		struct device_node *child;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		chg_regs = devm_kcalloc(dev,
135162306a36Sopenharmony_ci					desc->num_charger_regulators,
135262306a36Sopenharmony_ci					sizeof(*chg_regs),
135362306a36Sopenharmony_ci					GFP_KERNEL);
135462306a36Sopenharmony_ci		if (!chg_regs)
135562306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		desc->charger_regulators = chg_regs;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci		desc->sysfs_groups = devm_kcalloc(dev,
136062306a36Sopenharmony_ci					desc->num_charger_regulators + 1,
136162306a36Sopenharmony_ci					sizeof(*desc->sysfs_groups),
136262306a36Sopenharmony_ci					GFP_KERNEL);
136362306a36Sopenharmony_ci		if (!desc->sysfs_groups)
136462306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		for_each_child_of_node(np, child) {
136762306a36Sopenharmony_ci			struct charger_cable *cables;
136862306a36Sopenharmony_ci			struct device_node *_child;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci			of_property_read_string(child, "cm-regulator-name",
137162306a36Sopenharmony_ci					&chg_regs->regulator_name);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci			/* charger cables */
137462306a36Sopenharmony_ci			chg_regs->num_cables = of_get_child_count(child);
137562306a36Sopenharmony_ci			if (chg_regs->num_cables) {
137662306a36Sopenharmony_ci				cables = devm_kcalloc(dev,
137762306a36Sopenharmony_ci						      chg_regs->num_cables,
137862306a36Sopenharmony_ci						      sizeof(*cables),
137962306a36Sopenharmony_ci						      GFP_KERNEL);
138062306a36Sopenharmony_ci				if (!cables) {
138162306a36Sopenharmony_ci					of_node_put(child);
138262306a36Sopenharmony_ci					return ERR_PTR(-ENOMEM);
138362306a36Sopenharmony_ci				}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci				chg_regs->cables = cables;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci				for_each_child_of_node(child, _child) {
138862306a36Sopenharmony_ci					of_property_read_string(_child,
138962306a36Sopenharmony_ci					"cm-cable-name", &cables->name);
139062306a36Sopenharmony_ci					of_property_read_string(_child,
139162306a36Sopenharmony_ci					"cm-cable-extcon",
139262306a36Sopenharmony_ci					&cables->extcon_name);
139362306a36Sopenharmony_ci					of_property_read_u32(_child,
139462306a36Sopenharmony_ci					"cm-cable-min",
139562306a36Sopenharmony_ci					&cables->min_uA);
139662306a36Sopenharmony_ci					of_property_read_u32(_child,
139762306a36Sopenharmony_ci					"cm-cable-max",
139862306a36Sopenharmony_ci					&cables->max_uA);
139962306a36Sopenharmony_ci					cables++;
140062306a36Sopenharmony_ci				}
140162306a36Sopenharmony_ci			}
140262306a36Sopenharmony_ci			chg_regs++;
140362306a36Sopenharmony_ci		}
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci	return desc;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	if (pdev->dev.of_node)
141162306a36Sopenharmony_ci		return of_cm_parse_desc(&pdev->dev);
141262306a36Sopenharmony_ci	return dev_get_platdata(&pdev->dev);
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	cm_timer_set = false;
141862306a36Sopenharmony_ci	return ALARMTIMER_NORESTART;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_cistatic int charger_manager_probe(struct platform_device *pdev)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct charger_desc *desc = cm_get_drv_data(pdev);
142462306a36Sopenharmony_ci	struct charger_manager *cm;
142562306a36Sopenharmony_ci	int ret, i = 0;
142662306a36Sopenharmony_ci	union power_supply_propval val;
142762306a36Sopenharmony_ci	struct power_supply *fuel_gauge;
142862306a36Sopenharmony_ci	enum power_supply_property *properties;
142962306a36Sopenharmony_ci	size_t num_properties;
143062306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	if (IS_ERR(desc)) {
143362306a36Sopenharmony_ci		dev_err(&pdev->dev, "No platform data (desc) found\n");
143462306a36Sopenharmony_ci		return PTR_ERR(desc);
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
143862306a36Sopenharmony_ci	if (!cm)
143962306a36Sopenharmony_ci		return -ENOMEM;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* Basic Values. Unspecified are Null or 0 */
144262306a36Sopenharmony_ci	cm->dev = &pdev->dev;
144362306a36Sopenharmony_ci	cm->desc = desc;
144462306a36Sopenharmony_ci	psy_cfg.drv_data = cm;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* Initialize alarm timer */
144762306a36Sopenharmony_ci	if (alarmtimer_get_rtcdev()) {
144862306a36Sopenharmony_ci		cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
144962306a36Sopenharmony_ci		if (!cm_timer)
145062306a36Sopenharmony_ci			return -ENOMEM;
145162306a36Sopenharmony_ci		alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/*
145562306a36Sopenharmony_ci	 * Some of the following do not need to be errors.
145662306a36Sopenharmony_ci	 * Users may intentionally ignore those features.
145762306a36Sopenharmony_ci	 */
145862306a36Sopenharmony_ci	if (desc->fullbatt_uV == 0) {
145962306a36Sopenharmony_ci		dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci	if (!desc->fullbatt_vchkdrop_uV) {
146262306a36Sopenharmony_ci		dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n");
146362306a36Sopenharmony_ci		desc->fullbatt_vchkdrop_uV = 0;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci	if (desc->fullbatt_soc == 0) {
146662306a36Sopenharmony_ci		dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n");
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci	if (desc->fullbatt_full_capacity == 0) {
146962306a36Sopenharmony_ci		dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n");
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
147362306a36Sopenharmony_ci		dev_err(&pdev->dev, "charger_regulators undefined\n");
147462306a36Sopenharmony_ci		return -EINVAL;
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
147862306a36Sopenharmony_ci		dev_err(&pdev->dev, "No power supply defined\n");
147962306a36Sopenharmony_ci		return -EINVAL;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (!desc->psy_fuel_gauge) {
148362306a36Sopenharmony_ci		dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
148462306a36Sopenharmony_ci		return -EINVAL;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	/* Check if charger's supplies are present at probe */
148862306a36Sopenharmony_ci	for (i = 0; desc->psy_charger_stat[i]; i++) {
148962306a36Sopenharmony_ci		struct power_supply *psy;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
149262306a36Sopenharmony_ci		if (!psy) {
149362306a36Sopenharmony_ci			dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
149462306a36Sopenharmony_ci				desc->psy_charger_stat[i]);
149562306a36Sopenharmony_ci			return -ENODEV;
149662306a36Sopenharmony_ci		}
149762306a36Sopenharmony_ci		power_supply_put(psy);
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (cm->desc->polling_mode != CM_POLL_DISABLE &&
150162306a36Sopenharmony_ci	    (desc->polling_interval_ms == 0 ||
150262306a36Sopenharmony_ci	     msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL)) {
150362306a36Sopenharmony_ci		dev_err(&pdev->dev, "polling_interval_ms is too small\n");
150462306a36Sopenharmony_ci		return -EINVAL;
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	if (!desc->charging_max_duration_ms ||
150862306a36Sopenharmony_ci			!desc->discharging_max_duration_ms) {
150962306a36Sopenharmony_ci		dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n");
151062306a36Sopenharmony_ci		desc->charging_max_duration_ms = 0;
151162306a36Sopenharmony_ci		desc->discharging_max_duration_ms = 0;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	platform_set_drvdata(pdev, cm);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (!desc->psy_name)
151962306a36Sopenharmony_ci		strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
152062306a36Sopenharmony_ci	else
152162306a36Sopenharmony_ci		strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
152262306a36Sopenharmony_ci	cm->charger_psy_desc.name = cm->psy_name_buf;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* Allocate for psy properties because they may vary */
152562306a36Sopenharmony_ci	properties = devm_kcalloc(&pdev->dev,
152662306a36Sopenharmony_ci			     ARRAY_SIZE(default_charger_props) +
152762306a36Sopenharmony_ci				NUM_CHARGER_PSY_OPTIONAL,
152862306a36Sopenharmony_ci			     sizeof(*properties), GFP_KERNEL);
152962306a36Sopenharmony_ci	if (!properties)
153062306a36Sopenharmony_ci		return -ENOMEM;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	memcpy(properties, default_charger_props,
153362306a36Sopenharmony_ci		sizeof(enum power_supply_property) *
153462306a36Sopenharmony_ci		ARRAY_SIZE(default_charger_props));
153562306a36Sopenharmony_ci	num_properties = ARRAY_SIZE(default_charger_props);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Find which optional psy-properties are available */
153862306a36Sopenharmony_ci	fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
153962306a36Sopenharmony_ci	if (!fuel_gauge) {
154062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
154162306a36Sopenharmony_ci			desc->psy_fuel_gauge);
154262306a36Sopenharmony_ci		return -ENODEV;
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci	if (!power_supply_get_property(fuel_gauge,
154562306a36Sopenharmony_ci					POWER_SUPPLY_PROP_CHARGE_FULL, &val)) {
154662306a36Sopenharmony_ci		properties[num_properties] =
154762306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CHARGE_FULL;
154862306a36Sopenharmony_ci		num_properties++;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci	if (!power_supply_get_property(fuel_gauge,
155162306a36Sopenharmony_ci					  POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
155262306a36Sopenharmony_ci		properties[num_properties] =
155362306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CHARGE_NOW;
155462306a36Sopenharmony_ci		num_properties++;
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci	if (!power_supply_get_property(fuel_gauge,
155762306a36Sopenharmony_ci					  POWER_SUPPLY_PROP_CURRENT_NOW,
155862306a36Sopenharmony_ci					  &val)) {
155962306a36Sopenharmony_ci		properties[num_properties] =
156062306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CURRENT_NOW;
156162306a36Sopenharmony_ci		num_properties++;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties);
156562306a36Sopenharmony_ci	if (ret) {
156662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize thermal data\n");
156762306a36Sopenharmony_ci		cm->desc->measure_battery_temp = false;
156862306a36Sopenharmony_ci	}
156962306a36Sopenharmony_ci	power_supply_put(fuel_gauge);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	cm->charger_psy_desc.properties = properties;
157262306a36Sopenharmony_ci	cm->charger_psy_desc.num_properties = num_properties;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	/* Register sysfs entry for charger(regulator) */
157562306a36Sopenharmony_ci	ret = charger_manager_prepare_sysfs(cm);
157662306a36Sopenharmony_ci	if (ret < 0) {
157762306a36Sopenharmony_ci		dev_err(&pdev->dev,
157862306a36Sopenharmony_ci			"Cannot prepare sysfs entry of regulators\n");
157962306a36Sopenharmony_ci		return ret;
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci	psy_cfg.attr_grp = desc->sysfs_groups;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	cm->charger_psy = power_supply_register(&pdev->dev,
158462306a36Sopenharmony_ci						&cm->charger_psy_desc,
158562306a36Sopenharmony_ci						&psy_cfg);
158662306a36Sopenharmony_ci	if (IS_ERR(cm->charger_psy)) {
158762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
158862306a36Sopenharmony_ci			cm->charger_psy_desc.name);
158962306a36Sopenharmony_ci		return PTR_ERR(cm->charger_psy);
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	/* Register extcon device for charger cable */
159362306a36Sopenharmony_ci	ret = charger_manager_register_extcon(cm);
159462306a36Sopenharmony_ci	if (ret < 0) {
159562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot initialize extcon device\n");
159662306a36Sopenharmony_ci		goto err_reg_extcon;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* Add to the list */
160062306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
160162306a36Sopenharmony_ci	list_add(&cm->entry, &cm_list);
160262306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	/*
160562306a36Sopenharmony_ci	 * Charger-manager is capable of waking up the system from sleep
160662306a36Sopenharmony_ci	 * when event is happened through cm_notify_event()
160762306a36Sopenharmony_ci	 */
160862306a36Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
160962306a36Sopenharmony_ci	device_set_wakeup_capable(&pdev->dev, false);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/*
161262306a36Sopenharmony_ci	 * Charger-manager have to check the charging state right after
161362306a36Sopenharmony_ci	 * initialization of charger-manager and then update current charging
161462306a36Sopenharmony_ci	 * state.
161562306a36Sopenharmony_ci	 */
161662306a36Sopenharmony_ci	cm_monitor();
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	schedule_work(&setup_polling);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	return 0;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cierr_reg_extcon:
162362306a36Sopenharmony_ci	for (i = 0; i < desc->num_charger_regulators; i++)
162462306a36Sopenharmony_ci		regulator_put(desc->charger_regulators[i].consumer);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	power_supply_unregister(cm->charger_psy);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	return ret;
162962306a36Sopenharmony_ci}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_cistatic int charger_manager_remove(struct platform_device *pdev)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci	struct charger_manager *cm = platform_get_drvdata(pdev);
163462306a36Sopenharmony_ci	struct charger_desc *desc = cm->desc;
163562306a36Sopenharmony_ci	int i = 0;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/* Remove from the list */
163862306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
163962306a36Sopenharmony_ci	list_del(&cm->entry);
164062306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	cancel_work_sync(&setup_polling);
164362306a36Sopenharmony_ci	cancel_delayed_work_sync(&cm_monitor_work);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	for (i = 0 ; i < desc->num_charger_regulators ; i++)
164662306a36Sopenharmony_ci		regulator_put(desc->charger_regulators[i].consumer);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	power_supply_unregister(cm->charger_psy);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	try_charger_enable(cm, false);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	return 0;
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cistatic const struct platform_device_id charger_manager_id[] = {
165662306a36Sopenharmony_ci	{ "charger-manager", 0 },
165762306a36Sopenharmony_ci	{ },
165862306a36Sopenharmony_ci};
165962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, charger_manager_id);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cistatic int cm_suspend_noirq(struct device *dev)
166262306a36Sopenharmony_ci{
166362306a36Sopenharmony_ci	if (device_may_wakeup(dev)) {
166462306a36Sopenharmony_ci		device_set_wakeup_capable(dev, false);
166562306a36Sopenharmony_ci		return -EAGAIN;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	return 0;
166962306a36Sopenharmony_ci}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_cistatic bool cm_need_to_awake(void)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	struct charger_manager *cm;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (cm_timer)
167662306a36Sopenharmony_ci		return false;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	mutex_lock(&cm_list_mtx);
167962306a36Sopenharmony_ci	list_for_each_entry(cm, &cm_list, entry) {
168062306a36Sopenharmony_ci		if (is_charging(cm)) {
168162306a36Sopenharmony_ci			mutex_unlock(&cm_list_mtx);
168262306a36Sopenharmony_ci			return true;
168362306a36Sopenharmony_ci		}
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci	mutex_unlock(&cm_list_mtx);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	return false;
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_cistatic int cm_suspend_prepare(struct device *dev)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	if (cm_need_to_awake())
169362306a36Sopenharmony_ci		return -EBUSY;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (!cm_suspended)
169662306a36Sopenharmony_ci		cm_suspended = true;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	cm_timer_set = cm_setup_timer();
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	if (cm_timer_set) {
170162306a36Sopenharmony_ci		cancel_work_sync(&setup_polling);
170262306a36Sopenharmony_ci		cancel_delayed_work_sync(&cm_monitor_work);
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	return 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic void cm_suspend_complete(struct device *dev)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct charger_manager *cm = dev_get_drvdata(dev);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (cm_suspended)
171362306a36Sopenharmony_ci		cm_suspended = false;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (cm_timer_set) {
171662306a36Sopenharmony_ci		ktime_t remain;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci		alarm_cancel(cm_timer);
171962306a36Sopenharmony_ci		cm_timer_set = false;
172062306a36Sopenharmony_ci		remain = alarm_expires_remaining(cm_timer);
172162306a36Sopenharmony_ci		cm_suspend_duration_ms -= ktime_to_ms(remain);
172262306a36Sopenharmony_ci		schedule_work(&setup_polling);
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	_cm_monitor(cm);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	device_set_wakeup_capable(cm->dev, false);
172862306a36Sopenharmony_ci}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic const struct dev_pm_ops charger_manager_pm = {
173162306a36Sopenharmony_ci	.prepare	= cm_suspend_prepare,
173262306a36Sopenharmony_ci	.suspend_noirq	= cm_suspend_noirq,
173362306a36Sopenharmony_ci	.complete	= cm_suspend_complete,
173462306a36Sopenharmony_ci};
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic struct platform_driver charger_manager_driver = {
173762306a36Sopenharmony_ci	.driver = {
173862306a36Sopenharmony_ci		.name = "charger-manager",
173962306a36Sopenharmony_ci		.pm = &charger_manager_pm,
174062306a36Sopenharmony_ci		.of_match_table = charger_manager_match,
174162306a36Sopenharmony_ci	},
174262306a36Sopenharmony_ci	.probe = charger_manager_probe,
174362306a36Sopenharmony_ci	.remove = charger_manager_remove,
174462306a36Sopenharmony_ci	.id_table = charger_manager_id,
174562306a36Sopenharmony_ci};
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic int __init charger_manager_init(void)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	cm_wq = create_freezable_workqueue("charger_manager");
175062306a36Sopenharmony_ci	if (unlikely(!cm_wq))
175162306a36Sopenharmony_ci		return -ENOMEM;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	return platform_driver_register(&charger_manager_driver);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_cilate_initcall(charger_manager_init);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic void __exit charger_manager_cleanup(void)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	destroy_workqueue(cm_wq);
176262306a36Sopenharmony_ci	cm_wq = NULL;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	platform_driver_unregister(&charger_manager_driver);
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_cimodule_exit(charger_manager_cleanup);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ciMODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
176962306a36Sopenharmony_ciMODULE_DESCRIPTION("Charger Manager");
177062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1771