Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bee737bc authored by Chanwoo Choi's avatar Chanwoo Choi Committed by Anton Vorontsov
Browse files

charger-manager: Use EXTCON Subsystem to detect charger cables for charging



This patch support that charger-manager use EXTCON(External Connector)
Subsystem to detect the state of charger cables for enabling or disabling
charger(regulator) and select the charger cable for charging among
a number of external cable according to policy of H/W board.

Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: default avatarMyungjoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarAnton Vorontsov <anton.vorontsov@linaro.org>
parent 85a392d4
Loading
Loading
Loading
Loading
+121 −16
Original line number Original line Diff line number Diff line
@@ -271,16 +271,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
	if (enable) {
	if (enable) {
		if (cm->emergency_stop)
		if (cm->emergency_stop)
			return -EAGAIN;
			return -EAGAIN;
		err = regulator_bulk_enable(desc->num_charger_regulators,
		for (i = 0 ; i < desc->num_charger_regulators ; i++)
					desc->charger_regulators);
			regulator_enable(desc->charger_regulators[i].consumer);
	} else {
	} else {
		/*
		/*
		 * Abnormal battery state - Stop charging forcibly,
		 * Abnormal battery state - Stop charging forcibly,
		 * even if charger was enabled at the other places
		 * even if charger was enabled at the other places
		 */
		 */
		err = regulator_bulk_disable(desc->num_charger_regulators,
					desc->charger_regulators);

		for (i = 0; i < desc->num_charger_regulators; i++) {
		for (i = 0; i < desc->num_charger_regulators; i++) {
			if (regulator_is_enabled(
			if (regulator_is_enabled(
				    desc->charger_regulators[i].consumer)) {
				    desc->charger_regulators[i].consumer)) {
@@ -288,7 +285,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
					desc->charger_regulators[i].consumer);
					desc->charger_regulators[i].consumer);
				dev_warn(cm->dev,
				dev_warn(cm->dev,
					"Disable regulator(%s) forcibly.\n",
					"Disable regulator(%s) forcibly.\n",
					desc->charger_regulators[i].supply);
					desc->charger_regulators[i].regulator_name);
			}
			}
		}
		}
	}
	}
@@ -994,11 +991,77 @@ int setup_charger_manager(struct charger_global_desc *gd)
}
}
EXPORT_SYMBOL_GPL(setup_charger_manager);
EXPORT_SYMBOL_GPL(setup_charger_manager);


/**
 * charger_extcon_work - enable/diable charger according to the state
 *			of charger cable
 *
 * @work: work_struct of the function charger_extcon_work.
 */
static void charger_extcon_work(struct work_struct *work)
{
	struct charger_cable *cable =
			container_of(work, struct charger_cable, wq);

	try_charger_enable(cable->cm, cable->attached);
}

/**
 * charger_extcon_notifier - receive the state of charger cable
 *			when registered cable is attached or detached.
 *
 * @self: the notifier block of the charger_extcon_notifier.
 * @event: the cable state.
 * @ptr: the data pointer of notifier block.
 */
static int charger_extcon_notifier(struct notifier_block *self,
			unsigned long event, void *ptr)
{
	struct charger_cable *cable =
		container_of(self, struct charger_cable, nb);

	cable->attached = event;
	schedule_work(&cable->wq);

	return NOTIFY_DONE;
}

/**
 * charger_extcon_init - register external connector to use it
 *			as the charger cable
 *
 * @cm: the Charger Manager representing the battery.
 * @cable: the Charger cable representing the external connector.
 */
static int charger_extcon_init(struct charger_manager *cm,
		struct charger_cable *cable)
{
	int ret = 0;

	/*
	 * Charger manager use Extcon framework to identify
	 * the charger cable among various external connector
	 * cable (e.g., TA, USB, MHL, Dock).
	 */
	INIT_WORK(&cable->wq, charger_extcon_work);
	cable->nb.notifier_call = charger_extcon_notifier;
	ret = extcon_register_interest(&cable->extcon_dev,
			cable->extcon_name, cable->name, &cable->nb);
	if (ret < 0) {
		pr_info("Cannot register extcon_dev for %s(cable: %s).\n",
				cable->extcon_name,
				cable->name);
		ret = -EINVAL;
	}

	return ret;
}

static int charger_manager_probe(struct platform_device *pdev)
static int charger_manager_probe(struct platform_device *pdev)
{
{
	struct charger_desc *desc = dev_get_platdata(&pdev->dev);
	struct charger_desc *desc = dev_get_platdata(&pdev->dev);
	struct charger_manager *cm;
	struct charger_manager *cm;
	int ret = 0, i = 0;
	int ret = 0, i = 0;
	int j = 0;
	union power_supply_propval val;
	union power_supply_propval val;


	if (g_desc && !rtc_dev && g_desc->rtc_name) {
	if (g_desc && !rtc_dev && g_desc->rtc_name) {
@@ -1167,11 +1230,31 @@ static int charger_manager_probe(struct platform_device *pdev)
		goto err_register;
		goto err_register;
	}
	}


	ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
				 desc->charger_regulators);
		struct charger_regulator *charger
	if (ret) {
					= &desc->charger_regulators[i];
		dev_err(&pdev->dev, "Cannot get charger regulators.\n");

		goto err_bulk_get;
		charger->consumer = regulator_get(&pdev->dev,
					charger->regulator_name);
		if (charger->consumer == NULL) {
			dev_err(&pdev->dev, "Cannot find charger(%s)n",
						charger->regulator_name);
			ret = -EINVAL;
			goto err_chg_get;
		}

		for (j = 0 ; j < charger->num_cables ; j++) {
			struct charger_cable *cable = &charger->cables[j];

			ret = charger_extcon_init(cm, cable);
			if (ret < 0) {
				dev_err(&pdev->dev, "Cannot find charger(%s)n",
						charger->regulator_name);
				goto err_extcon;
			}
			cable->charger = charger;
			cable->cm = cm;
		}
	}
	}


	ret = try_charger_enable(cm, true);
	ret = try_charger_enable(cm, true);
@@ -1197,9 +1280,19 @@ static int charger_manager_probe(struct platform_device *pdev)
	return 0;
	return 0;


err_chg_enable:
err_chg_enable:
	regulator_bulk_free(desc->num_charger_regulators,
err_extcon:
			    desc->charger_regulators);
	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
err_bulk_get:
		struct charger_regulator *charger
				= &desc->charger_regulators[i];
		for (j = 0 ; j < charger->num_cables ; j++) {
			struct charger_cable *cable = &charger->cables[j];
			extcon_unregister_interest(&cable->extcon_dev);
		}
	}
err_chg_get:
	for (i = 0 ; i < desc->num_charger_regulators ; i++)
		regulator_put(desc->charger_regulators[i].consumer);

	power_supply_unregister(&cm->charger_psy);
	power_supply_unregister(&cm->charger_psy);
err_register:
err_register:
	kfree(cm->charger_psy.properties);
	kfree(cm->charger_psy.properties);
@@ -1218,6 +1311,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
{
{
	struct charger_manager *cm = platform_get_drvdata(pdev);
	struct charger_manager *cm = platform_get_drvdata(pdev);
	struct charger_desc *desc = cm->desc;
	struct charger_desc *desc = cm->desc;
	int i = 0;
	int j = 0;


	/* Remove from the list */
	/* Remove from the list */
	mutex_lock(&cm_list_mtx);
	mutex_lock(&cm_list_mtx);
@@ -1229,8 +1324,18 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
	if (delayed_work_pending(&cm_monitor_work))
	if (delayed_work_pending(&cm_monitor_work))
		cancel_delayed_work_sync(&cm_monitor_work);
		cancel_delayed_work_sync(&cm_monitor_work);


	regulator_bulk_free(desc->num_charger_regulators,
	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
			    desc->charger_regulators);
		struct charger_regulator *charger
				= &desc->charger_regulators[i];
		for (j = 0 ; j < charger->num_cables ; j++) {
			struct charger_cable *cable = &charger->cables[j];
			extcon_unregister_interest(&cable->extcon_dev);
		}
	}

	for (i = 0 ; i < desc->num_charger_regulators ; i++)
		regulator_put(desc->charger_regulators[i].consumer);

	power_supply_unregister(&cm->charger_psy);
	power_supply_unregister(&cm->charger_psy);


	try_charger_enable(cm, false);
	try_charger_enable(cm, false);
+58 −1
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@
#define _CHARGER_MANAGER_H
#define _CHARGER_MANAGER_H


#include <linux/power_supply.h>
#include <linux/power_supply.h>
#include <linux/extcon.h>


enum data_source {
enum data_source {
	CM_BATTERY_PRESENT,
	CM_BATTERY_PRESENT,
@@ -64,6 +65,62 @@ struct charger_global_desc {
	bool assume_timer_stops_in_suspend;
	bool assume_timer_stops_in_suspend;
};
};


/**
 * struct charger_cable
 * @extcon_name: the name of extcon device.
 * @name: the name of charger cable(external connector).
 * @extcon_dev: the extcon device.
 * @wq: the workqueue to control charger according to the state of
 *	charger cable. If charger cable is attached, enable charger.
 *	But if charger cable is detached, disable charger.
 * @nb: the notifier block to receive changed state from EXTCON
 *	(External Connector) when charger cable is attached/detached.
 * @attached: the state of charger cable.
 *	true: the charger cable is attached
 *	false: the charger cable is detached
 * @charger: the instance of struct charger_regulator.
 * @cm: the Charger Manager representing the battery.
 */
struct charger_cable {
	const char *extcon_name;
	const char *name;

	/* The charger-manager use Exton framework*/
	struct extcon_specific_cable_nb extcon_dev;
	struct work_struct wq;
	struct notifier_block nb;

	/* The state of charger cable */
	bool attached;

	struct charger_regulator *charger;
	struct charger_manager *cm;
};

/**
 * struct charger_regulator
 * @regulator_name: the name of regulator for using charger.
 * @consumer: the regulator consumer for the charger.
 * @cables:
 *	the array of charger cables to enable/disable charger
 *	and set current limit according to constratint data of
 *	struct charger_cable if only charger cable included
 *	in the array of charger cables is attached/detached.
 * @num_cables: the number of charger cables.
 */
struct charger_regulator {
	/* The name of regulator for charging */
	const char *regulator_name;
	struct regulator *consumer;

	/*
	 * Store constraint information related to current limit,
	 * each cable have different condition for charging.
	 */
	struct charger_cable *cables;
	int num_cables;
};

/**
/**
 * struct charger_desc
 * struct charger_desc
 * @psy_name: the name of power-supply-class for charger manager
 * @psy_name: the name of power-supply-class for charger manager
@@ -109,7 +166,7 @@ struct charger_desc {
	char **psy_charger_stat;
	char **psy_charger_stat;


	int num_charger_regulators;
	int num_charger_regulators;
	struct regulator_bulk_data *charger_regulators;
	struct charger_regulator *charger_regulators;


	char *psy_fuel_gauge;
	char *psy_fuel_gauge;