blob: 959062d16bacaecc03c010fe33a98f2b06aa0b34 [file] [log] [blame]
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001/*
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
Chanwoo Choid829dc72012-05-05 06:24:10 -070060/* About normal (not suspended) monitoring */
61static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
62static unsigned long next_polling; /* Next appointed polling time */
63static struct workqueue_struct *cm_wq; /* init at driver add */
64static struct delayed_work cm_monitor_work; /* init at driver add */
65
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +090066/* Global charger-manager description */
67static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
68
69/**
70 * is_batt_present - See if the battery presents in place.
71 * @cm: the Charger Manager representing the battery.
72 */
73static bool is_batt_present(struct charger_manager *cm)
74{
75 union power_supply_propval val;
76 bool present = false;
77 int i, ret;
78
79 switch (cm->desc->battery_present) {
Chanwoo Choid829dc72012-05-05 06:24:10 -070080 case CM_BATTERY_PRESENT:
81 present = true;
82 break;
83 case CM_NO_BATTERY:
84 break;
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +090085 case CM_FUEL_GAUGE:
86 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
87 POWER_SUPPLY_PROP_PRESENT, &val);
88 if (ret == 0 && val.intval)
89 present = true;
90 break;
91 case CM_CHARGER_STAT:
92 for (i = 0; cm->charger_stat[i]; i++) {
93 ret = cm->charger_stat[i]->get_property(
94 cm->charger_stat[i],
95 POWER_SUPPLY_PROP_PRESENT, &val);
96 if (ret == 0 && val.intval) {
97 present = true;
98 break;
99 }
100 }
101 break;
102 }
103
104 return present;
105}
106
107/**
108 * is_ext_pwr_online - See if an external power source is attached to charge
109 * @cm: the Charger Manager representing the battery.
110 *
111 * Returns true if at least one of the chargers of the battery has an external
112 * power source attached to charge the battery regardless of whether it is
113 * actually charging or not.
114 */
115static bool is_ext_pwr_online(struct charger_manager *cm)
116{
117 union power_supply_propval val;
118 bool online = false;
119 int i, ret;
120
121 /* If at least one of them has one, it's yes. */
122 for (i = 0; cm->charger_stat[i]; i++) {
123 ret = cm->charger_stat[i]->get_property(
124 cm->charger_stat[i],
125 POWER_SUPPLY_PROP_ONLINE, &val);
126 if (ret == 0 && val.intval) {
127 online = true;
128 break;
129 }
130 }
131
132 return online;
133}
134
135/**
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900136 * get_batt_uV - Get the voltage level of the battery
137 * @cm: the Charger Manager representing the battery.
138 * @uV: the voltage level returned.
139 *
140 * Returns 0 if there is no error.
141 * Returns a negative value on error.
142 */
143static int get_batt_uV(struct charger_manager *cm, int *uV)
144{
145 union power_supply_propval val;
146 int ret;
147
Axel Linbb2a95c2012-01-12 12:56:35 +0800148 if (!cm->fuel_gauge)
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900149 return -ENODEV;
150
Axel Linbb2a95c2012-01-12 12:56:35 +0800151 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
152 POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900153 if (ret)
154 return ret;
155
156 *uV = val.intval;
157 return 0;
158}
159
160/**
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900161 * is_charging - Returns true if the battery is being charged.
162 * @cm: the Charger Manager representing the battery.
163 */
164static bool is_charging(struct charger_manager *cm)
165{
166 int i, ret;
167 bool charging = false;
168 union power_supply_propval val;
169
170 /* If there is no battery, it cannot be charged */
171 if (!is_batt_present(cm))
172 return false;
173
174 /* If at least one of the charger is charging, return yes */
175 for (i = 0; cm->charger_stat[i]; i++) {
176 /* 1. The charger sholuld not be DISABLED */
177 if (cm->emergency_stop)
178 continue;
179 if (!cm->charger_enabled)
180 continue;
181
182 /* 2. The charger should be online (ext-power) */
183 ret = cm->charger_stat[i]->get_property(
184 cm->charger_stat[i],
185 POWER_SUPPLY_PROP_ONLINE, &val);
186 if (ret) {
187 dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n",
188 cm->desc->psy_charger_stat[i]);
189 continue;
190 }
191 if (val.intval == 0)
192 continue;
193
194 /*
195 * 3. The charger should not be FULL, DISCHARGING,
196 * or NOT_CHARGING.
197 */
198 ret = cm->charger_stat[i]->get_property(
199 cm->charger_stat[i],
200 POWER_SUPPLY_PROP_STATUS, &val);
201 if (ret) {
202 dev_warn(cm->dev, "Cannot read STATUS value from %s.\n",
203 cm->desc->psy_charger_stat[i]);
204 continue;
205 }
206 if (val.intval == POWER_SUPPLY_STATUS_FULL ||
207 val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
208 val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
209 continue;
210
211 /* Then, this is charging. */
212 charging = true;
213 break;
214 }
215
216 return charging;
217}
218
219/**
220 * is_polling_required - Return true if need to continue polling for this CM.
221 * @cm: the Charger Manager representing the battery.
222 */
223static bool is_polling_required(struct charger_manager *cm)
224{
225 switch (cm->desc->polling_mode) {
226 case CM_POLL_DISABLE:
227 return false;
228 case CM_POLL_ALWAYS:
229 return true;
230 case CM_POLL_EXTERNAL_POWER_ONLY:
231 return is_ext_pwr_online(cm);
232 case CM_POLL_CHARGING_ONLY:
233 return is_charging(cm);
234 default:
235 dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
236 cm->desc->polling_mode);
237 }
238
239 return false;
240}
241
242/**
243 * try_charger_enable - Enable/Disable chargers altogether
244 * @cm: the Charger Manager representing the battery.
245 * @enable: true: enable / false: disable
246 *
247 * Note that Charger Manager keeps the charger enabled regardless whether
248 * the charger is charging or not (because battery is full or no external
249 * power source exists) except when CM needs to disable chargers forcibly
250 * bacause of emergency causes; when the battery is overheated or too cold.
251 */
252static int try_charger_enable(struct charger_manager *cm, bool enable)
253{
254 int err = 0, i;
255 struct charger_desc *desc = cm->desc;
256
257 /* Ignore if it's redundent command */
Axel Linbb2a95c2012-01-12 12:56:35 +0800258 if (enable == cm->charger_enabled)
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900259 return 0;
260
261 if (enable) {
262 if (cm->emergency_stop)
263 return -EAGAIN;
264 err = regulator_bulk_enable(desc->num_charger_regulators,
265 desc->charger_regulators);
266 } else {
267 /*
268 * Abnormal battery state - Stop charging forcibly,
269 * even if charger was enabled at the other places
270 */
271 err = regulator_bulk_disable(desc->num_charger_regulators,
272 desc->charger_regulators);
273
274 for (i = 0; i < desc->num_charger_regulators; i++) {
275 if (regulator_is_enabled(
276 desc->charger_regulators[i].consumer)) {
277 regulator_force_disable(
278 desc->charger_regulators[i].consumer);
279 dev_warn(cm->dev,
280 "Disable regulator(%s) forcibly.\n",
281 desc->charger_regulators[i].supply);
282 }
283 }
284 }
285
286 if (!err)
287 cm->charger_enabled = enable;
288
289 return err;
290}
291
292/**
Chanwoo Choid829dc72012-05-05 06:24:10 -0700293 * try_charger_restart - Restart charging.
294 * @cm: the Charger Manager representing the battery.
295 *
296 * Restart charging by turning off and on the charger.
297 */
298static int try_charger_restart(struct charger_manager *cm)
299{
300 int err;
301
302 if (cm->emergency_stop)
303 return -EAGAIN;
304
305 err = try_charger_enable(cm, false);
306 if (err)
307 return err;
308
309 return try_charger_enable(cm, true);
310}
311
312/**
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900313 * uevent_notify - Let users know something has changed.
314 * @cm: the Charger Manager representing the battery.
315 * @event: the event string.
316 *
317 * If @event is null, it implies that uevent_notify is called
318 * by resume function. When called in the resume function, cm_suspended
319 * should be already reset to false in order to let uevent_notify
320 * notify the recent event during the suspend to users. While
321 * suspended, uevent_notify does not notify users, but tracks
322 * events so that uevent_notify can notify users later after resumed.
323 */
324static void uevent_notify(struct charger_manager *cm, const char *event)
325{
326 static char env_str[UEVENT_BUF_SIZE + 1] = "";
327 static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
328
329 if (cm_suspended) {
330 /* Nothing in suspended-event buffer */
331 if (env_str_save[0] == 0) {
332 if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
333 return; /* status not changed */
334 strncpy(env_str_save, event, UEVENT_BUF_SIZE);
335 return;
336 }
337
338 if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
339 return; /* Duplicated. */
Axel Linbb2a95c2012-01-12 12:56:35 +0800340 strncpy(env_str_save, event, UEVENT_BUF_SIZE);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900341 return;
342 }
343
344 if (event == NULL) {
345 /* No messages pending */
346 if (!env_str_save[0])
347 return;
348
349 strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
350 kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
351 env_str_save[0] = 0;
352
353 return;
354 }
355
356 /* status not changed */
357 if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
358 return;
359
360 /* save the status and notify the update */
361 strncpy(env_str, event, UEVENT_BUF_SIZE);
362 kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
363
364 dev_info(cm->dev, event);
365}
366
367/**
Chanwoo Choid829dc72012-05-05 06:24:10 -0700368 * fullbatt_vchk - Check voltage drop some times after "FULL" event.
369 * @work: the work_struct appointing the function
370 *
371 * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
372 * charger_desc, Charger Manager checks voltage drop after the battery
373 * "FULL" event. It checks whether the voltage has dropped more than
374 * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
375 */
376static void fullbatt_vchk(struct work_struct *work)
377{
378 struct delayed_work *dwork = to_delayed_work(work);
379 struct charger_manager *cm = container_of(dwork,
380 struct charger_manager, fullbatt_vchk_work);
381 struct charger_desc *desc = cm->desc;
382 int batt_uV, err, diff;
383
384 /* remove the appointment for fullbatt_vchk */
385 cm->fullbatt_vchk_jiffies_at = 0;
386
387 if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
388 return;
389
390 err = get_batt_uV(cm, &batt_uV);
391 if (err) {
392 dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
393 return;
394 }
395
396 diff = cm->fullbatt_vchk_uV;
397 diff -= batt_uV;
398
399 dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
400
401 if (diff > desc->fullbatt_vchkdrop_uV) {
402 try_charger_restart(cm);
403 uevent_notify(cm, "Recharge");
404 }
405}
406
407/**
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900408 * _cm_monitor - Monitor the temperature and return true for exceptions.
409 * @cm: the Charger Manager representing the battery.
410 *
411 * Returns true if there is an event to notify for the battery.
412 * (True if the status of "emergency_stop" changes)
413 */
414static bool _cm_monitor(struct charger_manager *cm)
415{
416 struct charger_desc *desc = cm->desc;
417 int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
418
419 dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n",
420 cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
421
422 /* It has been stopped or charging already */
423 if (!!temp == !!cm->emergency_stop)
424 return false;
425
426 if (temp) {
427 cm->emergency_stop = temp;
428 if (!try_charger_enable(cm, false)) {
429 if (temp > 0)
430 uevent_notify(cm, "OVERHEAT");
431 else
432 uevent_notify(cm, "COLD");
433 }
434 } else {
435 cm->emergency_stop = 0;
436 if (!try_charger_enable(cm, true))
437 uevent_notify(cm, "CHARGING");
438 }
439
440 return true;
441}
442
443/**
444 * cm_monitor - Monitor every battery.
445 *
446 * Returns true if there is an event to notify from any of the batteries.
447 * (True if the status of "emergency_stop" changes)
448 */
449static bool cm_monitor(void)
450{
451 bool stop = false;
452 struct charger_manager *cm;
453
454 mutex_lock(&cm_list_mtx);
455
Axel Linbb2a95c2012-01-12 12:56:35 +0800456 list_for_each_entry(cm, &cm_list, entry) {
457 if (_cm_monitor(cm))
458 stop = true;
459 }
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900460
461 mutex_unlock(&cm_list_mtx);
462
463 return stop;
464}
465
Chanwoo Choid829dc72012-05-05 06:24:10 -0700466/**
467 * _setup_polling - Setup the next instance of polling.
468 * @work: work_struct of the function _setup_polling.
469 */
470static void _setup_polling(struct work_struct *work)
471{
472 unsigned long min = ULONG_MAX;
473 struct charger_manager *cm;
474 bool keep_polling = false;
475 unsigned long _next_polling;
476
477 mutex_lock(&cm_list_mtx);
478
479 list_for_each_entry(cm, &cm_list, entry) {
480 if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
481 keep_polling = true;
482
483 if (min > cm->desc->polling_interval_ms)
484 min = cm->desc->polling_interval_ms;
485 }
486 }
487
488 polling_jiffy = msecs_to_jiffies(min);
489 if (polling_jiffy <= CM_JIFFIES_SMALL)
490 polling_jiffy = CM_JIFFIES_SMALL + 1;
491
492 if (!keep_polling)
493 polling_jiffy = ULONG_MAX;
494 if (polling_jiffy == ULONG_MAX)
495 goto out;
496
497 WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
498 ". try it later. %s\n", __func__);
499
500 _next_polling = jiffies + polling_jiffy;
501
502 if (!delayed_work_pending(&cm_monitor_work) ||
503 (delayed_work_pending(&cm_monitor_work) &&
504 time_after(next_polling, _next_polling))) {
505 cancel_delayed_work_sync(&cm_monitor_work);
506 next_polling = jiffies + polling_jiffy;
507 queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
508 }
509
510out:
511 mutex_unlock(&cm_list_mtx);
512}
513static DECLARE_WORK(setup_polling, _setup_polling);
514
515/**
516 * cm_monitor_poller - The Monitor / Poller.
517 * @work: work_struct of the function cm_monitor_poller
518 *
519 * During non-suspended state, cm_monitor_poller is used to poll and monitor
520 * the batteries.
521 */
522static void cm_monitor_poller(struct work_struct *work)
523{
524 cm_monitor();
525 schedule_work(&setup_polling);
526}
527
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900528static int charger_get_property(struct power_supply *psy,
529 enum power_supply_property psp,
530 union power_supply_propval *val)
531{
532 struct charger_manager *cm = container_of(psy,
533 struct charger_manager, charger_psy);
534 struct charger_desc *desc = cm->desc;
Anton Vorontsovdf58c042012-03-15 21:01:28 +0400535 int ret = 0;
536 int uV;
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900537
538 switch (psp) {
539 case POWER_SUPPLY_PROP_STATUS:
540 if (is_charging(cm))
541 val->intval = POWER_SUPPLY_STATUS_CHARGING;
542 else if (is_ext_pwr_online(cm))
543 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
544 else
545 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
546 break;
547 case POWER_SUPPLY_PROP_HEALTH:
548 if (cm->emergency_stop > 0)
549 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
550 else if (cm->emergency_stop < 0)
551 val->intval = POWER_SUPPLY_HEALTH_COLD;
552 else
553 val->intval = POWER_SUPPLY_HEALTH_GOOD;
554 break;
555 case POWER_SUPPLY_PROP_PRESENT:
556 if (is_batt_present(cm))
557 val->intval = 1;
558 else
559 val->intval = 0;
560 break;
561 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Anton Vorontsovdf58c042012-03-15 21:01:28 +0400562 ret = get_batt_uV(cm, &val->intval);
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900563 break;
564 case POWER_SUPPLY_PROP_CURRENT_NOW:
565 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
566 POWER_SUPPLY_PROP_CURRENT_NOW, val);
567 break;
568 case POWER_SUPPLY_PROP_TEMP:
569 /* in thenth of centigrade */
570 if (cm->last_temp_mC == INT_MIN)
571 desc->temperature_out_of_range(&cm->last_temp_mC);
572 val->intval = cm->last_temp_mC / 100;
573 if (!desc->measure_battery_temp)
574 ret = -ENODEV;
575 break;
576 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
577 /* in thenth of centigrade */
578 if (cm->last_temp_mC == INT_MIN)
579 desc->temperature_out_of_range(&cm->last_temp_mC);
580 val->intval = cm->last_temp_mC / 100;
581 if (desc->measure_battery_temp)
582 ret = -ENODEV;
583 break;
584 case POWER_SUPPLY_PROP_CAPACITY:
585 if (!cm->fuel_gauge) {
586 ret = -ENODEV;
587 break;
588 }
589
590 if (!is_batt_present(cm)) {
591 /* There is no battery. Assume 100% */
592 val->intval = 100;
593 break;
594 }
595
596 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
597 POWER_SUPPLY_PROP_CAPACITY, val);
598 if (ret)
599 break;
600
601 if (val->intval > 100) {
602 val->intval = 100;
603 break;
604 }
605 if (val->intval < 0)
606 val->intval = 0;
607
608 /* Do not adjust SOC when charging: voltage is overrated */
609 if (is_charging(cm))
610 break;
611
612 /*
613 * If the capacity value is inconsistent, calibrate it base on
614 * the battery voltage values and the thresholds given as desc
615 */
616 ret = get_batt_uV(cm, &uV);
617 if (ret) {
618 /* Voltage information not available. No calibration */
619 ret = 0;
620 break;
621 }
622
623 if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
624 !is_charging(cm)) {
625 val->intval = 100;
626 break;
627 }
628
629 break;
630 case POWER_SUPPLY_PROP_ONLINE:
631 if (is_ext_pwr_online(cm))
632 val->intval = 1;
633 else
634 val->intval = 0;
635 break;
636 case POWER_SUPPLY_PROP_CHARGE_FULL:
637 if (cm->fuel_gauge) {
638 if (cm->fuel_gauge->get_property(cm->fuel_gauge,
639 POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0)
640 break;
641 }
642
643 if (is_ext_pwr_online(cm)) {
644 /* Not full if it's charging. */
645 if (is_charging(cm)) {
646 val->intval = 0;
647 break;
648 }
649 /*
650 * Full if it's powered but not charging andi
651 * not forced stop by emergency
652 */
653 if (!cm->emergency_stop) {
654 val->intval = 1;
655 break;
656 }
657 }
658
659 /* Full if it's over the fullbatt voltage */
660 ret = get_batt_uV(cm, &uV);
661 if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
662 !is_charging(cm)) {
663 val->intval = 1;
664 break;
665 }
666
667 /* Full if the cap is 100 */
668 if (cm->fuel_gauge) {
669 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
670 POWER_SUPPLY_PROP_CAPACITY, val);
671 if (!ret && val->intval >= 100 && !is_charging(cm)) {
672 val->intval = 1;
673 break;
674 }
675 }
676
677 val->intval = 0;
678 ret = 0;
679 break;
680 case POWER_SUPPLY_PROP_CHARGE_NOW:
681 if (is_charging(cm)) {
682 ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
683 POWER_SUPPLY_PROP_CHARGE_NOW,
684 val);
685 if (ret) {
686 val->intval = 1;
687 ret = 0;
688 } else {
689 /* If CHARGE_NOW is supplied, use it */
690 val->intval = (val->intval > 0) ?
691 val->intval : 1;
692 }
693 } else {
694 val->intval = 0;
695 }
696 break;
697 default:
698 return -EINVAL;
699 }
700 return ret;
701}
702
703#define NUM_CHARGER_PSY_OPTIONAL (4)
704static enum power_supply_property default_charger_props[] = {
705 /* Guaranteed to provide */
706 POWER_SUPPLY_PROP_STATUS,
707 POWER_SUPPLY_PROP_HEALTH,
708 POWER_SUPPLY_PROP_PRESENT,
709 POWER_SUPPLY_PROP_VOLTAGE_NOW,
710 POWER_SUPPLY_PROP_CAPACITY,
711 POWER_SUPPLY_PROP_ONLINE,
712 POWER_SUPPLY_PROP_CHARGE_FULL,
713 /*
714 * Optional properties are:
715 * POWER_SUPPLY_PROP_CHARGE_NOW,
716 * POWER_SUPPLY_PROP_CURRENT_NOW,
717 * POWER_SUPPLY_PROP_TEMP, and
718 * POWER_SUPPLY_PROP_TEMP_AMBIENT,
719 */
720};
721
722static struct power_supply psy_default = {
723 .name = "battery",
724 .type = POWER_SUPPLY_TYPE_BATTERY,
725 .properties = default_charger_props,
726 .num_properties = ARRAY_SIZE(default_charger_props),
727 .get_property = charger_get_property,
728};
729
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900730/**
731 * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
732 * for suspend_again.
733 *
734 * Returns true if the alarm is set for Charger Manager to use.
735 * Returns false if
736 * cm_setup_timer fails to set an alarm,
737 * cm_setup_timer does not need to set an alarm for Charger Manager,
738 * or an alarm previously configured is to be used.
739 */
740static bool cm_setup_timer(void)
741{
742 struct charger_manager *cm;
743 unsigned int wakeup_ms = UINT_MAX;
744 bool ret = false;
745
746 mutex_lock(&cm_list_mtx);
747
748 list_for_each_entry(cm, &cm_list, entry) {
Chanwoo Choid829dc72012-05-05 06:24:10 -0700749 unsigned int fbchk_ms = 0;
750
751 /* fullbatt_vchk is required. setup timer for that */
752 if (cm->fullbatt_vchk_jiffies_at) {
753 fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
754 - jiffies);
755 if (time_is_before_eq_jiffies(
756 cm->fullbatt_vchk_jiffies_at) ||
757 msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
758 fullbatt_vchk(&cm->fullbatt_vchk_work.work);
759 fbchk_ms = 0;
760 }
761 }
762 CM_MIN_VALID(wakeup_ms, fbchk_ms);
763
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900764 /* Skip if polling is not required for this CM */
765 if (!is_polling_required(cm) && !cm->emergency_stop)
766 continue;
767 if (cm->desc->polling_interval_ms == 0)
768 continue;
769 CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
770 }
771
772 mutex_unlock(&cm_list_mtx);
773
774 if (wakeup_ms < UINT_MAX && wakeup_ms > 0) {
775 pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms);
776 if (rtc_dev) {
777 struct rtc_wkalrm tmp;
778 unsigned long time, now;
779 unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
780
781 /*
782 * Set alarm with the polling interval (wakeup_ms)
783 * except when rtc_wkalarm_save comes first.
784 * However, the alarm time should be NOW +
785 * CM_RTC_SMALL or later.
786 */
787 tmp.enabled = 1;
788 rtc_read_time(rtc_dev, &tmp.time);
789 rtc_tm_to_time(&tmp.time, &now);
790 if (add < CM_RTC_SMALL)
791 add = CM_RTC_SMALL;
792 time = now + add;
793
794 ret = true;
795
796 if (rtc_wkalarm_save.enabled &&
797 rtc_wkalarm_save_time &&
798 rtc_wkalarm_save_time < time) {
799 if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
800 time = now + CM_RTC_SMALL;
801 else
802 time = rtc_wkalarm_save_time;
803
804 /* The timer is not appointed by CM */
805 ret = false;
806 }
807
808 pr_info("Waking up after %lu secs.\n",
809 time - now);
810
811 rtc_time_to_tm(time, &tmp.time);
812 rtc_set_alarm(rtc_dev, &tmp);
813 cm_suspend_duration_ms += wakeup_ms;
814 return ret;
815 }
816 }
817
818 if (rtc_dev)
819 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
820 return false;
821}
822
Chanwoo Choid829dc72012-05-05 06:24:10 -0700823static void _cm_fbchk_in_suspend(struct charger_manager *cm)
824{
825 unsigned long jiffy_now = jiffies;
826
827 if (!cm->fullbatt_vchk_jiffies_at)
828 return;
829
830 if (g_desc && g_desc->assume_timer_stops_in_suspend)
831 jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
832
833 /* Execute now if it's going to be executed not too long after */
834 jiffy_now += CM_JIFFIES_SMALL;
835
836 if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
837 fullbatt_vchk(&cm->fullbatt_vchk_work.work);
838}
839
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900840/**
841 * cm_suspend_again - Determine whether suspend again or not
842 *
843 * Returns true if the system should be suspended again
844 * Returns false if the system should be woken up
845 */
846bool cm_suspend_again(void)
847{
848 struct charger_manager *cm;
849 bool ret = false;
850
851 if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
852 !cm_rtc_set)
853 return false;
854
855 if (cm_monitor())
856 goto out;
857
858 ret = true;
859 mutex_lock(&cm_list_mtx);
860 list_for_each_entry(cm, &cm_list, entry) {
Chanwoo Choid829dc72012-05-05 06:24:10 -0700861 _cm_fbchk_in_suspend(cm);
862
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900863 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
Axel Linbb2a95c2012-01-12 12:56:35 +0800864 cm->status_save_batt != is_batt_present(cm)) {
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900865 ret = false;
Axel Linbb2a95c2012-01-12 12:56:35 +0800866 break;
867 }
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900868 }
869 mutex_unlock(&cm_list_mtx);
870
871 cm_rtc_set = cm_setup_timer();
872out:
873 /* It's about the time when the non-CM appointed timer goes off */
874 if (rtc_wkalarm_save.enabled) {
875 unsigned long now;
876 struct rtc_time tmp;
877
878 rtc_read_time(rtc_dev, &tmp);
879 rtc_tm_to_time(&tmp, &now);
880
881 if (rtc_wkalarm_save_time &&
882 now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
883 return false;
884 }
885 return ret;
886}
887EXPORT_SYMBOL_GPL(cm_suspend_again);
888
889/**
890 * setup_charger_manager - initialize charger_global_desc data
891 * @gd: pointer to instance of charger_global_desc
892 */
893int setup_charger_manager(struct charger_global_desc *gd)
894{
895 if (!gd)
896 return -EINVAL;
897
898 if (rtc_dev)
899 rtc_class_close(rtc_dev);
900 rtc_dev = NULL;
901 g_desc = NULL;
902
903 if (!gd->rtc_only_wakeup) {
904 pr_err("The callback rtc_only_wakeup is not given.\n");
905 return -EINVAL;
906 }
907
908 if (gd->rtc_name) {
909 rtc_dev = rtc_class_open(gd->rtc_name);
910 if (IS_ERR_OR_NULL(rtc_dev)) {
911 rtc_dev = NULL;
912 /* Retry at probe. RTC may be not registered yet */
913 }
914 } else {
915 pr_warn("No wakeup timer is given for charger manager."
916 "In-suspend monitoring won't work.\n");
917 }
918
919 g_desc = gd;
920 return 0;
921}
922EXPORT_SYMBOL_GPL(setup_charger_manager);
923
924static int charger_manager_probe(struct platform_device *pdev)
925{
926 struct charger_desc *desc = dev_get_platdata(&pdev->dev);
927 struct charger_manager *cm;
928 int ret = 0, i = 0;
Donggeun Kimad3d13ee2011-12-27 18:47:49 +0900929 union power_supply_propval val;
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900930
931 if (g_desc && !rtc_dev && g_desc->rtc_name) {
932 rtc_dev = rtc_class_open(g_desc->rtc_name);
933 if (IS_ERR_OR_NULL(rtc_dev)) {
934 rtc_dev = NULL;
935 dev_err(&pdev->dev, "Cannot get RTC %s.\n",
936 g_desc->rtc_name);
937 ret = -ENODEV;
938 goto err_alloc;
939 }
940 }
941
942 if (!desc) {
943 dev_err(&pdev->dev, "No platform data (desc) found.\n");
944 ret = -ENODEV;
945 goto err_alloc;
946 }
947
948 cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
949 if (!cm) {
950 dev_err(&pdev->dev, "Cannot allocate memory.\n");
951 ret = -ENOMEM;
952 goto err_alloc;
953 }
954
955 /* Basic Values. Unspecified are Null or 0 */
956 cm->dev = &pdev->dev;
957 cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
958 if (!cm->desc) {
959 dev_err(&pdev->dev, "Cannot allocate memory.\n");
960 ret = -ENOMEM;
961 goto err_alloc_desc;
962 }
963 memcpy(cm->desc, desc, sizeof(struct charger_desc));
964 cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
965
Chanwoo Choid829dc72012-05-05 06:24:10 -0700966 /*
967 * The following two do not need to be errors.
968 * Users may intentionally ignore those two features.
969 */
970 if (desc->fullbatt_uV == 0) {
971 dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
972 " as it is not supplied.");
973 }
974 if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
975 dev_info(&pdev->dev, "Disabling full-battery voltage drop "
976 "checking mechanism as it is not supplied.");
977 desc->fullbatt_vchkdrop_ms = 0;
978 desc->fullbatt_vchkdrop_uV = 0;
979 }
980
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +0900981 if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
982 ret = -EINVAL;
983 dev_err(&pdev->dev, "charger_regulators undefined.\n");
984 goto err_no_charger;
985 }
986
987 if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
988 dev_err(&pdev->dev, "No power supply defined.\n");
989 ret = -EINVAL;
990 goto err_no_charger_stat;
991 }
992
993 /* Counting index only */
994 while (desc->psy_charger_stat[i])
995 i++;
996
997 cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
998 GFP_KERNEL);
999 if (!cm->charger_stat) {
1000 ret = -ENOMEM;
1001 goto err_no_charger_stat;
1002 }
1003
1004 for (i = 0; desc->psy_charger_stat[i]; i++) {
1005 cm->charger_stat[i] = power_supply_get_by_name(
1006 desc->psy_charger_stat[i]);
1007 if (!cm->charger_stat[i]) {
1008 dev_err(&pdev->dev, "Cannot find power supply "
1009 "\"%s\"\n",
1010 desc->psy_charger_stat[i]);
1011 ret = -ENODEV;
1012 goto err_chg_stat;
1013 }
1014 }
1015
1016 cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
1017 if (!cm->fuel_gauge) {
1018 dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
1019 desc->psy_fuel_gauge);
1020 ret = -ENODEV;
1021 goto err_chg_stat;
1022 }
1023
1024 if (desc->polling_interval_ms == 0 ||
1025 msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
1026 dev_err(&pdev->dev, "polling_interval_ms is too small\n");
1027 ret = -EINVAL;
1028 goto err_chg_stat;
1029 }
1030
1031 if (!desc->temperature_out_of_range) {
1032 dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
1033 ret = -EINVAL;
1034 goto err_chg_stat;
1035 }
1036
1037 platform_set_drvdata(pdev, cm);
1038
Axel Linbb2a95c2012-01-12 12:56:35 +08001039 memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
1040
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001041 if (!desc->psy_name) {
Axel Linbb2a95c2012-01-12 12:56:35 +08001042 strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001043 } else {
1044 strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
1045 }
1046 cm->charger_psy.name = cm->psy_name_buf;
1047
1048 /* Allocate for psy properties because they may vary */
1049 cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property)
1050 * (ARRAY_SIZE(default_charger_props) +
1051 NUM_CHARGER_PSY_OPTIONAL),
1052 GFP_KERNEL);
1053 if (!cm->charger_psy.properties) {
1054 dev_err(&pdev->dev, "Cannot allocate for psy properties.\n");
1055 ret = -ENOMEM;
1056 goto err_chg_stat;
1057 }
1058 memcpy(cm->charger_psy.properties, default_charger_props,
1059 sizeof(enum power_supply_property) *
1060 ARRAY_SIZE(default_charger_props));
1061 cm->charger_psy.num_properties = psy_default.num_properties;
1062
1063 /* Find which optional psy-properties are available */
1064 if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
1065 POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
1066 cm->charger_psy.properties[cm->charger_psy.num_properties] =
1067 POWER_SUPPLY_PROP_CHARGE_NOW;
1068 cm->charger_psy.num_properties++;
1069 }
1070 if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
1071 POWER_SUPPLY_PROP_CURRENT_NOW,
1072 &val)) {
1073 cm->charger_psy.properties[cm->charger_psy.num_properties] =
1074 POWER_SUPPLY_PROP_CURRENT_NOW;
1075 cm->charger_psy.num_properties++;
1076 }
Axel Linbb2a95c2012-01-12 12:56:35 +08001077
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001078 if (desc->measure_battery_temp) {
1079 cm->charger_psy.properties[cm->charger_psy.num_properties] =
1080 POWER_SUPPLY_PROP_TEMP;
1081 cm->charger_psy.num_properties++;
Axel Linbb2a95c2012-01-12 12:56:35 +08001082 } else {
1083 cm->charger_psy.properties[cm->charger_psy.num_properties] =
1084 POWER_SUPPLY_PROP_TEMP_AMBIENT;
1085 cm->charger_psy.num_properties++;
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001086 }
1087
Chanwoo Choid829dc72012-05-05 06:24:10 -07001088 INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
1089
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001090 ret = power_supply_register(NULL, &cm->charger_psy);
1091 if (ret) {
1092 dev_err(&pdev->dev, "Cannot register charger-manager with"
1093 " name \"%s\".\n", cm->charger_psy.name);
1094 goto err_register;
1095 }
1096
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001097 ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
1098 desc->charger_regulators);
1099 if (ret) {
1100 dev_err(&pdev->dev, "Cannot get charger regulators.\n");
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001101 goto err_bulk_get;
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001102 }
1103
1104 ret = try_charger_enable(cm, true);
1105 if (ret) {
1106 dev_err(&pdev->dev, "Cannot enable charger regulators\n");
1107 goto err_chg_enable;
1108 }
1109
1110 /* Add to the list */
1111 mutex_lock(&cm_list_mtx);
1112 list_add(&cm->entry, &cm_list);
1113 mutex_unlock(&cm_list_mtx);
1114
Chanwoo Choid829dc72012-05-05 06:24:10 -07001115 schedule_work(&setup_polling);
1116
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001117 return 0;
1118
1119err_chg_enable:
Axel Linbb2a95c2012-01-12 12:56:35 +08001120 regulator_bulk_free(desc->num_charger_regulators,
1121 desc->charger_regulators);
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001122err_bulk_get:
1123 power_supply_unregister(&cm->charger_psy);
1124err_register:
1125 kfree(cm->charger_psy.properties);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001126err_chg_stat:
1127 kfree(cm->charger_stat);
1128err_no_charger_stat:
1129err_no_charger:
1130 kfree(cm->desc);
1131err_alloc_desc:
1132 kfree(cm);
1133err_alloc:
1134 return ret;
1135}
1136
1137static int __devexit charger_manager_remove(struct platform_device *pdev)
1138{
1139 struct charger_manager *cm = platform_get_drvdata(pdev);
1140 struct charger_desc *desc = cm->desc;
1141
1142 /* Remove from the list */
1143 mutex_lock(&cm_list_mtx);
1144 list_del(&cm->entry);
1145 mutex_unlock(&cm_list_mtx);
1146
Chanwoo Choid829dc72012-05-05 06:24:10 -07001147 if (work_pending(&setup_polling))
1148 cancel_work_sync(&setup_polling);
1149 if (delayed_work_pending(&cm_monitor_work))
1150 cancel_delayed_work_sync(&cm_monitor_work);
1151
Axel Linbb2a95c2012-01-12 12:56:35 +08001152 regulator_bulk_free(desc->num_charger_regulators,
1153 desc->charger_regulators);
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001154 power_supply_unregister(&cm->charger_psy);
Chanwoo Choid829dc72012-05-05 06:24:10 -07001155
1156 try_charger_enable(cm, false);
1157
Donggeun Kimad3d13ee2011-12-27 18:47:49 +09001158 kfree(cm->charger_psy.properties);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001159 kfree(cm->charger_stat);
1160 kfree(cm->desc);
1161 kfree(cm);
1162
1163 return 0;
1164}
1165
Axel Lin1bbe24d2012-01-11 17:19:45 +08001166static const struct platform_device_id charger_manager_id[] = {
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001167 { "charger-manager", 0 },
1168 { },
1169};
Axel Lin1bbe24d2012-01-11 17:19:45 +08001170MODULE_DEVICE_TABLE(platform, charger_manager_id);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001171
1172static int cm_suspend_prepare(struct device *dev)
1173{
Axel Linbb2a95c2012-01-12 12:56:35 +08001174 struct charger_manager *cm = dev_get_drvdata(dev);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001175
1176 if (!cm_suspended) {
1177 if (rtc_dev) {
1178 struct rtc_time tmp;
1179 unsigned long now;
1180
1181 rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
1182 rtc_read_time(rtc_dev, &tmp);
1183
1184 if (rtc_wkalarm_save.enabled) {
1185 rtc_tm_to_time(&rtc_wkalarm_save.time,
1186 &rtc_wkalarm_save_time);
1187 rtc_tm_to_time(&tmp, &now);
1188 if (now > rtc_wkalarm_save_time)
1189 rtc_wkalarm_save_time = 0;
1190 } else {
1191 rtc_wkalarm_save_time = 0;
1192 }
1193 }
1194 cm_suspended = true;
1195 }
1196
Chanwoo Choid829dc72012-05-05 06:24:10 -07001197 if (delayed_work_pending(&cm->fullbatt_vchk_work))
1198 cancel_delayed_work(&cm->fullbatt_vchk_work);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001199 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
1200 cm->status_save_batt = is_batt_present(cm);
1201
1202 if (!cm_rtc_set) {
1203 cm_suspend_duration_ms = 0;
1204 cm_rtc_set = cm_setup_timer();
1205 }
1206
1207 return 0;
1208}
1209
1210static void cm_suspend_complete(struct device *dev)
1211{
Axel Linbb2a95c2012-01-12 12:56:35 +08001212 struct charger_manager *cm = dev_get_drvdata(dev);
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001213
1214 if (cm_suspended) {
1215 if (rtc_dev) {
1216 struct rtc_wkalrm tmp;
1217
1218 rtc_read_alarm(rtc_dev, &tmp);
1219 rtc_wkalarm_save.pending = tmp.pending;
1220 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
1221 }
1222 cm_suspended = false;
1223 cm_rtc_set = false;
1224 }
1225
Chanwoo Choid829dc72012-05-05 06:24:10 -07001226 /* Re-enqueue delayed work (fullbatt_vchk_work) */
1227 if (cm->fullbatt_vchk_jiffies_at) {
1228 unsigned long delay = 0;
1229 unsigned long now = jiffies + CM_JIFFIES_SMALL;
1230
1231 if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
1232 delay = (unsigned long)((long)now
1233 - (long)(cm->fullbatt_vchk_jiffies_at));
1234 delay = jiffies_to_msecs(delay);
1235 } else {
1236 delay = 0;
1237 }
1238
1239 /*
1240 * Account for cm_suspend_duration_ms if
1241 * assume_timer_stops_in_suspend is active
1242 */
1243 if (g_desc && g_desc->assume_timer_stops_in_suspend) {
1244 if (delay > cm_suspend_duration_ms)
1245 delay -= cm_suspend_duration_ms;
1246 else
1247 delay = 0;
1248 }
1249
1250 queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
1251 msecs_to_jiffies(delay));
1252 }
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001253 uevent_notify(cm, NULL);
1254}
1255
1256static const struct dev_pm_ops charger_manager_pm = {
1257 .prepare = cm_suspend_prepare,
1258 .complete = cm_suspend_complete,
1259};
1260
1261static struct platform_driver charger_manager_driver = {
1262 .driver = {
1263 .name = "charger-manager",
1264 .owner = THIS_MODULE,
1265 .pm = &charger_manager_pm,
1266 },
1267 .probe = charger_manager_probe,
1268 .remove = __devexit_p(charger_manager_remove),
1269 .id_table = charger_manager_id,
1270};
1271
1272static int __init charger_manager_init(void)
1273{
Chanwoo Choid829dc72012-05-05 06:24:10 -07001274 cm_wq = create_freezable_workqueue("charger_manager");
1275 INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
1276
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001277 return platform_driver_register(&charger_manager_driver);
1278}
1279late_initcall(charger_manager_init);
1280
1281static void __exit charger_manager_cleanup(void)
1282{
Chanwoo Choid829dc72012-05-05 06:24:10 -07001283 destroy_workqueue(cm_wq);
1284 cm_wq = NULL;
1285
Donggeun Kim3bb3dbb2011-12-27 18:47:48 +09001286 platform_driver_unregister(&charger_manager_driver);
1287}
1288module_exit(charger_manager_cleanup);
1289
1290MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1291MODULE_DESCRIPTION("Charger Manager");
1292MODULE_LICENSE("GPL");