aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/power/charger-manager.txt149
-rw-r--r--drivers/power/Kconfig10
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/charger-manager.c779
-rw-r--r--include/linux/power/charger-manager.h130
5 files changed, 1069 insertions, 0 deletions
diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
new file mode 100644
index 000000000000..081489f3db25
--- /dev/null
+++ b/Documentation/power/charger-manager.txt
@@ -0,0 +1,149 @@
1Charger Manager
2 (C) 2011 MyungJoo Ham <myungjoo.ham@samsung.com>, GPL
3
4Charger Manager provides in-kernel battery charger management that
5requires temperature monitoring during suspend-to-RAM state
6and where each battery may have multiple chargers attached and the userland
7wants to look at the aggregated information of the multiple chargers.
8
9Charger Manager is a platform_driver with power-supply-class entries.
10An instance of Charger Manager (a platform-device created with Charger-Manager)
11represents an independent battery with chargers. If there are multiple
12batteries with their own chargers acting independently in a system,
13the system may need multiple instances of Charger Manager.
14
151. Introduction
16===============
17
18Charger Manager supports the following:
19
20* Support for multiple chargers (e.g., a device with USB, AC, and solar panels)
21 A system may have multiple chargers (or power sources) and some of
22 they may be activated at the same time. Each charger may have its
23 own power-supply-class and each power-supply-class can provide
24 different information about the battery status. This framework
25 aggregates charger-related information from multiple sources and
26 shows combined information as a single power-supply-class.
27
28* Support for in suspend-to-RAM polling (with suspend_again callback)
29 While the battery is being charged and the system is in suspend-to-RAM,
30 we may need to monitor the battery health by looking at the ambient or
31 battery temperature. We can accomplish this by waking up the system
32 periodically. However, such a method wakes up devices unncessary for
33 monitoring the battery health and tasks, and user processes that are
34 supposed to be kept suspended. That, in turn, incurs unnecessary power
35 consumption and slow down charging process. Or even, such peak power
36 consumption can stop chargers in the middle of charging
37 (external power input < device power consumption), which not
38 only affects the charging time, but the lifespan of the battery.
39
40 Charger Manager provides a function "cm_suspend_again" that can be
41 used as suspend_again callback of platform_suspend_ops. If the platform
42 requires tasks other than cm_suspend_again, it may implement its own
43 suspend_again callback that calls cm_suspend_again in the middle.
44 Normally, the platform will need to resume and suspend some devices
45 that are used by Charger Manager.
46
472. Global Charger-Manager Data related with suspend_again
48========================================================
49In order to setup Charger Manager with suspend-again feature
50(in-suspend monitoring), the user should provide charger_global_desc
51with setup_charger_manager(struct charger_global_desc *).
52This charger_global_desc data for in-suspend monitoring is global
53as the name suggests. Thus, the user needs to provide only once even
54if there are multiple batteries. If there are multiple batteries, the
55multiple instances of Charger Manager share the same charger_global_desc
56and it will manage in-suspend monitoring for all instances of Charger Manager.
57
58The user needs to provide all the two entries properly in order to activate
59in-suspend monitoring:
60
61struct charger_global_desc {
62
63char *rtc_name;
64 : The name of rtc (e.g., "rtc0") used to wakeup the system from
65 suspend for Charger Manager. The alarm interrupt (AIE) of the rtc
66 should be able to wake up the system from suspend. Charger Manager
67 saves and restores the alarm value and use the previously-defined
68 alarm if it is going to go off earlier than Charger Manager so that
69 Charger Manager does not interfere with previously-defined alarms.
70
71bool (*rtc_only_wakeup)(void);
72 : This callback should let CM know whether
73 the wakeup-from-suspend is caused only by the alarm of "rtc" in the
74 same struct. If there is any other wakeup source triggered the
75 wakeup, it should return false. If the "rtc" is the only wakeup
76 reason, it should return true.
77};
78
793. How to setup suspend_again
80=============================
81Charger Manager provides a function "extern bool cm_suspend_again(void)".
82When cm_suspend_again is called, it monitors every battery. The suspend_ops
83callback of the system's platform_suspend_ops can call cm_suspend_again
84function to know whether Charger Manager wants to suspend again or not.
85If there are no other devices or tasks that want to use suspend_again
86feature, the platform_suspend_ops may directly refer to cm_suspend_again
87for its suspend_again callback.
88
89The cm_suspend_again() returns true (meaning "I want to suspend again")
90if the system was woken up by Charger Manager and the polling
91(in-suspend monitoring) results in "normal".
92
934. Charger-Manager Data (struct charger_desc)
94=============================================
95For each battery charged independently from other batteries (if a series of
96batteries are charged by a single charger, they are counted as one independent
97battery), an instance of Charger Manager is attached to it.
98
99struct charger_desc {
100
101enum polling_modes polling_mode;
102 : CM_POLL_DISABLE: do not poll this battery.
103 CM_POLL_ALWAYS: always poll this battery.
104 CM_POLL_EXTERNAL_POWER_ONLY: poll this battery if and only if
105 an external power source is attached.
106 CM_POLL_CHARGING_ONLY: poll this battery if and only if the
107 battery is being charged.
108
109unsigned int polling_interval_ms;
110 : Required polling interval in ms. Charger Manager will poll
111 this battery every polling_interval_ms or more frequently.
112
113enum data_source battery_present;
114 CM_FUEL_GAUGE: get battery presence information from fuel gauge.
115 CM_CHARGER_STAT: get battery presence from chargers.
116
117char **psy_charger_stat;
118 : An array ending with NULL that has power-supply-class names of
119 chargers. Each power-supply-class should provide "PRESENT" (if
120 battery_present is "CM_CHARGER_STAT"), "ONLINE" (shows whether an
121 external power source is attached or not), and "STATUS" (shows whether
122 the battery is {"FULL" or not FULL} or {"FULL", "Charging",
123 "Discharging", "NotCharging"}).
124
125int num_charger_regulators;
126struct regulator_bulk_data *charger_regulators;
127 : Regulators representing the chargers in the form for
128 regulator framework's bulk functions.
129
130char *psy_fuel_gauge;
131 : Power-supply-class name of the fuel gauge.
132
133int (*temperature_out_of_range)(int *mC);
134 : This callback returns 0 if the temperature is safe for charging,
135 a positive number if it is too hot to charge, and a negative number
136 if it is too cold to charge. With the variable mC, the callback returns
137 the temperature in 1/1000 of centigrade.
138};
139
1405. Other Considerations
141=======================
142
143At the charger/battery-related events such as battery-pulled-out,
144charger-pulled-out, charger-inserted, DCIN-over/under-voltage, charger-stopped,
145and others critical to chargers, the system should be configured to wake up.
146At least the following should wake up the system from a suspend:
147a) charger-on/off b) external-power-in/out c) battery-in/out (while charging)
148
149It is usually accomplished by configuring the PMIC as a wakeup source.
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 57de051a74b3..363f4d1ae067 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -235,6 +235,16 @@ config CHARGER_GPIO
235 This driver can be build as a module. If so, the module will be 235 This driver can be build as a module. If so, the module will be
236 called gpio-charger. 236 called gpio-charger.
237 237
238config CHARGER_MANAGER
239 bool "Battery charger manager for multiple chargers"
240 depends on REGULATOR && RTC_CLASS
241 help
242 Say Y to enable charger-manager support, which allows multiple
243 chargers attached to a battery and multiple batteries attached to a
244 system. The charger-manager also can monitor charging status in
245 runtime and in suspend-to-RAM by waking up the system periodically
246 with help of suspend_again support.
247
238config CHARGER_MAX8997 248config CHARGER_MAX8997
239 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" 249 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
240 depends on MFD_MAX8997 && REGULATOR_MAX8997 250 depends on MFD_MAX8997 && REGULATOR_MAX8997
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b4af13dd8b66..d3b24e99acbe 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -36,5 +36,6 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
36obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o 36obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
37obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o 37obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
38obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o 38obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
39obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
39obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o 40obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
40obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o 41obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
new file mode 100644
index 000000000000..727a259ea46c
--- /dev/null
+++ b/drivers/power/charger-manager.c
@@ -0,0 +1,779 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co., Ltd.
3 * MyungJoo Ham <myungjoo.ham@samsung.com>
4 *
5 * This driver enables to monitor battery health and control charger
6 * during suspend-to-mem.
7 * Charger manager depends on other devices. register this later than
8 * the depending devices.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13**/
14
15#include <linux/io.h>
16#include <linux/module.h>
17#include <linux/irq.h>
18#include <linux/interrupt.h>
19#include <linux/rtc.h>
20#include <linux/slab.h>
21#include <linux/workqueue.h>
22#include <linux/platform_device.h>
23#include <linux/power/charger-manager.h>
24#include <linux/regulator/consumer.h>
25
26/*
27 * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
28 * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
29 * without any delays.
30 */
31#define CM_JIFFIES_SMALL (2)
32
33/* If y is valid (> 0) and smaller than x, do x = y */
34#define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x))
35
36/*
37 * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
38 * rtc alarm. It should be 2 or larger
39 */
40#define CM_RTC_SMALL (2)
41
42#define UEVENT_BUF_SIZE 32
43
44static LIST_HEAD(cm_list);
45static DEFINE_MUTEX(cm_list_mtx);
46
47/* About in-suspend (suspend-again) monitoring */
48static struct rtc_device *rtc_dev;
49/*
50 * Backup RTC alarm
51 * Save the wakeup alarm before entering suspend-to-RAM
52 */
53static struct rtc_wkalrm rtc_wkalarm_save;
54/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
55static unsigned long rtc_wkalarm_save_time;
56static bool cm_suspended;
57static bool cm_rtc_set;
58static unsigned long cm_suspend_duration_ms;
59
60/* Global charger-manager description */
61static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
62
63/**
64 * is_batt_present - See if the battery presents in place.
65 * @cm: the Charger Manager representing the battery.
66 */
67static bool is_batt_present(struct charger_manager *cm)
68{
69 union power_supply_propval val;
70 bool present = false;
71 int i, ret;
72
73 switch (cm->desc->battery_present) {
74 case CM_FUEL_GAUGE:
75 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
76 POWER_SUPPLY_PROP_PRESENT, &val);
77 if (ret == 0 && val.intval)
78 present = true;
79 break;
80 case CM_CHARGER_STAT:
81 for (i = 0; cm->charger_stat[i]; i++) {
82 ret = cm->charger_stat[i]->get_property(
83 cm->charger_stat[i],
84 POWER_SUPPLY_PROP_PRESENT, &val);
85 if (ret == 0 && val.intval) {
86 present = true;
87 break;
88 }
89 }
90 break;
91 }
92
93 return present;
94}
95
96/**
97 * is_ext_pwr_online - See if an external power source is attached to charge
98 * @cm: the Charger Manager representing the battery.
99 *
100 * Returns true if at least one of the chargers of the battery has an external
101 * power source attached to charge the battery regardless of whether it is
102 * actually charging or not.
103 */
104static bool is_ext_pwr_online(struct charger_manager *cm)
105{
106 union power_supply_propval val;
107 bool online = false;
108 int i, ret;
109
110 /* If at least one of them has one, it's yes. */
111 for (i = 0; cm->charger_stat[i]; i++) {
112 ret = cm->charger_stat[i]->get_property(
113 cm->charger_stat[i],
114 POWER_SUPPLY_PROP_ONLINE, &val);
115 if (ret == 0 && val.intval) {
116 online = true;
117 break;
118 }
119 }
120
121 return online;
122}
123
124/**
125 * is_charging - Returns true if the battery is being charged.
126 * @cm: the Charger Manager representing the battery.
127 */
128static bool is_charging(struct charger_manager *cm)
129{
130 int i, ret;
131 bool charging = false;
132 union power_supply_propval val;
133
134 /* If there is no battery, it cannot be charged */
135 if (!is_batt_present(cm))
136 return false;
137
138 /* If at least one of the charger is charging, return yes */
139 for (i = 0; cm->charger_stat[i]; i++) {
140 /* 1. The charger sholuld not be DISABLED */
141 if (cm->emergency_stop)
142 continue;
143 if (!cm->charger_enabled)
144 continue;
145
146 /* 2. The charger should be online (ext-power) */
147 ret = cm->charger_stat[i]->get_property(
148 cm->charger_stat[i],
149 POWER_SUPPLY_PROP_ONLINE, &val);
150 if (ret) {
151 dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n",
152 cm->desc->psy_charger_stat[i]);
153 continue;
154 }
155 if (val.intval == 0)
156 continue;
157
158 /*
159 * 3. The charger should not be FULL, DISCHARGING,
160 * or NOT_CHARGING.
161 */
162 ret = cm->charger_stat[i]->get_property(
163 cm->charger_stat[i],
164 POWER_SUPPLY_PROP_STATUS, &val);
165 if (ret) {
166 dev_warn(cm->dev, "Cannot read STATUS value from %s.\n",
167 cm->desc->psy_charger_stat[i]);
168 continue;
169 }
170 if (val.intval == POWER_SUPPLY_STATUS_FULL ||
171 val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
172 val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
173 continue;
174
175 /* Then, this is charging. */
176 charging = true;
177 break;
178 }
179
180 return charging;
181}
182
183/**
184 * is_polling_required - Return true if need to continue polling for this CM.
185 * @cm: the Charger Manager representing the battery.
186 */
187static bool is_polling_required(struct charger_manager *cm)
188{
189 switch (cm->desc->polling_mode) {
190 case CM_POLL_DISABLE:
191 return false;
192 case CM_POLL_ALWAYS:
193 return true;
194 case CM_POLL_EXTERNAL_POWER_ONLY:
195 return is_ext_pwr_online(cm);
196 case CM_POLL_CHARGING_ONLY:
197 return is_charging(cm);
198 default:
199 dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
200 cm->desc->polling_mode);
201 }
202
203 return false;
204}
205
206/**
207 * try_charger_enable - Enable/Disable chargers altogether
208 * @cm: the Charger Manager representing the battery.
209 * @enable: true: enable / false: disable
210 *
211 * Note that Charger Manager keeps the charger enabled regardless whether
212 * the charger is charging or not (because battery is full or no external
213 * power source exists) except when CM needs to disable chargers forcibly
214 * bacause of emergency causes; when the battery is overheated or too cold.
215 */
216static int try_charger_enable(struct charger_manager *cm, bool enable)
217{
218 int err = 0, i;
219 struct charger_desc *desc = cm->desc;
220
221 /* Ignore if it's redundent command */
222 if (enable && cm->charger_enabled)
223 return 0;
224 if (!enable && !cm->charger_enabled)
225 return 0;
226
227 if (enable) {
228 if (cm->emergency_stop)
229 return -EAGAIN;
230 err = regulator_bulk_enable(desc->num_charger_regulators,
231 desc->charger_regulators);
232 } else {
233 /*
234 * Abnormal battery state - Stop charging forcibly,
235 * even if charger was enabled at the other places
236 */
237 err = regulator_bulk_disable(desc->num_charger_regulators,
238 desc->charger_regulators);
239
240 for (i = 0; i < desc->num_charger_regulators; i++) {
241 if (regulator_is_enabled(
242 desc->charger_regulators[i].consumer)) {
243 regulator_force_disable(
244 desc->charger_regulators[i].consumer);
245 dev_warn(cm->dev,
246 "Disable regulator(%s) forcibly.\n",
247 desc->charger_regulators[i].supply);
248 }
249 }
250 }
251
252 if (!err)
253 cm->charger_enabled = enable;
254
255 return err;
256}
257
258/**
259 * uevent_notify - Let users know something has changed.
260 * @cm: the Charger Manager representing the battery.
261 * @event: the event string.
262 *
263 * If @event is null, it implies that uevent_notify is called
264 * by resume function. When called in the resume function, cm_suspended
265 * should be already reset to false in order to let uevent_notify
266 * notify the recent event during the suspend to users. While
267 * suspended, uevent_notify does not notify users, but tracks
268 * events so that uevent_notify can notify users later after resumed.
269 */
270static void uevent_notify(struct charger_manager *cm, const char *event)
271{
272 static char env_str[UEVENT_BUF_SIZE + 1] = "";
273 static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
274
275 if (cm_suspended) {
276 /* Nothing in suspended-event buffer */
277 if (env_str_save[0] == 0) {
278 if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
279 return; /* status not changed */
280 strncpy(env_str_save, event, UEVENT_BUF_SIZE);
281 return;
282 }
283
284 if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
285 return; /* Duplicated. */
286 else
287 strncpy(env_str_save, event, UEVENT_BUF_SIZE);
288
289 return;
290 }
291
292 if (event == NULL) {
293 /* No messages pending */
294 if (!env_str_save[0])
295 return;
296
297 strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
298 kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
299 env_str_save[0] = 0;
300
301 return;
302 }
303
304 /* status not changed */
305 if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
306 return;
307
308 /* save the status and notify the update */
309 strncpy(env_str, event, UEVENT_BUF_SIZE);
310 kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
311
312 dev_info(cm->dev, event);
313}
314
315/**
316 * _cm_monitor - Monitor the temperature and return true for exceptions.
317 * @cm: the Charger Manager representing the battery.
318 *
319 * Returns true if there is an event to notify for the battery.
320 * (True if the status of "emergency_stop" changes)
321 */
322static bool _cm_monitor(struct charger_manager *cm)
323{
324 struct charger_desc *desc = cm->desc;
325 int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
326
327 dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n",
328 cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
329
330 /* It has been stopped or charging already */
331 if (!!temp == !!cm->emergency_stop)
332 return false;
333
334 if (temp) {
335 cm->emergency_stop = temp;
336 if (!try_charger_enable(cm, false)) {
337 if (temp > 0)
338 uevent_notify(cm, "OVERHEAT");
339 else
340 uevent_notify(cm, "COLD");
341 }
342 } else {
343 cm->emergency_stop = 0;
344 if (!try_charger_enable(cm, true))
345 uevent_notify(cm, "CHARGING");
346 }
347
348 return true;
349}
350
351/**
352 * cm_monitor - Monitor every battery.
353 *
354 * Returns true if there is an event to notify from any of the batteries.
355 * (True if the status of "emergency_stop" changes)
356 */
357static bool cm_monitor(void)
358{
359 bool stop = false;
360 struct charger_manager *cm;
361
362 mutex_lock(&cm_list_mtx);
363
364 list_for_each_entry(cm, &cm_list, entry)
365 stop = stop || _cm_monitor(cm);
366
367 mutex_unlock(&cm_list_mtx);
368
369 return stop;
370}
371
372/**
373 * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
374 * for suspend_again.
375 *
376 * Returns true if the alarm is set for Charger Manager to use.
377 * Returns false if
378 * cm_setup_timer fails to set an alarm,
379 * cm_setup_timer does not need to set an alarm for Charger Manager,
380 * or an alarm previously configured is to be used.
381 */
382static bool cm_setup_timer(void)
383{
384 struct charger_manager *cm;
385 unsigned int wakeup_ms = UINT_MAX;
386 bool ret = false;
387
388 mutex_lock(&cm_list_mtx);
389
390 list_for_each_entry(cm, &cm_list, entry) {
391 /* Skip if polling is not required for this CM */
392 if (!is_polling_required(cm) && !cm->emergency_stop)
393 continue;
394 if (cm->desc->polling_interval_ms == 0)
395 continue;
396 CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
397 }
398
399 mutex_unlock(&cm_list_mtx);
400
401 if (wakeup_ms < UINT_MAX && wakeup_ms > 0) {
402 pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms);
403 if (rtc_dev) {
404 struct rtc_wkalrm tmp;
405 unsigned long time, now;
406 unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
407
408 /*
409 * Set alarm with the polling interval (wakeup_ms)
410 * except when rtc_wkalarm_save comes first.
411 * However, the alarm time should be NOW +
412 * CM_RTC_SMALL or later.
413 */
414 tmp.enabled = 1;
415 rtc_read_time(rtc_dev, &tmp.time);
416 rtc_tm_to_time(&tmp.time, &now);
417 if (add < CM_RTC_SMALL)
418 add = CM_RTC_SMALL;
419 time = now + add;
420
421 ret = true;
422
423 if (rtc_wkalarm_save.enabled &&
424 rtc_wkalarm_save_time &&
425 rtc_wkalarm_save_time < time) {
426 if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
427 time = now + CM_RTC_SMALL;
428 else
429 time = rtc_wkalarm_save_time;
430
431 /* The timer is not appointed by CM */
432 ret = false;
433 }
434
435 pr_info("Waking up after %lu secs.\n",
436 time - now);
437
438 rtc_time_to_tm(time, &tmp.time);
439 rtc_set_alarm(rtc_dev, &tmp);
440 cm_suspend_duration_ms += wakeup_ms;
441 return ret;
442 }
443 }
444
445 if (rtc_dev)
446 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
447 return false;
448}
449
450/**
451 * cm_suspend_again - Determine whether suspend again or not
452 *
453 * Returns true if the system should be suspended again
454 * Returns false if the system should be woken up
455 */
456bool cm_suspend_again(void)
457{
458 struct charger_manager *cm;
459 bool ret = false;
460
461 if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
462 !cm_rtc_set)
463 return false;
464
465 if (cm_monitor())
466 goto out;
467
468 ret = true;
469 mutex_lock(&cm_list_mtx);
470 list_for_each_entry(cm, &cm_list, entry) {
471 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
472 cm->status_save_batt != is_batt_present(cm))
473 ret = false;
474 }
475 mutex_unlock(&cm_list_mtx);
476
477 cm_rtc_set = cm_setup_timer();
478out:
479 /* It's about the time when the non-CM appointed timer goes off */
480 if (rtc_wkalarm_save.enabled) {
481 unsigned long now;
482 struct rtc_time tmp;
483
484 rtc_read_time(rtc_dev, &tmp);
485 rtc_tm_to_time(&tmp, &now);
486
487 if (rtc_wkalarm_save_time &&
488 now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
489 return false;
490 }
491 return ret;
492}
493EXPORT_SYMBOL_GPL(cm_suspend_again);
494
495/**
496 * setup_charger_manager - initialize charger_global_desc data
497 * @gd: pointer to instance of charger_global_desc
498 */
499int setup_charger_manager(struct charger_global_desc *gd)
500{
501 if (!gd)
502 return -EINVAL;
503
504 if (rtc_dev)
505 rtc_class_close(rtc_dev);
506 rtc_dev = NULL;
507 g_desc = NULL;
508
509 if (!gd->rtc_only_wakeup) {
510 pr_err("The callback rtc_only_wakeup is not given.\n");
511 return -EINVAL;
512 }
513
514 if (gd->rtc_name) {
515 rtc_dev = rtc_class_open(gd->rtc_name);
516 if (IS_ERR_OR_NULL(rtc_dev)) {
517 rtc_dev = NULL;
518 /* Retry at probe. RTC may be not registered yet */
519 }
520 } else {
521 pr_warn("No wakeup timer is given for charger manager."
522 "In-suspend monitoring won't work.\n");
523 }
524
525 g_desc = gd;
526 return 0;
527}
528EXPORT_SYMBOL_GPL(setup_charger_manager);
529
530static int charger_manager_probe(struct platform_device *pdev)
531{
532 struct charger_desc *desc = dev_get_platdata(&pdev->dev);
533 struct charger_manager *cm;
534 int ret = 0, i = 0;
535
536 if (g_desc && !rtc_dev && g_desc->rtc_name) {
537 rtc_dev = rtc_class_open(g_desc->rtc_name);
538 if (IS_ERR_OR_NULL(rtc_dev)) {
539 rtc_dev = NULL;
540 dev_err(&pdev->dev, "Cannot get RTC %s.\n",
541 g_desc->rtc_name);
542 ret = -ENODEV;
543 goto err_alloc;
544 }
545 }
546
547 if (!desc) {
548 dev_err(&pdev->dev, "No platform data (desc) found.\n");
549 ret = -ENODEV;
550 goto err_alloc;
551 }
552
553 cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
554 if (!cm) {
555 dev_err(&pdev->dev, "Cannot allocate memory.\n");
556 ret = -ENOMEM;
557 goto err_alloc;
558 }
559
560 /* Basic Values. Unspecified are Null or 0 */
561 cm->dev = &pdev->dev;
562 cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
563 if (!cm->desc) {
564 dev_err(&pdev->dev, "Cannot allocate memory.\n");
565 ret = -ENOMEM;
566 goto err_alloc_desc;
567 }
568 memcpy(cm->desc, desc, sizeof(struct charger_desc));
569 cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
570
571 if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
572 ret = -EINVAL;
573 dev_err(&pdev->dev, "charger_regulators undefined.\n");
574 goto err_no_charger;
575 }
576
577 if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
578 dev_err(&pdev->dev, "No power supply defined.\n");
579 ret = -EINVAL;
580 goto err_no_charger_stat;
581 }
582
583 /* Counting index only */
584 while (desc->psy_charger_stat[i])
585 i++;
586
587 cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
588 GFP_KERNEL);
589 if (!cm->charger_stat) {
590 ret = -ENOMEM;
591 goto err_no_charger_stat;
592 }
593
594 for (i = 0; desc->psy_charger_stat[i]; i++) {
595 cm->charger_stat[i] = power_supply_get_by_name(
596 desc->psy_charger_stat[i]);
597 if (!cm->charger_stat[i]) {
598 dev_err(&pdev->dev, "Cannot find power supply "
599 "\"%s\"\n",
600 desc->psy_charger_stat[i]);
601 ret = -ENODEV;
602 goto err_chg_stat;
603 }
604 }
605
606 cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
607 if (!cm->fuel_gauge) {
608 dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
609 desc->psy_fuel_gauge);
610 ret = -ENODEV;
611 goto err_chg_stat;
612 }
613
614 if (desc->polling_interval_ms == 0 ||
615 msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
616 dev_err(&pdev->dev, "polling_interval_ms is too small\n");
617 ret = -EINVAL;
618 goto err_chg_stat;
619 }
620
621 if (!desc->temperature_out_of_range) {
622 dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
623 ret = -EINVAL;
624 goto err_chg_stat;
625 }
626
627 platform_set_drvdata(pdev, cm);
628
629 ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
630 desc->charger_regulators);
631 if (ret) {
632 dev_err(&pdev->dev, "Cannot get charger regulators.\n");
633 goto err_chg_stat;
634 }
635
636 ret = try_charger_enable(cm, true);
637 if (ret) {
638 dev_err(&pdev->dev, "Cannot enable charger regulators\n");
639 goto err_chg_enable;
640 }
641
642 /* Add to the list */
643 mutex_lock(&cm_list_mtx);
644 list_add(&cm->entry, &cm_list);
645 mutex_unlock(&cm_list_mtx);
646
647 return 0;
648
649err_chg_enable:
650 if (desc->charger_regulators)
651 regulator_bulk_free(desc->num_charger_regulators,
652 desc->charger_regulators);
653err_chg_stat:
654 kfree(cm->charger_stat);
655err_no_charger_stat:
656err_no_charger:
657 kfree(cm->desc);
658err_alloc_desc:
659 kfree(cm);
660err_alloc:
661 return ret;
662}
663
664static int __devexit charger_manager_remove(struct platform_device *pdev)
665{
666 struct charger_manager *cm = platform_get_drvdata(pdev);
667 struct charger_desc *desc = cm->desc;
668
669 /* Remove from the list */
670 mutex_lock(&cm_list_mtx);
671 list_del(&cm->entry);
672 mutex_unlock(&cm_list_mtx);
673
674 if (desc->charger_regulators)
675 regulator_bulk_free(desc->num_charger_regulators,
676 desc->charger_regulators);
677 kfree(cm->charger_stat);
678 kfree(cm->desc);
679 kfree(cm);
680
681 return 0;
682}
683
684const struct platform_device_id charger_manager_id[] = {
685 { "charger-manager", 0 },
686 { },
687};
688
689static int cm_suspend_prepare(struct device *dev)
690{
691 struct platform_device *pdev = container_of(dev, struct platform_device,
692 dev);
693 struct charger_manager *cm = platform_get_drvdata(pdev);
694
695 if (!cm_suspended) {
696 if (rtc_dev) {
697 struct rtc_time tmp;
698 unsigned long now;
699
700 rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
701 rtc_read_time(rtc_dev, &tmp);
702
703 if (rtc_wkalarm_save.enabled) {
704 rtc_tm_to_time(&rtc_wkalarm_save.time,
705 &rtc_wkalarm_save_time);
706 rtc_tm_to_time(&tmp, &now);
707 if (now > rtc_wkalarm_save_time)
708 rtc_wkalarm_save_time = 0;
709 } else {
710 rtc_wkalarm_save_time = 0;
711 }
712 }
713 cm_suspended = true;
714 }
715
716 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
717 cm->status_save_batt = is_batt_present(cm);
718
719 if (!cm_rtc_set) {
720 cm_suspend_duration_ms = 0;
721 cm_rtc_set = cm_setup_timer();
722 }
723
724 return 0;
725}
726
727static void cm_suspend_complete(struct device *dev)
728{
729 struct platform_device *pdev = container_of(dev, struct platform_device,
730 dev);
731 struct charger_manager *cm = platform_get_drvdata(pdev);
732
733 if (cm_suspended) {
734 if (rtc_dev) {
735 struct rtc_wkalrm tmp;
736
737 rtc_read_alarm(rtc_dev, &tmp);
738 rtc_wkalarm_save.pending = tmp.pending;
739 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
740 }
741 cm_suspended = false;
742 cm_rtc_set = false;
743 }
744
745 uevent_notify(cm, NULL);
746}
747
748static const struct dev_pm_ops charger_manager_pm = {
749 .prepare = cm_suspend_prepare,
750 .complete = cm_suspend_complete,
751};
752
753static struct platform_driver charger_manager_driver = {
754 .driver = {
755 .name = "charger-manager",
756 .owner = THIS_MODULE,
757 .pm = &charger_manager_pm,
758 },
759 .probe = charger_manager_probe,
760 .remove = __devexit_p(charger_manager_remove),
761 .id_table = charger_manager_id,
762};
763
764static int __init charger_manager_init(void)
765{
766 return platform_driver_register(&charger_manager_driver);
767}
768late_initcall(charger_manager_init);
769
770static void __exit charger_manager_cleanup(void)
771{
772 platform_driver_unregister(&charger_manager_driver);
773}
774module_exit(charger_manager_cleanup);
775
776MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
777MODULE_DESCRIPTION("Charger Manager");
778MODULE_LICENSE("GPL");
779MODULE_ALIAS("charger-manager");
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
new file mode 100644
index 000000000000..102c5b3f3325
--- /dev/null
+++ b/include/linux/power/charger-manager.h
@@ -0,0 +1,130 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co., Ltd.
3 * MyungJoo.Ham <myungjoo.ham@samsung.com>
4 *
5 * Charger Manager.
6 * This framework enables to control and multiple chargers and to
7 * monitor charging even in the context of suspend-to-RAM with
8 * an interface combining the chargers.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13**/
14
15#ifndef _CHARGER_MANAGER_H
16#define _CHARGER_MANAGER_H
17
18#include <linux/power_supply.h>
19
20enum data_source {
21 CM_FUEL_GAUGE,
22 CM_CHARGER_STAT,
23};
24
25enum polling_modes {
26 CM_POLL_DISABLE = 0,
27 CM_POLL_ALWAYS,
28 CM_POLL_EXTERNAL_POWER_ONLY,
29 CM_POLL_CHARGING_ONLY,
30};
31
32/**
33 * struct charger_global_desc
34 * @rtc_name: the name of RTC used to wake up the system from suspend.
35 * @rtc_only_wakeup:
36 * If the system is woken up by waekup-sources other than the RTC or
37 * callbacks, Charger Manager should recognize with
38 * rtc_only_wakeup() returning false.
39 * If the RTC given to CM is the only wakeup reason,
40 * rtc_only_wakeup should return true.
41 */
42struct charger_global_desc {
43 char *rtc_name;
44
45 bool (*rtc_only_wakeup)(void);
46};
47
48/**
49 * struct charger_desc
50 * @polling_mode:
51 * Determine which polling mode will be used
52 * @polling_interval_ms: interval in millisecond at which
53 * charger manager will monitor battery health
54 * @battery_present:
55 * Specify where information for existance of battery can be obtained
56 * @psy_charger_stat: the names of power-supply for chargers
57 * @num_charger_regulator: the number of entries in charger_regulators
58 * @charger_regulators: array of regulator_bulk_data for chargers
59 * @psy_fuel_gauge: the name of power-supply for fuel gauge
60 * @temperature_out_of_range:
61 * Determine whether the status is overheat or cold or normal.
62 * return_value > 0: overheat
63 * return_value == 0: normal
64 * return_value < 0: cold
65 */
66struct charger_desc {
67 enum polling_modes polling_mode;
68 unsigned int polling_interval_ms;
69
70 enum data_source battery_present;
71
72 char **psy_charger_stat;
73
74 int num_charger_regulators;
75 struct regulator_bulk_data *charger_regulators;
76
77 char *psy_fuel_gauge;
78
79 int (*temperature_out_of_range)(int *mC);
80};
81
82#define PSY_NAME_MAX 30
83
84/**
85 * struct charger_manager
86 * @entry: entry for list
87 * @dev: device pointer
88 * @desc: instance of charger_desc
89 * @fuel_gauge: power_supply for fuel gauge
90 * @charger_stat: array of power_supply for chargers
91 * @charger_enabled: the state of charger
92 * @emergency_stop:
93 * When setting true, stop charging
94 * @last_temp_mC: the measured temperature in milli-Celsius
95 * @status_save_ext_pwr_inserted:
96 * saved status of external power before entering suspend-to-RAM
97 * @status_save_batt:
98 * saved status of battery before entering suspend-to-RAM
99 */
100struct charger_manager {
101 struct list_head entry;
102 struct device *dev;
103 struct charger_desc *desc;
104
105 struct power_supply *fuel_gauge;
106 struct power_supply **charger_stat;
107
108 bool charger_enabled;
109
110 int emergency_stop;
111 int last_temp_mC;
112
113 bool status_save_ext_pwr_inserted;
114 bool status_save_batt;
115};
116
117#ifdef CONFIG_CHARGER_MANAGER
118extern int setup_charger_manager(struct charger_global_desc *gd);
119extern bool cm_suspend_again(void);
120#else
121static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
122{ }
123
124static bool __maybe_unused cm_suspend_again(void)
125{
126 return false;
127}
128#endif
129
130#endif /* _CHARGER_MANAGER_H */