aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonghwa Lee <jonghwa3.lee@samsung.com>2014-12-19 03:55:13 -0500
committerSebastian Reichel <sre@kernel.org>2015-01-21 14:52:07 -0500
commitc1155c64e603378dccfc21ee0612cf60dd11725b (patch)
tree7e532baf21fd51eb6a70339786152aceec54a206
parenta538cf04ef67861a208075a6d57d0f045822e1d6 (diff)
power: charger-manager: Use alarmtimer for battery monitoring in suspend.
To guerantee proper charing and managing batteries even in suspend, charger-manager has used rtc device with rtc framework interface. However, it is better to use alarmtimer for cleaner and more appropriate operation. This patch makes driver to use alarmtimer for polling work in suspend and removes all deprecated codes related with using rtc interface. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r--drivers/power/Kconfig2
-rw-r--r--drivers/power/charger-manager.c289
-rw-r--r--include/linux/power/charger-manager.h32
3 files changed, 84 insertions, 239 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index e2569a538501..a79f16afb588 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -315,7 +315,7 @@ config CHARGER_GPIO
315 315
316config CHARGER_MANAGER 316config CHARGER_MANAGER
317 bool "Battery charger manager for multiple chargers" 317 bool "Battery charger manager for multiple chargers"
318 depends on REGULATOR && RTC_CLASS 318 depends on REGULATOR
319 select EXTCON 319 select EXTCON
320 help 320 help
321 Say Y to enable charger-manager support, which allows multiple 321 Say Y to enable charger-manager support, which allows multiple
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 649052e1f2d9..14b0d85318eb 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -69,16 +69,10 @@ static LIST_HEAD(cm_list);
69static DEFINE_MUTEX(cm_list_mtx); 69static DEFINE_MUTEX(cm_list_mtx);
70 70
71/* About in-suspend (suspend-again) monitoring */ 71/* About in-suspend (suspend-again) monitoring */
72static struct rtc_device *rtc_dev; 72static struct alarm *cm_timer;
73/* 73
74 * Backup RTC alarm
75 * Save the wakeup alarm before entering suspend-to-RAM
76 */
77static struct rtc_wkalrm rtc_wkalarm_save;
78/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
79static unsigned long rtc_wkalarm_save_time;
80static bool cm_suspended; 74static bool cm_suspended;
81static bool cm_rtc_set; 75static bool cm_timer_set;
82static unsigned long cm_suspend_duration_ms; 76static unsigned long cm_suspend_duration_ms;
83 77
84/* About normal (not suspended) monitoring */ 78/* About normal (not suspended) monitoring */
@@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */
87static struct workqueue_struct *cm_wq; /* init at driver add */ 81static struct workqueue_struct *cm_wq; /* init at driver add */
88static struct delayed_work cm_monitor_work; /* init at driver add */ 82static struct delayed_work cm_monitor_work; /* init at driver add */
89 83
90/* Global charger-manager description */
91static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
92
93/** 84/**
94 * is_batt_present - See if the battery presents in place. 85 * is_batt_present - See if the battery presents in place.
95 * @cm: the Charger Manager representing the battery. 86 * @cm: the Charger Manager representing the battery.
@@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void)
1047{ 1038{
1048 struct charger_manager *cm; 1039 struct charger_manager *cm;
1049 unsigned int wakeup_ms = UINT_MAX; 1040 unsigned int wakeup_ms = UINT_MAX;
1050 bool ret = false; 1041 int timer_req = 0;
1051 1042
1052 mutex_lock(&cm_list_mtx); 1043 if (time_after(next_polling, jiffies))
1044 CM_MIN_VALID(wakeup_ms,
1045 jiffies_to_msecs(next_polling - jiffies));
1053 1046
1047 mutex_lock(&cm_list_mtx);
1054 list_for_each_entry(cm, &cm_list, entry) { 1048 list_for_each_entry(cm, &cm_list, entry) {
1055 unsigned int fbchk_ms = 0; 1049 unsigned int fbchk_ms = 0;
1056 1050
@@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void)
1070 /* Skip if polling is not required for this CM */ 1064 /* Skip if polling is not required for this CM */
1071 if (!is_polling_required(cm) && !cm->emergency_stop) 1065 if (!is_polling_required(cm) && !cm->emergency_stop)
1072 continue; 1066 continue;
1067 timer_req++;
1073 if (cm->desc->polling_interval_ms == 0) 1068 if (cm->desc->polling_interval_ms == 0)
1074 continue; 1069 continue;
1075 CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); 1070 CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
1076 } 1071 }
1077
1078 mutex_unlock(&cm_list_mtx); 1072 mutex_unlock(&cm_list_mtx);
1079 1073
1080 if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { 1074 if (timer_req && cm_timer) {
1081 pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); 1075 ktime_t now, add;
1082 if (rtc_dev) {
1083 struct rtc_wkalrm tmp;
1084 unsigned long time, now;
1085 unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
1086
1087 /*
1088 * Set alarm with the polling interval (wakeup_ms)
1089 * except when rtc_wkalarm_save comes first.
1090 * However, the alarm time should be NOW +
1091 * CM_RTC_SMALL or later.
1092 */
1093 tmp.enabled = 1;
1094 rtc_read_time(rtc_dev, &tmp.time);
1095 rtc_tm_to_time(&tmp.time, &now);
1096 if (add < CM_RTC_SMALL)
1097 add = CM_RTC_SMALL;
1098 time = now + add;
1099 1076
1100 ret = true; 1077 /*
1078 * Set alarm with the polling interval (wakeup_ms)
1079 * The alarm time should be NOW + CM_RTC_SMALL or later.
1080 */
1081 if (wakeup_ms == UINT_MAX ||
1082 wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
1083 wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
1101 1084
1102 if (rtc_wkalarm_save.enabled && 1085 pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
1103 rtc_wkalarm_save_time &&
1104 rtc_wkalarm_save_time < time) {
1105 if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
1106 time = now + CM_RTC_SMALL;
1107 else
1108 time = rtc_wkalarm_save_time;
1109 1086
1110 /* The timer is not appointed by CM */ 1087 now = ktime_get_boottime();
1111 ret = false; 1088 add = ktime_set(wakeup_ms / MSEC_PER_SEC,
1112 } 1089 (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
1090 alarm_start(cm_timer, ktime_add(now, add));
1113 1091
1114 pr_info("Waking up after %lu secs\n", time - now); 1092 cm_suspend_duration_ms = wakeup_ms;
1115 1093
1116 rtc_time_to_tm(time, &tmp.time); 1094 return true;
1117 rtc_set_alarm(rtc_dev, &tmp);
1118 cm_suspend_duration_ms += wakeup_ms;
1119 return ret;
1120 }
1121 } 1095 }
1122
1123 if (rtc_dev)
1124 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
1125 return false; 1096 return false;
1126} 1097}
1127 1098
1128static void _cm_fbchk_in_suspend(struct charger_manager *cm)
1129{
1130 unsigned long jiffy_now = jiffies;
1131
1132 if (!cm->fullbatt_vchk_jiffies_at)
1133 return;
1134
1135 if (g_desc && g_desc->assume_timer_stops_in_suspend)
1136 jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
1137
1138 /* Execute now if it's going to be executed not too long after */
1139 jiffy_now += CM_JIFFIES_SMALL;
1140
1141 if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
1142 fullbatt_vchk(&cm->fullbatt_vchk_work.work);
1143}
1144
1145/**
1146 * cm_suspend_again - Determine whether suspend again or not
1147 *
1148 * Returns true if the system should be suspended again
1149 * Returns false if the system should be woken up
1150 */
1151bool cm_suspend_again(void)
1152{
1153 struct charger_manager *cm;
1154 bool ret = false;
1155
1156 if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
1157 !cm_rtc_set)
1158 return false;
1159
1160 if (cm_monitor())
1161 goto out;
1162
1163 ret = true;
1164 mutex_lock(&cm_list_mtx);
1165 list_for_each_entry(cm, &cm_list, entry) {
1166 _cm_fbchk_in_suspend(cm);
1167
1168 if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
1169 cm->status_save_batt != is_batt_present(cm)) {
1170 ret = false;
1171 break;
1172 }
1173 }
1174 mutex_unlock(&cm_list_mtx);
1175
1176 cm_rtc_set = cm_setup_timer();
1177out:
1178 /* It's about the time when the non-CM appointed timer goes off */
1179 if (rtc_wkalarm_save.enabled) {
1180 unsigned long now;
1181 struct rtc_time tmp;
1182
1183 rtc_read_time(rtc_dev, &tmp);
1184 rtc_tm_to_time(&tmp, &now);
1185
1186 if (rtc_wkalarm_save_time &&
1187 now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
1188 return false;
1189 }
1190 return ret;
1191}
1192EXPORT_SYMBOL_GPL(cm_suspend_again);
1193
1194/**
1195 * setup_charger_manager - initialize charger_global_desc data
1196 * @gd: pointer to instance of charger_global_desc
1197 */
1198int setup_charger_manager(struct charger_global_desc *gd)
1199{
1200 if (!gd)
1201 return -EINVAL;
1202
1203 if (rtc_dev)
1204 rtc_class_close(rtc_dev);
1205 rtc_dev = NULL;
1206 g_desc = NULL;
1207
1208 if (!gd->rtc_only_wakeup) {
1209 pr_err("The callback rtc_only_wakeup is not given\n");
1210 return -EINVAL;
1211 }
1212
1213 if (gd->rtc_name) {
1214 rtc_dev = rtc_class_open(gd->rtc_name);
1215 if (IS_ERR_OR_NULL(rtc_dev)) {
1216 rtc_dev = NULL;
1217 /* Retry at probe. RTC may be not registered yet */
1218 }
1219 } else {
1220 pr_warn("No wakeup timer is given for charger manager. "
1221 "In-suspend monitoring won't work.\n");
1222 }
1223
1224 g_desc = gd;
1225 return 0;
1226}
1227EXPORT_SYMBOL_GPL(setup_charger_manager);
1228
1229/** 1099/**
1230 * charger_extcon_work - enable/diable charger according to the state 1100 * charger_extcon_work - enable/diable charger according to the state
1231 * of charger cable 1101 * of charger cable
@@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
1719 return dev_get_platdata(&pdev->dev); 1589 return dev_get_platdata(&pdev->dev);
1720} 1590}
1721 1591
1592static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
1593{
1594 cm_timer_set = false;
1595 return ALARMTIMER_NORESTART;
1596}
1597
1722static int charger_manager_probe(struct platform_device *pdev) 1598static int charger_manager_probe(struct platform_device *pdev)
1723{ 1599{
1724 struct charger_desc *desc = cm_get_drv_data(pdev); 1600 struct charger_desc *desc = cm_get_drv_data(pdev);
@@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev)
1728 union power_supply_propval val; 1604 union power_supply_propval val;
1729 struct power_supply *fuel_gauge; 1605 struct power_supply *fuel_gauge;
1730 1606
1731 if (g_desc && !rtc_dev && g_desc->rtc_name) {
1732 rtc_dev = rtc_class_open(g_desc->rtc_name);
1733 if (IS_ERR_OR_NULL(rtc_dev)) {
1734 rtc_dev = NULL;
1735 dev_err(&pdev->dev, "Cannot get RTC %s\n",
1736 g_desc->rtc_name);
1737 return -ENODEV;
1738 }
1739 }
1740
1741 if (IS_ERR(desc)) { 1607 if (IS_ERR(desc)) {
1742 dev_err(&pdev->dev, "No platform data (desc) found\n"); 1608 dev_err(&pdev->dev, "No platform data (desc) found\n");
1743 return -ENODEV; 1609 return -ENODEV;
@@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev)
1752 cm->dev = &pdev->dev; 1618 cm->dev = &pdev->dev;
1753 cm->desc = desc; 1619 cm->desc = desc;
1754 1620
1621 /* Initialize alarm timer */
1622 if (alarmtimer_get_rtcdev()) {
1623 cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
1624 alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
1625 }
1626
1755 /* 1627 /*
1756 * The following two do not need to be errors. 1628 * The following two do not need to be errors.
1757 * Users may intentionally ignore those two features. 1629 * Users may intentionally ignore those two features.
@@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev)
1993 return ret; 1865 return ret;
1994} 1866}
1995 1867
1868static bool cm_need_to_awake(void)
1869{
1870 struct charger_manager *cm;
1871
1872 if (cm_timer)
1873 return false;
1874
1875 mutex_lock(&cm_list_mtx);
1876 list_for_each_entry(cm, &cm_list, entry) {
1877 if (is_charging(cm)) {
1878 mutex_unlock(&cm_list_mtx);
1879 return true;
1880 }
1881 }
1882 mutex_unlock(&cm_list_mtx);
1883
1884 return false;
1885}
1886
1996static int cm_suspend_prepare(struct device *dev) 1887static int cm_suspend_prepare(struct device *dev)
1997{ 1888{
1998 struct charger_manager *cm = dev_get_drvdata(dev); 1889 struct charger_manager *cm = dev_get_drvdata(dev);
1999 1890
2000 if (!cm_suspended) { 1891 if (cm_need_to_awake())
2001 if (rtc_dev) { 1892 return -EBUSY;
2002 struct rtc_time tmp;
2003 unsigned long now;
2004
2005 rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
2006 rtc_read_time(rtc_dev, &tmp);
2007 1893
2008 if (rtc_wkalarm_save.enabled) { 1894 if (!cm_suspended)
2009 rtc_tm_to_time(&rtc_wkalarm_save.time,
2010 &rtc_wkalarm_save_time);
2011 rtc_tm_to_time(&tmp, &now);
2012 if (now > rtc_wkalarm_save_time)
2013 rtc_wkalarm_save_time = 0;
2014 } else {
2015 rtc_wkalarm_save_time = 0;
2016 }
2017 }
2018 cm_suspended = true; 1895 cm_suspended = true;
2019 }
2020 1896
2021 cancel_delayed_work(&cm->fullbatt_vchk_work); 1897 cm_timer_set = cm_setup_timer();
2022 cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
2023 cm->status_save_batt = is_batt_present(cm);
2024 1898
2025 if (!cm_rtc_set) { 1899 if (cm_timer_set) {
2026 cm_suspend_duration_ms = 0; 1900 cancel_work_sync(&setup_polling);
2027 cm_rtc_set = cm_setup_timer(); 1901 cancel_delayed_work_sync(&cm_monitor_work);
1902 cancel_delayed_work(&cm->fullbatt_vchk_work);
2028 } 1903 }
2029 1904
2030 return 0; 1905 return 0;
@@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev)
2034{ 1909{
2035 struct charger_manager *cm = dev_get_drvdata(dev); 1910 struct charger_manager *cm = dev_get_drvdata(dev);
2036 1911
2037 if (cm_suspended) { 1912 if (cm_suspended)
2038 if (rtc_dev) {
2039 struct rtc_wkalrm tmp;
2040
2041 rtc_read_alarm(rtc_dev, &tmp);
2042 rtc_wkalarm_save.pending = tmp.pending;
2043 rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
2044 }
2045 cm_suspended = false; 1913 cm_suspended = false;
2046 cm_rtc_set = false; 1914
1915 if (cm_timer_set) {
1916 ktime_t remain;
1917
1918 alarm_cancel(cm_timer);
1919 cm_timer_set = false;
1920 remain = alarm_expires_remaining(cm_timer);
1921 cm_suspend_duration_ms -= ktime_to_ms(remain);
1922 schedule_work(&setup_polling);
2047 } 1923 }
2048 1924
1925 _cm_monitor(cm);
1926
2049 /* Re-enqueue delayed work (fullbatt_vchk_work) */ 1927 /* Re-enqueue delayed work (fullbatt_vchk_work) */
2050 if (cm->fullbatt_vchk_jiffies_at) { 1928 if (cm->fullbatt_vchk_jiffies_at) {
2051 unsigned long delay = 0; 1929 unsigned long delay = 0;
@@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev)
2060 } 1938 }
2061 1939
2062 /* 1940 /*
2063 * Account for cm_suspend_duration_ms if 1941 * Account for cm_suspend_duration_ms with assuming that
2064 * assume_timer_stops_in_suspend is active 1942 * timer stops in suspend.
2065 */ 1943 */
2066 if (g_desc && g_desc->assume_timer_stops_in_suspend) { 1944 if (delay > cm_suspend_duration_ms)
2067 if (delay > cm_suspend_duration_ms) 1945 delay -= cm_suspend_duration_ms;
2068 delay -= cm_suspend_duration_ms; 1946 else
2069 else 1947 delay = 0;
2070 delay = 0;
2071 }
2072 1948
2073 queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, 1949 queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
2074 msecs_to_jiffies(delay)); 1950 msecs_to_jiffies(delay));
2075 } 1951 }
2076 device_set_wakeup_capable(cm->dev, false); 1952 device_set_wakeup_capable(cm->dev, false);
2077 uevent_notify(cm, NULL);
2078} 1953}
2079 1954
2080static const struct dev_pm_ops charger_manager_pm = { 1955static const struct dev_pm_ops charger_manager_pm = {
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
index e97fc656a058..416ebeb6ee1e 100644
--- a/include/linux/power/charger-manager.h
+++ b/include/linux/power/charger-manager.h
@@ -17,6 +17,7 @@
17 17
18#include <linux/power_supply.h> 18#include <linux/power_supply.h>
19#include <linux/extcon.h> 19#include <linux/extcon.h>
20#include <linux/alarmtimer.h>
20 21
21enum data_source { 22enum data_source {
22 CM_BATTERY_PRESENT, 23 CM_BATTERY_PRESENT,
@@ -45,29 +46,6 @@ enum cm_event_types {
45}; 46};
46 47
47/** 48/**
48 * struct charger_global_desc
49 * @rtc_name: the name of RTC used to wake up the system from suspend.
50 * @rtc_only_wakeup:
51 * If the system is woken up by waekup-sources other than the RTC or
52 * callbacks, Charger Manager should recognize with
53 * rtc_only_wakeup() returning false.
54 * If the RTC given to CM is the only wakeup reason,
55 * rtc_only_wakeup should return true.
56 * @assume_timer_stops_in_suspend:
57 * Assume that the jiffy timer stops in suspend-to-RAM.
58 * When enabled, CM does not rely on jiffies value in
59 * suspend_again and assumes that jiffies value does not
60 * change during suspend.
61 */
62struct charger_global_desc {
63 char *rtc_name;
64
65 bool (*rtc_only_wakeup)(void);
66
67 bool assume_timer_stops_in_suspend;
68};
69
70/**
71 * struct charger_cable 49 * struct charger_cable
72 * @extcon_name: the name of extcon device. 50 * @extcon_name: the name of extcon device.
73 * @name: the name of charger cable(external connector). 51 * @name: the name of charger cable(external connector).
@@ -266,22 +244,14 @@ struct charger_manager {
266 char psy_name_buf[PSY_NAME_MAX + 1]; 244 char psy_name_buf[PSY_NAME_MAX + 1];
267 struct power_supply charger_psy; 245 struct power_supply charger_psy;
268 246
269 bool status_save_ext_pwr_inserted;
270 bool status_save_batt;
271
272 u64 charging_start_time; 247 u64 charging_start_time;
273 u64 charging_end_time; 248 u64 charging_end_time;
274}; 249};
275 250
276#ifdef CONFIG_CHARGER_MANAGER 251#ifdef CONFIG_CHARGER_MANAGER
277extern int setup_charger_manager(struct charger_global_desc *gd);
278extern bool cm_suspend_again(void);
279extern void cm_notify_event(struct power_supply *psy, 252extern void cm_notify_event(struct power_supply *psy,
280 enum cm_event_types type, char *msg); 253 enum cm_event_types type, char *msg);
281#else 254#else
282static inline int setup_charger_manager(struct charger_global_desc *gd)
283{ return 0; }
284static inline bool cm_suspend_again(void) { return false; }
285static inline void cm_notify_event(struct power_supply *psy, 255static inline void cm_notify_event(struct power_supply *psy,
286 enum cm_event_types type, char *msg) { } 256 enum cm_event_types type, char *msg) { }
287#endif 257#endif