linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] power_supply: update Charger-Manager
@ 2012-02-23  4:44 Donggeun Kim
  2012-02-23  4:44 ` [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state Donggeun Kim
  2012-02-23  4:45 ` [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use Donggeun Kim
  0 siblings, 2 replies; 6+ messages in thread
From: Donggeun Kim @ 2012-02-23  4:44 UTC (permalink / raw)
  To: linux-kernel
  Cc: cbouatmailru, len.brown, pavel, rjw, rdunlap, myungjoo.ham,
	kyungmin.park, dg77.kim

Initially, the main role of Charger-Manager is to monitor battery health
while the system is in suspend-to-RAM.

This patchset updates the initial Charger-Manager to
poll the battery state in normal state and
provide function for in-kernel use.

Donggeun Kim (2):
  power_supply: Charger-Manager: poll battery health in normal state
  power_supply: Charger-Manager: provide function for in-kernel use

 Documentation/power/charger-manager.txt |   41 +++-
 drivers/power/charger-manager.c         |  387 +++++++++++++++++++++++++++++++
 include/linux/power/charger-manager.h   |   43 ++++
 3 files changed, 469 insertions(+), 2 deletions(-)

-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state
  2012-02-23  4:44 [PATCH 0/2] power_supply: update Charger-Manager Donggeun Kim
@ 2012-02-23  4:44 ` Donggeun Kim
  2012-02-29 23:58   ` Rafael J. Wysocki
  2012-02-23  4:45 ` [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use Donggeun Kim
  1 sibling, 1 reply; 6+ messages in thread
From: Donggeun Kim @ 2012-02-23  4:44 UTC (permalink / raw)
  To: linux-kernel
  Cc: cbouatmailru, len.brown, pavel, rjw, rdunlap, myungjoo.ham,
	kyungmin.park, dg77.kim

Charger-Manager needs to check battery health in normal state
as well as suspend-to-RAM state.
When the battery is fully charged,
Charger-Manager needs to determine when the chargers restart charging.

This patch allows Charger-Manager to monitor battery health in normal state
and handle operation for chargers after battery is fully charged.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 Documentation/power/charger-manager.txt |   25 ++++-
 drivers/power/charger-manager.c         |  230 +++++++++++++++++++++++++++++++
 include/linux/power/charger-manager.h   |   25 ++++
 3 files changed, 279 insertions(+), 1 deletions(-)

diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
index fdcca99..47f7fdc 100644
--- a/Documentation/power/charger-manager.txt
+++ b/Documentation/power/charger-manager.txt
@@ -44,6 +44,12 @@ Charger Manager supports the following:
 	Normally, the platform will need to resume and suspend some devices
 	that are used by Charger Manager.
 
+* Support for premature full-battery event handling
+	If the battery voltage drops by "fullbatt_vchkdrop_uV" after
+	"fullbatt_vchkdrop_ms" from the full-battery event, the framework
+	restarts charging. This check is also performed while suspended by
+	setting wakeup time accordingly and using suspend_again.
+
 2. Global Charger-Manager Data related with suspend_again
 ========================================================
 In order to setup Charger Manager with suspend-again feature
@@ -55,7 +61,7 @@ if there are multiple batteries. If there are multiple batteries, the
 multiple instances of Charger Manager share the same charger_global_desc
 and it will manage in-suspend monitoring for all instances of Charger Manager.
 
-The user needs to provide all the two entries properly in order to activate
+The user needs to provide all the three entries properly in order to activate
 in-suspend monitoring:
 
 struct charger_global_desc {
@@ -74,6 +80,11 @@ bool (*rtc_only_wakeup)(void);
 	same struct. If there is any other wakeup source triggered the
 	wakeup, it should return false. If the "rtc" is the only wakeup
 	reason, it should return true.
+
+bool assume_timer_stops_in_suspend;
+	: if true, Charger Manager assumes that
+	the timer (CM uses jiffies as timer) stops during suspend. Then, CM
+	assumes that the suspend-duration is same as the alarm length.
 };
 
 3. How to setup suspend_again
@@ -111,6 +122,16 @@ enum polling_modes polling_mode;
 	  CM_POLL_CHARGING_ONLY: poll this battery if and only if the
 				 battery is being charged.
 
+unsigned int fullbatt_vchkdrop_ms;
+unsigned int fullbatt_vchkdrop_uV;
+	: If both have non-zero values, Charger Manager will check the
+	battery voltage drop fullbatt_vchkdrop_ms after the battery is fully
+	charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger
+	Manager will try to recharge the battery by disabling and enabling
+	chargers. Recharge with voltage drop condition only (without delay
+	condition) is needed to be implemented with hardware interrupts from
+	fuel gauges or charger devices/chips.
+
 unsigned int fullbatt_uV;
 	: If specified with a non-zero value, Charger Manager assumes
 	that the battery is full (capacity = 100) if the battery is not being
@@ -122,6 +143,8 @@ unsigned int polling_interval_ms;
 	this battery every polling_interval_ms or more frequently.
 
 enum data_source battery_present;
+	: CM_ASSUME_ALWAYS_TRUE: assume that the battery exists.
+	CM_ASSUME_ALWAYS_FALSE: assume that the battery does not exists.
 	CM_FUEL_GAUGE: get battery presence information from fuel gauge.
 	CM_CHARGER_STAT: get battery presence from chargers.
 
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 88fd971..9ecde4c 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -57,6 +57,12 @@ static bool cm_suspended;
 static bool cm_rtc_set;
 static unsigned long cm_suspend_duration_ms;
 
+/* About normal (not suspended) monitoring */
+static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
+static unsigned long next_polling; /* Next appointed polling time */
+static struct workqueue_struct *cm_wq; /* init at driver add */
+static struct delayed_work cm_monitor_work; /* init at driver add */
+
 /* Global charger-manager description */
 static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
 
@@ -71,6 +77,12 @@ static bool is_batt_present(struct charger_manager *cm)
 	int i, ret;
 
 	switch (cm->desc->battery_present) {
+	case CM_ASSUME_ALWAYS_TRUE:
+		present = true;
+		break;
+	case CM_ASSUME_ALWAYS_FALSE:
+		present = false;
+		break;
 	case CM_FUEL_GAUGE:
 		ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
 				POWER_SUPPLY_PROP_PRESENT, &val);
@@ -282,6 +294,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
 }
 
 /**
+ * try_charger_restart - Restart charging.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Restart charging by turning off and on the charger.
+ */
+static int try_charger_restart(struct charger_manager *cm)
+{
+	int err;
+
+	if (cm->emergency_stop)
+		return -EAGAIN;
+
+	err = try_charger_enable(cm, false);
+	if (err)
+		return err;
+
+	return try_charger_enable(cm, true);
+}
+
+/**
  * uevent_notify - Let users know something has changed.
  * @cm: the Charger Manager representing the battery.
  * @event: the event string.
@@ -339,6 +371,47 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
 }
 
 /**
+ * fullbatt_vchk - Check voltage drop some times after "FULL" event.
+ * @work: the work_struct appointing the function
+ *
+ * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
+ * charger_desc, Charger Manager checks voltage drop after the battery
+ * "FULL" event. It checks whether the voltage has dropped more than
+ * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
+ */
+static void fullbatt_vchk(struct work_struct *work)
+{
+	struct delayed_work *dwork =
+		container_of(work, struct delayed_work, work);
+	struct charger_manager *cm = container_of(dwork,
+			struct charger_manager, fullbatt_vchk_work);
+	struct charger_desc *desc = cm->desc;
+	int batt_uV, err, diff;
+
+	/* remove the appointment for fullbatt_vchk */
+	cm->fullbatt_vchk_jiffies_at = 0;
+
+	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+		return;
+
+	err = get_batt_uV(cm, &batt_uV);
+	if (err) {
+		dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
+		return;
+	}
+
+	diff = cm->fullbatt_vchk_uV;
+	diff -= batt_uV;
+
+	dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
+
+	if (diff > desc->fullbatt_vchkdrop_uV) {
+		try_charger_restart(cm);
+		uevent_notify(cm, "Recharge");
+	}
+}
+
+/**
  * _cm_monitor - Monitor the temperature and return true for exceptions.
  * @cm: the Charger Manager representing the battery.
  *
@@ -395,6 +468,68 @@ static bool cm_monitor(void)
 	return stop;
 }
 
+/**
+ * _setup_polling - Setup the next instance of polling.
+ * @work: work_struct of the function _setup_polling.
+ */
+static void _setup_polling(struct work_struct *work)
+{
+	unsigned long min = ULONG_MAX;
+	struct charger_manager *cm;
+	bool keep_polling = false;
+	unsigned long _next_polling;
+
+	mutex_lock(&cm_list_mtx);
+
+	list_for_each_entry(cm, &cm_list, entry) {
+		if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
+			keep_polling = true;
+
+			if (min > cm->desc->polling_interval_ms)
+				min = cm->desc->polling_interval_ms;
+		}
+	}
+
+	polling_jiffy = msecs_to_jiffies(min);
+	if (polling_jiffy <= CM_JIFFIES_SMALL)
+		polling_jiffy = CM_JIFFIES_SMALL + 1;
+
+	if (!keep_polling)
+		polling_jiffy = ULONG_MAX;
+	if (polling_jiffy == ULONG_MAX)
+		goto out;
+
+	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
+			    ". try it later. %s\n", __func__);
+
+	_next_polling = jiffies + polling_jiffy;
+
+	if (!delayed_work_pending(&cm_monitor_work) ||
+	    (delayed_work_pending(&cm_monitor_work) &&
+	     time_after(next_polling, _next_polling))) {
+		cancel_delayed_work(&cm_monitor_work);
+		next_polling = jiffies + polling_jiffy;
+		queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+	}
+
+out:
+	mutex_unlock(&cm_list_mtx);
+}
+static DECLARE_WORK(setup_polling, _setup_polling);
+
+/**
+ * cm_monitor_poller - The Monitor / Poller.
+ * @work: work_struct of the function cm_monitor_poller
+ *
+ * During non-suspended state, cm_monitor_poller is used to poll and monitor
+ * the batteries.
+ */
+static void cm_monitor_poller(struct work_struct *work)
+{
+	cm_monitor();
+	schedule_work(&setup_polling);
+}
+
 static int charger_get_property(struct power_supply *psy,
 		enum power_supply_property psp,
 		union power_supply_propval *val)
@@ -616,6 +751,20 @@ static bool cm_setup_timer(void)
 	mutex_lock(&cm_list_mtx);
 
 	list_for_each_entry(cm, &cm_list, entry) {
+		unsigned int fbchk_ms = 0;
+
+		/* fullbatt_vchk is required. setup timer for that */
+		if (cm->fullbatt_vchk_jiffies_at) {
+			fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
+						    - jiffies);
+			if (cm->fullbatt_vchk_jiffies_at <= jiffies ||
+				msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
+				fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+				fbchk_ms = 0;
+			}
+		}
+		CM_MIN_VALID(wakeup_ms, fbchk_ms);
+
 		/* Skip if polling is not required for this CM */
 		if (!is_polling_required(cm) && !cm->emergency_stop)
 			continue;
@@ -675,6 +824,27 @@ static bool cm_setup_timer(void)
 	return false;
 }
 
+static bool _cm_fbchk_in_suspend(struct charger_manager *cm)
+{
+	unsigned long jiffy_now = jiffies;
+
+	if (!cm->fullbatt_vchk_jiffies_at)
+		return false;
+
+	if (g_desc && g_desc->assume_timer_stops_in_suspend)
+		jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
+
+	/* Execute now if it's going to be executed not too long after */
+	jiffy_now += CM_JIFFIES_SMALL;
+
+	if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) {
+		fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+		return true;
+	}
+
+	return false;
+}
+
 /**
  * cm_suspend_again - Determine whether suspend again or not
  *
@@ -696,6 +866,8 @@ bool cm_suspend_again(void)
 	ret = true;
 	mutex_lock(&cm_list_mtx);
 	list_for_each_entry(cm, &cm_list, entry) {
+		_cm_fbchk_in_suspend(cm);
+
 		if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
 		    cm->status_save_batt != is_batt_present(cm))
 			ret = false;
@@ -797,6 +969,21 @@ static int charger_manager_probe(struct platform_device *pdev)
 	memcpy(cm->desc, desc, sizeof(struct charger_desc));
 	cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
 
+	/*
+	 * The following two do not need to be errors.
+	 * Users may intentionally ignore those two features.
+	 */
+	if (desc->fullbatt_uV == 0) {
+		dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
+					" as it is not supplied.");
+	}
+	if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
+		dev_info(&pdev->dev, "Disabling full-battery voltage drop "
+				"checking mechanism as it is not supplied.");
+		desc->fullbatt_vchkdrop_ms = 0;
+		desc->fullbatt_vchkdrop_uV = 0;
+	}
+
 	if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
 		ret = -EINVAL;
 		dev_err(&pdev->dev, "charger_regulators undefined.\n");
@@ -905,6 +1092,8 @@ static int charger_manager_probe(struct platform_device *pdev)
 		cm->charger_psy.num_properties++;
 	}
 
+	INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
+
 	ret = power_supply_register(NULL, &cm->charger_psy);
 	if (ret) {
 		dev_err(&pdev->dev, "Cannot register charger-manager with"
@@ -930,6 +1119,8 @@ static int charger_manager_probe(struct platform_device *pdev)
 	list_add(&cm->entry, &cm_list);
 	mutex_unlock(&cm_list_mtx);
 
+	schedule_work(&setup_polling);
+
 	return 0;
 
 err_chg_enable:
@@ -961,10 +1152,14 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
 	list_del(&cm->entry);
 	mutex_unlock(&cm_list_mtx);
 
+	schedule_work(&setup_polling);
+
 	if (desc->charger_regulators)
 		regulator_bulk_free(desc->num_charger_regulators,
 					desc->charger_regulators);
 
+	try_charger_enable(cm, false);
+
 	power_supply_unregister(&cm->charger_psy);
 	kfree(cm->charger_psy.properties);
 	kfree(cm->charger_stat);
@@ -1007,6 +1202,7 @@ static int cm_suspend_prepare(struct device *dev)
 		cm_suspended = true;
 	}
 
+	cancel_delayed_work(&cm->fullbatt_vchk_work);
 	cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
 	cm->status_save_batt = is_batt_present(cm);
 
@@ -1036,6 +1232,34 @@ static void cm_suspend_complete(struct device *dev)
 		cm_rtc_set = false;
 	}
 
+	/* Re-enqueue delayed work (fullbatt_vchk_work) */
+	if (cm->fullbatt_vchk_jiffies_at) {
+		unsigned long delay = 0;
+		unsigned long now = jiffies;
+
+		if (time_after_eq(now + CM_JIFFIES_SMALL,
+				  cm->fullbatt_vchk_jiffies_at)) {
+			delay = (unsigned long)((long)(now + CM_JIFFIES_SMALL)
+				- (long)(cm->fullbatt_vchk_jiffies_at));
+			delay = jiffies_to_msecs(delay);
+		} else {
+			delay = 0;
+		}
+
+		/*
+		 * Account for cm_suspend_duration_ms if
+		 * assume_timer_stops_in_suspend is active
+		 */
+		if (g_desc && g_desc->assume_timer_stops_in_suspend) {
+			if (delay > cm_suspend_duration_ms)
+				delay -= cm_suspend_duration_ms;
+			else
+				delay = 0;
+		}
+
+		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+				   msecs_to_jiffies(delay));
+	}
 	uevent_notify(cm, NULL);
 }
 
@@ -1057,12 +1281,18 @@ static struct platform_driver charger_manager_driver = {
 
 static int __init charger_manager_init(void)
 {
+	cm_wq = create_freezable_workqueue("charger_manager");
+	INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
+
 	return platform_driver_register(&charger_manager_driver);
 }
 late_initcall(charger_manager_init);
 
 static void __exit charger_manager_cleanup(void)
 {
+	destroy_workqueue(cm_wq);
+	cm_wq = NULL;
+
 	platform_driver_unregister(&charger_manager_driver);
 }
 module_exit(charger_manager_cleanup);
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
index 4f75e53..78b9865 100644
--- a/include/linux/power/charger-manager.h
+++ b/include/linux/power/charger-manager.h
@@ -18,6 +18,8 @@
 #include <linux/power_supply.h>
 
 enum data_source {
+	CM_ASSUME_ALWAYS_TRUE,
+	CM_ASSUME_ALWAYS_FALSE,
 	CM_FUEL_GAUGE,
 	CM_CHARGER_STAT,
 };
@@ -38,11 +40,18 @@ enum polling_modes {
  *	rtc_only_wakeup() returning false.
  *	If the RTC given to CM is the only wakeup reason,
  *	rtc_only_wakeup should return true.
+ * @assume_timer_stops_in_suspend:
+ *	Assume that the jiffy timer stops in suspend-to-RAM.
+ *	When enabled, CM does not rely on jiffies value in
+ *	suspend_again and assumes that jiffies value does not
+ *	change during suspend.
  */
 struct charger_global_desc {
 	char *rtc_name;
 
 	bool (*rtc_only_wakeup)(void);
+
+	bool assume_timer_stops_in_suspend;
 };
 
 /**
@@ -50,6 +59,11 @@ struct charger_global_desc {
  * @psy_name: the name of power-supply-class for charger manager
  * @polling_mode:
  *	Determine which polling mode will be used
+ * @fullbatt_vchkdrop_ms:
+ * @fullbatt_vchkdrop_uV:
+ *	Check voltage drop after the battery is fully charged.
+ *	If it has dropped more than fullbatt_vchkdrop_uV after
+ *	fullbatt_vchkdrop_ms, CM will restart charging.
  * @fullbatt_uV: voltage in microvolt
  *	If it is not being charged and VBATT >= fullbatt_uV,
  *	it is assumed to be full.
@@ -76,6 +90,8 @@ struct charger_desc {
 	enum polling_modes polling_mode;
 	unsigned int polling_interval_ms;
 
+	unsigned int fullbatt_vchkdrop_ms;
+	unsigned int fullbatt_vchkdrop_uV;
 	unsigned int fullbatt_uV;
 
 	enum data_source battery_present;
@@ -101,6 +117,11 @@ struct charger_desc {
  * @fuel_gauge: power_supply for fuel gauge
  * @charger_stat: array of power_supply for chargers
  * @charger_enabled: the state of charger
+ * @fullbatt_vchk_jiffies_at:
+ *	jiffies at the time full battery check will occur.
+ * @fullbatt_vchk_uV: voltage in microvolt
+ *	criteria for full battery
+ * @fullbatt_vchk_work: work queue for full battery check
  * @emergency_stop:
  *	When setting true, stop charging
  * @last_temp_mC: the measured temperature in milli-Celsius
@@ -121,6 +142,10 @@ struct charger_manager {
 
 	bool charger_enabled;
 
+	unsigned long fullbatt_vchk_jiffies_at;
+	unsigned int fullbatt_vchk_uV;
+	struct delayed_work fullbatt_vchk_work;
+
 	int emergency_stop;
 	int last_temp_mC;
 
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use
  2012-02-23  4:44 [PATCH 0/2] power_supply: update Charger-Manager Donggeun Kim
  2012-02-23  4:44 ` [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state Donggeun Kim
@ 2012-02-23  4:45 ` Donggeun Kim
  2012-03-01  0:13   ` Rafael J. Wysocki
  2012-03-04 23:00   ` Rafael J. Wysocki
  1 sibling, 2 replies; 6+ messages in thread
From: Donggeun Kim @ 2012-02-23  4:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: cbouatmailru, len.brown, pavel, rjw, rdunlap, myungjoo.ham,
	kyungmin.park, dg77.kim

By using cm_notify_event function,
charger driver can report several charger events
(e.g. battery full and external power in/out, etc) to Charger-Manager.
Charger-Manager can properly and immediately control chargers
by the reported event.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 Documentation/power/charger-manager.txt |   16 +++-
 drivers/power/charger-manager.c         |  157 +++++++++++++++++++++++++++++++
 include/linux/power/charger-manager.h   |   18 ++++
 3 files changed, 190 insertions(+), 1 deletions(-)

diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
index 47f7fdc..f6566c7 100644
--- a/Documentation/power/charger-manager.txt
+++ b/Documentation/power/charger-manager.txt
@@ -50,6 +50,10 @@ Charger Manager supports the following:
 	restarts charging. This check is also performed while suspended by
 	setting wakeup time accordingly and using suspend_again.
 
+* Support for uevent-notify
+	With the charger-related events, the device sends
+	notification to users with UEVENT.
+
 2. Global Charger-Manager Data related with suspend_again
 ========================================================
 In order to setup Charger Manager with suspend-again feature
@@ -174,7 +178,17 @@ bool measure_battery_temp;
 	the value of measure_battery_temp.
 };
 
-5. Other Considerations
+5. Notify Charger-Manager of charger events: cm_notify_event()
+=========================================================
+If there is an charger event is required to notify
+Charger Manager, a charger device driver that triggers the event can call
+cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
+In the function, psy is the charger driver's power_supply pointer, which is
+associated with Charger-Manager. The parameter "type"
+is the same as irq's type (enum cm_event_types). The event message "msg" is
+optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
+
+6. Other Considerations
 =======================
 
 At the charger/battery-related events such as battery-pulled-out,
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 9ecde4c..8cf0d42 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -23,6 +23,16 @@
 #include <linux/power/charger-manager.h>
 #include <linux/regulator/consumer.h>
 
+static const char * const default_event_names[] = {
+	[CM_EVENT_UNDESCRIBED] = "Undescribed",
+	[CM_EVENT_BATT_FULL] = "Battery Full",
+	[CM_EVENT_BATT_IN] = "Battery Inserted",
+	[CM_EVENT_BATT_OUT] = "Battery Pulled Out",
+	[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
+	[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
+	[CM_EVENT_OTHERS] = "Other battery events"
+};
+
 /*
  * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
  * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
@@ -530,6 +540,68 @@ static void cm_monitor_poller(struct work_struct *work)
 	schedule_work(&setup_polling);
 }
 
+/**
+ * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
+ * @cm: the Charger Manager representing the battery.
+ */
+static void fullbatt_handler(struct charger_manager *cm)
+{
+	struct charger_desc *desc = cm->desc;
+
+	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+		goto out;
+
+	if (cm_suspended)
+		cm->cancel_suspend = true;
+
+	cancel_delayed_work(&cm->fullbatt_vchk_work);
+	queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+			   msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
+	cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
+				       desc->fullbatt_vchkdrop_ms);
+
+	if (cm->fullbatt_vchk_jiffies_at == 0)
+		cm->fullbatt_vchk_jiffies_at = 1;
+
+out:
+	dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
+	uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
+}
+
+/**
+ * battout_handler - Event handler for CM_EVENT_BATT_OUT
+ * @cm: the Charger Manager representing the battery.
+ */
+static void battout_handler(struct charger_manager *cm)
+{
+	if (cm_suspended)
+		cm->cancel_suspend = true;
+
+	if (!is_batt_present(cm)) {
+		dev_emerg(cm->dev, "Battery Pulled Out!\n");
+		uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
+	} else {
+		uevent_notify(cm, "Battery Reinserted?");
+	}
+}
+
+/**
+ * misc_event_handler - Handler for other evnets
+ * @cm: the Charger Manager representing the battery.
+ * @type: the Charger Manager representing the battery.
+ */
+static void misc_event_handler(struct charger_manager *cm,
+			enum cm_event_types type)
+{
+	if (cm_suspended)
+		cm->cancel_suspend = true;
+
+	if (!delayed_work_pending(&cm_monitor_work) &&
+	    is_polling_required(cm) && cm->desc->polling_interval_ms)
+		schedule_work(&setup_polling);
+	uevent_notify(cm, default_event_names[type]);
+}
+
 static int charger_get_property(struct power_supply *psy,
 		enum power_supply_property psp,
 		union power_supply_propval *val)
@@ -1175,6 +1247,20 @@ static const struct platform_device_id charger_manager_id[] = {
 };
 MODULE_DEVICE_TABLE(platform, charger_manager_id);
 
+static int cm_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct charger_manager *cm = platform_get_drvdata(pdev);
+
+	if (cm->cancel_suspend) {
+		cm->cancel_suspend = false;
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
 static int cm_suspend_prepare(struct device *dev)
 {
 	struct platform_device *pdev = container_of(dev, struct platform_device,
@@ -1260,11 +1346,13 @@ static void cm_suspend_complete(struct device *dev)
 		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
 				   msecs_to_jiffies(delay));
 	}
+	cm->cancel_suspend = false;
 	uevent_notify(cm, NULL);
 }
 
 static const struct dev_pm_ops charger_manager_pm = {
 	.prepare	= cm_suspend_prepare,
+	.suspend_noirq	= cm_suspend_noirq,
 	.complete	= cm_suspend_complete,
 };
 
@@ -1297,6 +1385,75 @@ static void __exit charger_manager_cleanup(void)
 }
 module_exit(charger_manager_cleanup);
 
+/**
+ * find_power_supply - find the associated power_supply of charger
+ * @cm: the Charger Manager representing the battery
+ * @psy: pointer to instance of charger's power_supply
+ */
+static bool find_power_supply(struct charger_manager *cm,
+			struct power_supply *psy)
+{
+	int i;
+	bool found = false;
+
+	for (i = 0; cm->charger_stat[i]; i++) {
+		if (psy == cm->charger_stat[i]) {
+			found = true;
+			break;
+		}
+	}
+
+	return found;
+}
+
+/**
+ * cm_notify_event - charger driver notify Charger Manager of charger event
+ * @psy: pointer to instance of charger's power_supply
+ * @type: type of charger event
+ * @msg: optional message passed to uevent_notify fuction
+ */
+void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
+		     char *msg)
+{
+	struct charger_manager *cm;
+	bool found_power_supply = false;
+
+	if (psy == NULL)
+		return;
+
+	mutex_lock(&cm_list_mtx);
+	list_for_each_entry(cm, &cm_list, entry) {
+		found_power_supply = find_power_supply(cm, psy);
+		if (found_power_supply)
+			break;
+	}
+	mutex_unlock(&cm_list_mtx);
+
+	if (!found_power_supply)
+		return;
+
+	switch (type) {
+	case CM_EVENT_BATT_FULL:
+		fullbatt_handler(cm);
+		break;
+	case CM_EVENT_BATT_OUT:
+		battout_handler(cm);
+		break;
+	case CM_EVENT_BATT_IN:
+	case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
+		misc_event_handler(cm, type);
+		break;
+	case CM_EVENT_UNDESCRIBED:
+	case CM_EVENT_OTHERS:
+		uevent_notify(cm, msg ? msg : default_event_names[type]);
+		break;
+	default:
+		dev_err(cm->dev, "%s type not specified.\n", __func__);
+		break;
+	}
+}
+EXPORT_SYMBOL_GPL(cm_notify_event);
+
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 MODULE_DESCRIPTION("Charger Manager");
 MODULE_LICENSE("GPL");
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
index 78b9865..086917c 100644
--- a/include/linux/power/charger-manager.h
+++ b/include/linux/power/charger-manager.h
@@ -31,6 +31,16 @@ enum polling_modes {
 	CM_POLL_CHARGING_ONLY,
 };
 
+enum cm_event_types {
+	CM_EVENT_UNDESCRIBED = 0,
+	CM_EVENT_BATT_FULL,
+	CM_EVENT_BATT_IN,
+	CM_EVENT_BATT_OUT,
+	CM_EVENT_EXT_PWR_IN_OUT,
+	CM_EVENT_CHG_START_STOP,
+	CM_EVENT_OTHERS,
+};
+
 /**
  * struct charger_global_desc
  * @rtc_name: the name of RTC used to wake up the system from suspend.
@@ -116,6 +126,7 @@ struct charger_desc {
  * @desc: instance of charger_desc
  * @fuel_gauge: power_supply for fuel gauge
  * @charger_stat: array of power_supply for chargers
+ * @cancel_suspend: true if there is a pending charger event
  * @charger_enabled: the state of charger
  * @fullbatt_vchk_jiffies_at:
  *	jiffies at the time full battery check will occur.
@@ -140,6 +151,7 @@ struct charger_manager {
 	struct power_supply *fuel_gauge;
 	struct power_supply **charger_stat;
 
+	bool cancel_suspend;
 	bool charger_enabled;
 
 	unsigned long fullbatt_vchk_jiffies_at;
@@ -159,6 +171,8 @@ struct charger_manager {
 #ifdef CONFIG_CHARGER_MANAGER
 extern int setup_charger_manager(struct charger_global_desc *gd);
 extern bool cm_suspend_again(void);
+extern void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
+			    char *msg); /* msg: optional */
 #else
 static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
 { }
@@ -167,6 +181,10 @@ static bool __maybe_unused cm_suspend_again(void)
 {
 	return false;
 }
+
+static void __maybe_unused cm_notify_event(struct power_supply *psy,
+					   enum cm_event_types type, char *msg)
+{ }
 #endif
 
 #endif /* _CHARGER_MANAGER_H */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state
  2012-02-23  4:44 ` [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state Donggeun Kim
@ 2012-02-29 23:58   ` Rafael J. Wysocki
  0 siblings, 0 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2012-02-29 23:58 UTC (permalink / raw)
  To: Donggeun Kim
  Cc: linux-kernel, cbouatmailru, len.brown, pavel, rdunlap,
	myungjoo.ham, kyungmin.park

On Thursday, February 23, 2012, Donggeun Kim wrote:
> Charger-Manager needs to check battery health in normal state
> as well as suspend-to-RAM state.
> When the battery is fully charged,
> Charger-Manager needs to determine when the chargers restart charging.
> 
> This patch allows Charger-Manager to monitor battery health in normal state
> and handle operation for chargers after battery is fully charged.
> 
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  Documentation/power/charger-manager.txt |   25 ++++-
>  drivers/power/charger-manager.c         |  230 +++++++++++++++++++++++++++++++
>  include/linux/power/charger-manager.h   |   25 ++++
>  3 files changed, 279 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
> index fdcca99..47f7fdc 100644
> --- a/Documentation/power/charger-manager.txt
> +++ b/Documentation/power/charger-manager.txt
> @@ -44,6 +44,12 @@ Charger Manager supports the following:
>  	Normally, the platform will need to resume and suspend some devices
>  	that are used by Charger Manager.
>  
> +* Support for premature full-battery event handling
> +	If the battery voltage drops by "fullbatt_vchkdrop_uV" after
> +	"fullbatt_vchkdrop_ms" from the full-battery event, the framework
> +	restarts charging. This check is also performed while suspended by
> +	setting wakeup time accordingly and using suspend_again.
> +
>  2. Global Charger-Manager Data related with suspend_again
>  ========================================================
>  In order to setup Charger Manager with suspend-again feature
> @@ -55,7 +61,7 @@ if there are multiple batteries. If there are multiple batteries, the
>  multiple instances of Charger Manager share the same charger_global_desc
>  and it will manage in-suspend monitoring for all instances of Charger Manager.
>  
> -The user needs to provide all the two entries properly in order to activate
> +The user needs to provide all the three entries properly in order to activate
>  in-suspend monitoring:
>  
>  struct charger_global_desc {
> @@ -74,6 +80,11 @@ bool (*rtc_only_wakeup)(void);
>  	same struct. If there is any other wakeup source triggered the
>  	wakeup, it should return false. If the "rtc" is the only wakeup
>  	reason, it should return true.
> +
> +bool assume_timer_stops_in_suspend;
> +	: if true, Charger Manager assumes that
> +	the timer (CM uses jiffies as timer) stops during suspend. Then, CM
> +	assumes that the suspend-duration is same as the alarm length.

The description here is not 100% clear to me.  Is it supposed to mean
that if assume_timer_stops_in_suspend is set, the charger manager will assume
that there won't be timer events while suspended?

>  };
>  
>  3. How to setup suspend_again
> @@ -111,6 +122,16 @@ enum polling_modes polling_mode;
>  	  CM_POLL_CHARGING_ONLY: poll this battery if and only if the
>  				 battery is being charged.
>  
> +unsigned int fullbatt_vchkdrop_ms;
> +unsigned int fullbatt_vchkdrop_uV;
> +	: If both have non-zero values, Charger Manager will check the
> +	battery voltage drop fullbatt_vchkdrop_ms after the battery is fully
> +	charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger
> +	Manager will try to recharge the battery by disabling and enabling
> +	chargers. Recharge with voltage drop condition only (without delay
> +	condition) is needed to be implemented with hardware interrupts from
> +	fuel gauges or charger devices/chips.
> +
>  unsigned int fullbatt_uV;
>  	: If specified with a non-zero value, Charger Manager assumes
>  	that the battery is full (capacity = 100) if the battery is not being
> @@ -122,6 +143,8 @@ unsigned int polling_interval_ms;
>  	this battery every polling_interval_ms or more frequently.
>  
>  enum data_source battery_present;
> +	: CM_ASSUME_ALWAYS_TRUE: assume that the battery exists.

I'd call that CM_BATTERY_PRESENT

> +	CM_ASSUME_ALWAYS_FALSE: assume that the battery does not exists.

And that CM_NO_BATTERY

>  	CM_FUEL_GAUGE: get battery presence information from fuel gauge.
>  	CM_CHARGER_STAT: get battery presence from chargers.
>  
> diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
> index 88fd971..9ecde4c 100644
> --- a/drivers/power/charger-manager.c
> +++ b/drivers/power/charger-manager.c
> @@ -57,6 +57,12 @@ static bool cm_suspended;
>  static bool cm_rtc_set;
>  static unsigned long cm_suspend_duration_ms;
>  
> +/* About normal (not suspended) monitoring */
> +static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
> +static unsigned long next_polling; /* Next appointed polling time */
> +static struct workqueue_struct *cm_wq; /* init at driver add */
> +static struct delayed_work cm_monitor_work; /* init at driver add */
> +
>  /* Global charger-manager description */
>  static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
>  
> @@ -71,6 +77,12 @@ static bool is_batt_present(struct charger_manager *cm)
>  	int i, ret;
>  
>  	switch (cm->desc->battery_present) {
> +	case CM_ASSUME_ALWAYS_TRUE:
> +		present = true;
> +		break;
> +	case CM_ASSUME_ALWAYS_FALSE:
> +		present = false;

You don't have to do preset = false here, because it's false already.

> +		break;
>  	case CM_FUEL_GAUGE:
>  		ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
>  				POWER_SUPPLY_PROP_PRESENT, &val);
> @@ -282,6 +294,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
>  }
>  
>  /**
> + * try_charger_restart - Restart charging.
> + * @cm: the Charger Manager representing the battery.
> + *
> + * Restart charging by turning off and on the charger.
> + */
> +static int try_charger_restart(struct charger_manager *cm)
> +{
> +	int err;
> +
> +	if (cm->emergency_stop)
> +		return -EAGAIN;
> +
> +	err = try_charger_enable(cm, false);
> +	if (err)
> +		return err;
> +
> +	return try_charger_enable(cm, true);
> +}
> +
> +/**
>   * uevent_notify - Let users know something has changed.
>   * @cm: the Charger Manager representing the battery.
>   * @event: the event string.
> @@ -339,6 +371,47 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
>  }
>  
>  /**
> + * fullbatt_vchk - Check voltage drop some times after "FULL" event.
> + * @work: the work_struct appointing the function
> + *
> + * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
> + * charger_desc, Charger Manager checks voltage drop after the battery
> + * "FULL" event. It checks whether the voltage has dropped more than
> + * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
> + */
> +static void fullbatt_vchk(struct work_struct *work)
> +{
> +	struct delayed_work *dwork =
> +		container_of(work, struct delayed_work, work);

There's the to_delayed_work() function for that.

> +	struct charger_manager *cm = container_of(dwork,
> +			struct charger_manager, fullbatt_vchk_work);
> +	struct charger_desc *desc = cm->desc;
> +	int batt_uV, err, diff;
> +
> +	/* remove the appointment for fullbatt_vchk */
> +	cm->fullbatt_vchk_jiffies_at = 0;
> +
> +	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
> +		return;
> +
> +	err = get_batt_uV(cm, &batt_uV);
> +	if (err) {
> +		dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
> +		return;
> +	}
> +
> +	diff = cm->fullbatt_vchk_uV;
> +	diff -= batt_uV;
> +
> +	dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
> +
> +	if (diff > desc->fullbatt_vchkdrop_uV) {
> +		try_charger_restart(cm);
> +		uevent_notify(cm, "Recharge");
> +	}
> +}
> +
> +/**
>   * _cm_monitor - Monitor the temperature and return true for exceptions.
>   * @cm: the Charger Manager representing the battery.
>   *
> @@ -395,6 +468,68 @@ static bool cm_monitor(void)
>  	return stop;
>  }
>  
> +/**
> + * _setup_polling - Setup the next instance of polling.
> + * @work: work_struct of the function _setup_polling.
> + */
> +static void _setup_polling(struct work_struct *work)
> +{
> +	unsigned long min = ULONG_MAX;
> +	struct charger_manager *cm;
> +	bool keep_polling = false;
> +	unsigned long _next_polling;
> +
> +	mutex_lock(&cm_list_mtx);
> +
> +	list_for_each_entry(cm, &cm_list, entry) {
> +		if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
> +			keep_polling = true;
> +
> +			if (min > cm->desc->polling_interval_ms)
> +				min = cm->desc->polling_interval_ms;
> +		}
> +	}
> +
> +	polling_jiffy = msecs_to_jiffies(min);
> +	if (polling_jiffy <= CM_JIFFIES_SMALL)
> +		polling_jiffy = CM_JIFFIES_SMALL + 1;
> +
> +	if (!keep_polling)
> +		polling_jiffy = ULONG_MAX;
> +	if (polling_jiffy == ULONG_MAX)
> +		goto out;
> +
> +	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
> +			    ". try it later. %s\n", __func__);
> +
> +	_next_polling = jiffies + polling_jiffy;
> +
> +	if (!delayed_work_pending(&cm_monitor_work) ||
> +	    (delayed_work_pending(&cm_monitor_work) &&
> +	     time_after(next_polling, _next_polling))) {
> +		cancel_delayed_work(&cm_monitor_work);

Shouldn't that be cancel_delayes_work_sync() ?

> +		next_polling = jiffies + polling_jiffy;
> +		queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
> +	}
> +
> +out:
> +	mutex_unlock(&cm_list_mtx);
> +}
> +static DECLARE_WORK(setup_polling, _setup_polling);
> +
> +/**
> + * cm_monitor_poller - The Monitor / Poller.
> + * @work: work_struct of the function cm_monitor_poller
> + *
> + * During non-suspended state, cm_monitor_poller is used to poll and monitor
> + * the batteries.
> + */
> +static void cm_monitor_poller(struct work_struct *work)
> +{
> +	cm_monitor();
> +	schedule_work(&setup_polling);
> +}
> +
>  static int charger_get_property(struct power_supply *psy,
>  		enum power_supply_property psp,
>  		union power_supply_propval *val)
> @@ -616,6 +751,20 @@ static bool cm_setup_timer(void)
>  	mutex_lock(&cm_list_mtx);
>  
>  	list_for_each_entry(cm, &cm_list, entry) {
> +		unsigned int fbchk_ms = 0;
> +
> +		/* fullbatt_vchk is required. setup timer for that */
> +		if (cm->fullbatt_vchk_jiffies_at) {
> +			fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
> +						    - jiffies);
> +			if (cm->fullbatt_vchk_jiffies_at <= jiffies

time_before_eq_jiffies() ?

> ||
> +				msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
> +				fullbatt_vchk(&cm->fullbatt_vchk_work.work);
> +				fbchk_ms = 0;
> +			}
> +		}
> +		CM_MIN_VALID(wakeup_ms, fbchk_ms);
> +
>  		/* Skip if polling is not required for this CM */
>  		if (!is_polling_required(cm) && !cm->emergency_stop)
>  			continue;
> @@ -675,6 +824,27 @@ static bool cm_setup_timer(void)
>  	return false;
>  }
>  
> +static bool _cm_fbchk_in_suspend(struct charger_manager *cm)

Why do you need it to return bool?

> +{
> +	unsigned long jiffy_now = jiffies;
> +
> +	if (!cm->fullbatt_vchk_jiffies_at)
> +		return false;
> +
> +	if (g_desc && g_desc->assume_timer_stops_in_suspend)
> +		jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
> +
> +	/* Execute now if it's going to be executed not too long after */
> +	jiffy_now += CM_JIFFIES_SMALL;
> +
> +	if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) {
> +		fullbatt_vchk(&cm->fullbatt_vchk_work.work);
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
>  /**
>   * cm_suspend_again - Determine whether suspend again or not
>   *
> @@ -696,6 +866,8 @@ bool cm_suspend_again(void)
>  	ret = true;
>  	mutex_lock(&cm_list_mtx);
>  	list_for_each_entry(cm, &cm_list, entry) {
> +		_cm_fbchk_in_suspend(cm);
> +
>  		if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
>  		    cm->status_save_batt != is_batt_present(cm))
>  			ret = false;
> @@ -797,6 +969,21 @@ static int charger_manager_probe(struct platform_device *pdev)
>  	memcpy(cm->desc, desc, sizeof(struct charger_desc));
>  	cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
>  
> +	/*
> +	 * The following two do not need to be errors.
> +	 * Users may intentionally ignore those two features.
> +	 */
> +	if (desc->fullbatt_uV == 0) {
> +		dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
> +					" as it is not supplied.");
> +	}
> +	if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
> +		dev_info(&pdev->dev, "Disabling full-battery voltage drop "
> +				"checking mechanism as it is not supplied.");
> +		desc->fullbatt_vchkdrop_ms = 0;
> +		desc->fullbatt_vchkdrop_uV = 0;
> +	}
> +
>  	if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
>  		ret = -EINVAL;
>  		dev_err(&pdev->dev, "charger_regulators undefined.\n");
> @@ -905,6 +1092,8 @@ static int charger_manager_probe(struct platform_device *pdev)
>  		cm->charger_psy.num_properties++;
>  	}
>  
> +	INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
> +
>  	ret = power_supply_register(NULL, &cm->charger_psy);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Cannot register charger-manager with"
> @@ -930,6 +1119,8 @@ static int charger_manager_probe(struct platform_device *pdev)
>  	list_add(&cm->entry, &cm_list);
>  	mutex_unlock(&cm_list_mtx);
>  
> +	schedule_work(&setup_polling);
> +
>  	return 0;
>  
>  err_chg_enable:
> @@ -961,10 +1152,14 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
>  	list_del(&cm->entry);
>  	mutex_unlock(&cm_list_mtx);
>  
> +	schedule_work(&setup_polling);
> +
>  	if (desc->charger_regulators)
>  		regulator_bulk_free(desc->num_charger_regulators,
>  					desc->charger_regulators);
>  
> +	try_charger_enable(cm, false);
> +
>  	power_supply_unregister(&cm->charger_psy);
>  	kfree(cm->charger_psy.properties);
>  	kfree(cm->charger_stat);
> @@ -1007,6 +1202,7 @@ static int cm_suspend_prepare(struct device *dev)
>  		cm_suspended = true;
>  	}
>  
> +	cancel_delayed_work(&cm->fullbatt_vchk_work);

Why do you need to cancel it here?

>  	cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
>  	cm->status_save_batt = is_batt_present(cm);
>  
> @@ -1036,6 +1232,34 @@ static void cm_suspend_complete(struct device *dev)
>  		cm_rtc_set = false;
>  	}
>  
> +	/* Re-enqueue delayed work (fullbatt_vchk_work) */
> +	if (cm->fullbatt_vchk_jiffies_at) {
> +		unsigned long delay = 0;
> +		unsigned long now = jiffies;

I'd do

+		unsigned long now = jiffies + CM_JIFFIES_SMALL;

which would simplify the code below.

> +
> +		if (time_after_eq(now + CM_JIFFIES_SMALL,
> +				  cm->fullbatt_vchk_jiffies_at)) {
> +			delay = (unsigned long)((long)(now + CM_JIFFIES_SMALL)
> +				- (long)(cm->fullbatt_vchk_jiffies_at));

I don't really think that handles the possible overflow correctly (if you get
a negative result and then covert it to unsigned long, that still will produce a
big positive number).

> +			delay = jiffies_to_msecs(delay);
> +		} else {
> +			delay = 0;
> +		}
> +
> +		/*
> +		 * Account for cm_suspend_duration_ms if
> +		 * assume_timer_stops_in_suspend is active
> +		 */
> +		if (g_desc && g_desc->assume_timer_stops_in_suspend) {
> +			if (delay > cm_suspend_duration_ms)
> +				delay -= cm_suspend_duration_ms;
> +			else
> +				delay = 0;
> +		}
> +
> +		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
> +				   msecs_to_jiffies(delay));
> +	}
>  	uevent_notify(cm, NULL);
>  }
>  
> @@ -1057,12 +1281,18 @@ static struct platform_driver charger_manager_driver = {
>  
>  static int __init charger_manager_init(void)
>  {
> +	cm_wq = create_freezable_workqueue("charger_manager");
> +	INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
> +
>  	return platform_driver_register(&charger_manager_driver);
>  }
>  late_initcall(charger_manager_init);
>  
>  static void __exit charger_manager_cleanup(void)
>  {
> +	destroy_workqueue(cm_wq);
> +	cm_wq = NULL;
> +
>  	platform_driver_unregister(&charger_manager_driver);
>  }
>  module_exit(charger_manager_cleanup);
> diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
> index 4f75e53..78b9865 100644
> --- a/include/linux/power/charger-manager.h
> +++ b/include/linux/power/charger-manager.h
> @@ -18,6 +18,8 @@
>  #include <linux/power_supply.h>
>  
>  enum data_source {
> +	CM_ASSUME_ALWAYS_TRUE,
> +	CM_ASSUME_ALWAYS_FALSE,
>  	CM_FUEL_GAUGE,
>  	CM_CHARGER_STAT,
>  };
> @@ -38,11 +40,18 @@ enum polling_modes {
>   *	rtc_only_wakeup() returning false.
>   *	If the RTC given to CM is the only wakeup reason,
>   *	rtc_only_wakeup should return true.
> + * @assume_timer_stops_in_suspend:
> + *	Assume that the jiffy timer stops in suspend-to-RAM.
> + *	When enabled, CM does not rely on jiffies value in
> + *	suspend_again and assumes that jiffies value does not
> + *	change during suspend.
>   */
>  struct charger_global_desc {
>  	char *rtc_name;
>  
>  	bool (*rtc_only_wakeup)(void);
> +
> +	bool assume_timer_stops_in_suspend;
>  };
>  
>  /**
> @@ -50,6 +59,11 @@ struct charger_global_desc {
>   * @psy_name: the name of power-supply-class for charger manager
>   * @polling_mode:
>   *	Determine which polling mode will be used
> + * @fullbatt_vchkdrop_ms:
> + * @fullbatt_vchkdrop_uV:
> + *	Check voltage drop after the battery is fully charged.
> + *	If it has dropped more than fullbatt_vchkdrop_uV after
> + *	fullbatt_vchkdrop_ms, CM will restart charging.
>   * @fullbatt_uV: voltage in microvolt
>   *	If it is not being charged and VBATT >= fullbatt_uV,
>   *	it is assumed to be full.
> @@ -76,6 +90,8 @@ struct charger_desc {
>  	enum polling_modes polling_mode;
>  	unsigned int polling_interval_ms;
>  
> +	unsigned int fullbatt_vchkdrop_ms;
> +	unsigned int fullbatt_vchkdrop_uV;
>  	unsigned int fullbatt_uV;
>  
>  	enum data_source battery_present;
> @@ -101,6 +117,11 @@ struct charger_desc {
>   * @fuel_gauge: power_supply for fuel gauge
>   * @charger_stat: array of power_supply for chargers
>   * @charger_enabled: the state of charger
> + * @fullbatt_vchk_jiffies_at:
> + *	jiffies at the time full battery check will occur.
> + * @fullbatt_vchk_uV: voltage in microvolt
> + *	criteria for full battery
> + * @fullbatt_vchk_work: work queue for full battery check
>   * @emergency_stop:
>   *	When setting true, stop charging
>   * @last_temp_mC: the measured temperature in milli-Celsius
> @@ -121,6 +142,10 @@ struct charger_manager {
>  
>  	bool charger_enabled;
>  
> +	unsigned long fullbatt_vchk_jiffies_at;
> +	unsigned int fullbatt_vchk_uV;
> +	struct delayed_work fullbatt_vchk_work;
> +
>  	int emergency_stop;
>  	int last_temp_mC;
>  
> 

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use
  2012-02-23  4:45 ` [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use Donggeun Kim
@ 2012-03-01  0:13   ` Rafael J. Wysocki
  2012-03-04 23:00   ` Rafael J. Wysocki
  1 sibling, 0 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2012-03-01  0:13 UTC (permalink / raw)
  To: Donggeun Kim
  Cc: linux-kernel, cbouatmailru, len.brown, pavel, rdunlap,
	myungjoo.ham, kyungmin.park

On Thursday, February 23, 2012, Donggeun Kim wrote:
> By using cm_notify_event function,
> charger driver can report several charger events
> (e.g. battery full and external power in/out, etc) to Charger-Manager.
> Charger-Manager can properly and immediately control chargers
> by the reported event.
> 
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  Documentation/power/charger-manager.txt |   16 +++-
>  drivers/power/charger-manager.c         |  157 +++++++++++++++++++++++++++++++
>  include/linux/power/charger-manager.h   |   18 ++++
>  3 files changed, 190 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
> index 47f7fdc..f6566c7 100644
> --- a/Documentation/power/charger-manager.txt
> +++ b/Documentation/power/charger-manager.txt
> @@ -50,6 +50,10 @@ Charger Manager supports the following:
>  	restarts charging. This check is also performed while suspended by
>  	setting wakeup time accordingly and using suspend_again.
>  
> +* Support for uevent-notify
> +	With the charger-related events, the device sends
> +	notification to users with UEVENT.
> +
>  2. Global Charger-Manager Data related with suspend_again
>  ========================================================
>  In order to setup Charger Manager with suspend-again feature
> @@ -174,7 +178,17 @@ bool measure_battery_temp;
>  	the value of measure_battery_temp.
>  };
>  
> -5. Other Considerations
> +5. Notify Charger-Manager of charger events: cm_notify_event()
> +=========================================================
> +If there is an charger event is required to notify
> +Charger Manager, a charger device driver that triggers the event can call
> +cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
> +In the function, psy is the charger driver's power_supply pointer, which is
> +associated with Charger-Manager. The parameter "type"
> +is the same as irq's type (enum cm_event_types). The event message "msg" is
> +optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
> +
> +6. Other Considerations
>  =======================
>  
>  At the charger/battery-related events such as battery-pulled-out,
> diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
> index 9ecde4c..8cf0d42 100644
> --- a/drivers/power/charger-manager.c
> +++ b/drivers/power/charger-manager.c
> @@ -23,6 +23,16 @@
>  #include <linux/power/charger-manager.h>
>  #include <linux/regulator/consumer.h>
>  
> +static const char * const default_event_names[] = {
> +	[CM_EVENT_UNDESCRIBED] = "Undescribed",

"Unknown" perhaps?

> +	[CM_EVENT_BATT_FULL] = "Battery Full",
> +	[CM_EVENT_BATT_IN] = "Battery Inserted",
> +	[CM_EVENT_BATT_OUT] = "Battery Pulled Out",
> +	[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
> +	[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
> +	[CM_EVENT_OTHERS] = "Other battery events"
> +};
> +
>  /*
>   * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
>   * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
> @@ -530,6 +540,68 @@ static void cm_monitor_poller(struct work_struct *work)
>  	schedule_work(&setup_polling);
>  }
>  
> +/**
> + * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
> + * @cm: the Charger Manager representing the battery.
> + */
> +static void fullbatt_handler(struct charger_manager *cm)
> +{
> +	struct charger_desc *desc = cm->desc;
> +
> +	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
> +		goto out;
> +
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	cancel_delayed_work(&cm->fullbatt_vchk_work);

Is cancel_delayed_work() sufficient here?

> +	queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
> +			   msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
> +	cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
> +				       desc->fullbatt_vchkdrop_ms);
> +
> +	if (cm->fullbatt_vchk_jiffies_at == 0)
> +		cm->fullbatt_vchk_jiffies_at = 1;
> +
> +out:
> +	dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
> +	uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
> +}
> +
> +/**
> + * battout_handler - Event handler for CM_EVENT_BATT_OUT
> + * @cm: the Charger Manager representing the battery.
> + */
> +static void battout_handler(struct charger_manager *cm)
> +{
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	if (!is_batt_present(cm)) {
> +		dev_emerg(cm->dev, "Battery Pulled Out!\n");
> +		uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
> +	} else {
> +		uevent_notify(cm, "Battery Reinserted?");
> +	}
> +}
> +
> +/**
> + * misc_event_handler - Handler for other evnets
> + * @cm: the Charger Manager representing the battery.
> + * @type: the Charger Manager representing the battery.
> + */
> +static void misc_event_handler(struct charger_manager *cm,
> +			enum cm_event_types type)
> +{
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	if (!delayed_work_pending(&cm_monitor_work) &&
> +	    is_polling_required(cm) && cm->desc->polling_interval_ms)
> +		schedule_work(&setup_polling);
> +	uevent_notify(cm, default_event_names[type]);
> +}
> +
>  static int charger_get_property(struct power_supply *psy,
>  		enum power_supply_property psp,
>  		union power_supply_propval *val)
> @@ -1175,6 +1247,20 @@ static const struct platform_device_id charger_manager_id[] = {
>  };
>  MODULE_DEVICE_TABLE(platform, charger_manager_id);
>  
> +static int cm_suspend_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct charger_manager *cm = platform_get_drvdata(pdev);
> +
> +	if (cm->cancel_suspend) {
> +		cm->cancel_suspend = false;
> +		return -EAGAIN;
> +	}
> +
> +	return 0;
> +}
> +
>  static int cm_suspend_prepare(struct device *dev)
>  {
>  	struct platform_device *pdev = container_of(dev, struct platform_device,
> @@ -1260,11 +1346,13 @@ static void cm_suspend_complete(struct device *dev)
>  		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
>  				   msecs_to_jiffies(delay));
>  	}
> +	cm->cancel_suspend = false;
>  	uevent_notify(cm, NULL);
>  }
>  
>  static const struct dev_pm_ops charger_manager_pm = {
>  	.prepare	= cm_suspend_prepare,
> +	.suspend_noirq	= cm_suspend_noirq,
>  	.complete	= cm_suspend_complete,
>  };
>  
> @@ -1297,6 +1385,75 @@ static void __exit charger_manager_cleanup(void)
>  }
>  module_exit(charger_manager_cleanup);
>  
> +/**
> + * find_power_supply - find the associated power_supply of charger
> + * @cm: the Charger Manager representing the battery
> + * @psy: pointer to instance of charger's power_supply
> + */
> +static bool find_power_supply(struct charger_manager *cm,
> +			struct power_supply *psy)
> +{
> +	int i;
> +	bool found = false;
> +
> +	for (i = 0; cm->charger_stat[i]; i++) {
> +		if (psy == cm->charger_stat[i]) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	return found;
> +}
> +
> +/**
> + * cm_notify_event - charger driver notify Charger Manager of charger event
> + * @psy: pointer to instance of charger's power_supply
> + * @type: type of charger event
> + * @msg: optional message passed to uevent_notify fuction
> + */
> +void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
> +		     char *msg)
> +{
> +	struct charger_manager *cm;
> +	bool found_power_supply = false;
> +
> +	if (psy == NULL)
> +		return;
> +
> +	mutex_lock(&cm_list_mtx);
> +	list_for_each_entry(cm, &cm_list, entry) {
> +		found_power_supply = find_power_supply(cm, psy);
> +		if (found_power_supply)
> +			break;
> +	}
> +	mutex_unlock(&cm_list_mtx);

Why don't you move the entire loop above to find_power_supply()?
You won't need the found_power_supply variable in that case.

> +	if (!found_power_supply)
> +		return;
> +
> +	switch (type) {
> +	case CM_EVENT_BATT_FULL:
> +		fullbatt_handler(cm);
> +		break;
> +	case CM_EVENT_BATT_OUT:
> +		battout_handler(cm);
> +		break;
> +	case CM_EVENT_BATT_IN:
> +	case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
> +		misc_event_handler(cm, type);
> +		break;
> +	case CM_EVENT_UNDESCRIBED:
> +	case CM_EVENT_OTHERS:
> +		uevent_notify(cm, msg ? msg : default_event_names[type]);
> +		break;
> +	default:
> +		dev_err(cm->dev, "%s type not specified.\n", __func__);
> +		break;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(cm_notify_event);
> +
>  MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
>  MODULE_DESCRIPTION("Charger Manager");
>  MODULE_LICENSE("GPL");
> diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
> index 78b9865..086917c 100644
> --- a/include/linux/power/charger-manager.h
> +++ b/include/linux/power/charger-manager.h
> @@ -31,6 +31,16 @@ enum polling_modes {
>  	CM_POLL_CHARGING_ONLY,
>  };
>  
> +enum cm_event_types {
> +	CM_EVENT_UNDESCRIBED = 0,
> +	CM_EVENT_BATT_FULL,
> +	CM_EVENT_BATT_IN,
> +	CM_EVENT_BATT_OUT,
> +	CM_EVENT_EXT_PWR_IN_OUT,
> +	CM_EVENT_CHG_START_STOP,
> +	CM_EVENT_OTHERS,
> +};
> +
>  /**
>   * struct charger_global_desc
>   * @rtc_name: the name of RTC used to wake up the system from suspend.
> @@ -116,6 +126,7 @@ struct charger_desc {
>   * @desc: instance of charger_desc
>   * @fuel_gauge: power_supply for fuel gauge
>   * @charger_stat: array of power_supply for chargers
> + * @cancel_suspend: true if there is a pending charger event
>   * @charger_enabled: the state of charger
>   * @fullbatt_vchk_jiffies_at:
>   *	jiffies at the time full battery check will occur.
> @@ -140,6 +151,7 @@ struct charger_manager {
>  	struct power_supply *fuel_gauge;
>  	struct power_supply **charger_stat;
>  
> +	bool cancel_suspend;
>  	bool charger_enabled;
>  
>  	unsigned long fullbatt_vchk_jiffies_at;
> @@ -159,6 +171,8 @@ struct charger_manager {
>  #ifdef CONFIG_CHARGER_MANAGER
>  extern int setup_charger_manager(struct charger_global_desc *gd);
>  extern bool cm_suspend_again(void);
> +extern void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
> +			    char *msg); /* msg: optional */
>  #else
>  static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
>  { }
> @@ -167,6 +181,10 @@ static bool __maybe_unused cm_suspend_again(void)
>  {
>  	return false;
>  }
> +
> +static void __maybe_unused cm_notify_event(struct power_supply *psy,
> +					   enum cm_event_types type, char *msg)
> +{ }

We usually define such things as static inline, in which case the __maybe_unused
is not necessary.

>  #endif
>  
>  #endif /* _CHARGER_MANAGER_H */
> 

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use
  2012-02-23  4:45 ` [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use Donggeun Kim
  2012-03-01  0:13   ` Rafael J. Wysocki
@ 2012-03-04 23:00   ` Rafael J. Wysocki
  1 sibling, 0 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2012-03-04 23:00 UTC (permalink / raw)
  To: Donggeun Kim
  Cc: linux-kernel, cbouatmailru, len.brown, pavel, rdunlap,
	myungjoo.ham, kyungmin.park

On Thursday, February 23, 2012, Donggeun Kim wrote:
> By using cm_notify_event function,
> charger driver can report several charger events
> (e.g. battery full and external power in/out, etc) to Charger-Manager.
> Charger-Manager can properly and immediately control chargers
> by the reported event.

I have one more general remark regarding this patch in addition to the
comments I sent before.  Namely, I think that it would be better to use
the existing wakeup events infrastructure instead of introducing a special
suspend cancellation mechanism just for chargers.

Thanks,
Rafael


> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  Documentation/power/charger-manager.txt |   16 +++-
>  drivers/power/charger-manager.c         |  157 +++++++++++++++++++++++++++++++
>  include/linux/power/charger-manager.h   |   18 ++++
>  3 files changed, 190 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
> index 47f7fdc..f6566c7 100644
> --- a/Documentation/power/charger-manager.txt
> +++ b/Documentation/power/charger-manager.txt
> @@ -50,6 +50,10 @@ Charger Manager supports the following:
>  	restarts charging. This check is also performed while suspended by
>  	setting wakeup time accordingly and using suspend_again.
>  
> +* Support for uevent-notify
> +	With the charger-related events, the device sends
> +	notification to users with UEVENT.
> +
>  2. Global Charger-Manager Data related with suspend_again
>  ========================================================
>  In order to setup Charger Manager with suspend-again feature
> @@ -174,7 +178,17 @@ bool measure_battery_temp;
>  	the value of measure_battery_temp.
>  };
>  
> -5. Other Considerations
> +5. Notify Charger-Manager of charger events: cm_notify_event()
> +=========================================================
> +If there is an charger event is required to notify
> +Charger Manager, a charger device driver that triggers the event can call
> +cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
> +In the function, psy is the charger driver's power_supply pointer, which is
> +associated with Charger-Manager. The parameter "type"
> +is the same as irq's type (enum cm_event_types). The event message "msg" is
> +optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
> +
> +6. Other Considerations
>  =======================
>  
>  At the charger/battery-related events such as battery-pulled-out,
> diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
> index 9ecde4c..8cf0d42 100644
> --- a/drivers/power/charger-manager.c
> +++ b/drivers/power/charger-manager.c
> @@ -23,6 +23,16 @@
>  #include <linux/power/charger-manager.h>
>  #include <linux/regulator/consumer.h>
>  
> +static const char * const default_event_names[] = {
> +	[CM_EVENT_UNDESCRIBED] = "Undescribed",
> +	[CM_EVENT_BATT_FULL] = "Battery Full",
> +	[CM_EVENT_BATT_IN] = "Battery Inserted",
> +	[CM_EVENT_BATT_OUT] = "Battery Pulled Out",
> +	[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
> +	[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
> +	[CM_EVENT_OTHERS] = "Other battery events"
> +};
> +
>  /*
>   * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
>   * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
> @@ -530,6 +540,68 @@ static void cm_monitor_poller(struct work_struct *work)
>  	schedule_work(&setup_polling);
>  }
>  
> +/**
> + * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
> + * @cm: the Charger Manager representing the battery.
> + */
> +static void fullbatt_handler(struct charger_manager *cm)
> +{
> +	struct charger_desc *desc = cm->desc;
> +
> +	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
> +		goto out;
> +
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	cancel_delayed_work(&cm->fullbatt_vchk_work);
> +	queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
> +			   msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
> +	cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
> +				       desc->fullbatt_vchkdrop_ms);
> +
> +	if (cm->fullbatt_vchk_jiffies_at == 0)
> +		cm->fullbatt_vchk_jiffies_at = 1;
> +
> +out:
> +	dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
> +	uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
> +}
> +
> +/**
> + * battout_handler - Event handler for CM_EVENT_BATT_OUT
> + * @cm: the Charger Manager representing the battery.
> + */
> +static void battout_handler(struct charger_manager *cm)
> +{
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	if (!is_batt_present(cm)) {
> +		dev_emerg(cm->dev, "Battery Pulled Out!\n");
> +		uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
> +	} else {
> +		uevent_notify(cm, "Battery Reinserted?");
> +	}
> +}
> +
> +/**
> + * misc_event_handler - Handler for other evnets
> + * @cm: the Charger Manager representing the battery.
> + * @type: the Charger Manager representing the battery.
> + */
> +static void misc_event_handler(struct charger_manager *cm,
> +			enum cm_event_types type)
> +{
> +	if (cm_suspended)
> +		cm->cancel_suspend = true;
> +
> +	if (!delayed_work_pending(&cm_monitor_work) &&
> +	    is_polling_required(cm) && cm->desc->polling_interval_ms)
> +		schedule_work(&setup_polling);
> +	uevent_notify(cm, default_event_names[type]);
> +}
> +
>  static int charger_get_property(struct power_supply *psy,
>  		enum power_supply_property psp,
>  		union power_supply_propval *val)
> @@ -1175,6 +1247,20 @@ static const struct platform_device_id charger_manager_id[] = {
>  };
>  MODULE_DEVICE_TABLE(platform, charger_manager_id);
>  
> +static int cm_suspend_noirq(struct device *dev)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct charger_manager *cm = platform_get_drvdata(pdev);
> +
> +	if (cm->cancel_suspend) {
> +		cm->cancel_suspend = false;
> +		return -EAGAIN;
> +	}
> +
> +	return 0;
> +}
> +
>  static int cm_suspend_prepare(struct device *dev)
>  {
>  	struct platform_device *pdev = container_of(dev, struct platform_device,
> @@ -1260,11 +1346,13 @@ static void cm_suspend_complete(struct device *dev)
>  		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
>  				   msecs_to_jiffies(delay));
>  	}
> +	cm->cancel_suspend = false;
>  	uevent_notify(cm, NULL);
>  }
>  
>  static const struct dev_pm_ops charger_manager_pm = {
>  	.prepare	= cm_suspend_prepare,
> +	.suspend_noirq	= cm_suspend_noirq,
>  	.complete	= cm_suspend_complete,
>  };
>  
> @@ -1297,6 +1385,75 @@ static void __exit charger_manager_cleanup(void)
>  }
>  module_exit(charger_manager_cleanup);
>  
> +/**
> + * find_power_supply - find the associated power_supply of charger
> + * @cm: the Charger Manager representing the battery
> + * @psy: pointer to instance of charger's power_supply
> + */
> +static bool find_power_supply(struct charger_manager *cm,
> +			struct power_supply *psy)
> +{
> +	int i;
> +	bool found = false;
> +
> +	for (i = 0; cm->charger_stat[i]; i++) {
> +		if (psy == cm->charger_stat[i]) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	return found;
> +}
> +
> +/**
> + * cm_notify_event - charger driver notify Charger Manager of charger event
> + * @psy: pointer to instance of charger's power_supply
> + * @type: type of charger event
> + * @msg: optional message passed to uevent_notify fuction
> + */
> +void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
> +		     char *msg)
> +{
> +	struct charger_manager *cm;
> +	bool found_power_supply = false;
> +
> +	if (psy == NULL)
> +		return;
> +
> +	mutex_lock(&cm_list_mtx);
> +	list_for_each_entry(cm, &cm_list, entry) {
> +		found_power_supply = find_power_supply(cm, psy);
> +		if (found_power_supply)
> +			break;
> +	}
> +	mutex_unlock(&cm_list_mtx);
> +
> +	if (!found_power_supply)
> +		return;
> +
> +	switch (type) {
> +	case CM_EVENT_BATT_FULL:
> +		fullbatt_handler(cm);
> +		break;
> +	case CM_EVENT_BATT_OUT:
> +		battout_handler(cm);
> +		break;
> +	case CM_EVENT_BATT_IN:
> +	case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
> +		misc_event_handler(cm, type);
> +		break;
> +	case CM_EVENT_UNDESCRIBED:
> +	case CM_EVENT_OTHERS:
> +		uevent_notify(cm, msg ? msg : default_event_names[type]);
> +		break;
> +	default:
> +		dev_err(cm->dev, "%s type not specified.\n", __func__);
> +		break;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(cm_notify_event);
> +
>  MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
>  MODULE_DESCRIPTION("Charger Manager");
>  MODULE_LICENSE("GPL");
> diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
> index 78b9865..086917c 100644
> --- a/include/linux/power/charger-manager.h
> +++ b/include/linux/power/charger-manager.h
> @@ -31,6 +31,16 @@ enum polling_modes {
>  	CM_POLL_CHARGING_ONLY,
>  };
>  
> +enum cm_event_types {
> +	CM_EVENT_UNDESCRIBED = 0,
> +	CM_EVENT_BATT_FULL,
> +	CM_EVENT_BATT_IN,
> +	CM_EVENT_BATT_OUT,
> +	CM_EVENT_EXT_PWR_IN_OUT,
> +	CM_EVENT_CHG_START_STOP,
> +	CM_EVENT_OTHERS,
> +};
> +
>  /**
>   * struct charger_global_desc
>   * @rtc_name: the name of RTC used to wake up the system from suspend.
> @@ -116,6 +126,7 @@ struct charger_desc {
>   * @desc: instance of charger_desc
>   * @fuel_gauge: power_supply for fuel gauge
>   * @charger_stat: array of power_supply for chargers
> + * @cancel_suspend: true if there is a pending charger event
>   * @charger_enabled: the state of charger
>   * @fullbatt_vchk_jiffies_at:
>   *	jiffies at the time full battery check will occur.
> @@ -140,6 +151,7 @@ struct charger_manager {
>  	struct power_supply *fuel_gauge;
>  	struct power_supply **charger_stat;
>  
> +	bool cancel_suspend;
>  	bool charger_enabled;
>  
>  	unsigned long fullbatt_vchk_jiffies_at;
> @@ -159,6 +171,8 @@ struct charger_manager {
>  #ifdef CONFIG_CHARGER_MANAGER
>  extern int setup_charger_manager(struct charger_global_desc *gd);
>  extern bool cm_suspend_again(void);
> +extern void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
> +			    char *msg); /* msg: optional */
>  #else
>  static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
>  { }
> @@ -167,6 +181,10 @@ static bool __maybe_unused cm_suspend_again(void)
>  {
>  	return false;
>  }
> +
> +static void __maybe_unused cm_notify_event(struct power_supply *psy,
> +					   enum cm_event_types type, char *msg)
> +{ }
>  #endif
>  
>  #endif /* _CHARGER_MANAGER_H */
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2012-03-04 22:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-23  4:44 [PATCH 0/2] power_supply: update Charger-Manager Donggeun Kim
2012-02-23  4:44 ` [PATCH 1/2] power_supply: Charger-Manager: poll battery health in normal state Donggeun Kim
2012-02-29 23:58   ` Rafael J. Wysocki
2012-02-23  4:45 ` [PATCH 2/2] power_supply: Charger-Manager: provide function for in-kernel use Donggeun Kim
2012-03-01  0:13   ` Rafael J. Wysocki
2012-03-04 23:00   ` Rafael J. Wysocki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).