diff options
-rw-r--r-- | drivers/power/abx500_chargalg.c | 91 |
1 files changed, 52 insertions, 39 deletions
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index e23b92a5332e..55c9d2ea5dff 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2012 | 2 | * Copyright (C) ST-Ericsson SA 2012 |
3 | * Copyright (c) 2012 Sony Mobile Communications AB | ||
3 | * | 4 | * |
4 | * Charging algorithm driver for abx500 variants | 5 | * Charging algorithm driver for abx500 variants |
5 | * | 6 | * |
@@ -8,11 +9,13 @@ | |||
8 | * Johan Palsson <johan.palsson@stericsson.com> | 9 | * Johan Palsson <johan.palsson@stericsson.com> |
9 | * Karl Komierowski <karl.komierowski@stericsson.com> | 10 | * Karl Komierowski <karl.komierowski@stericsson.com> |
10 | * Arun R Murthy <arun.murthy@stericsson.com> | 11 | * Arun R Murthy <arun.murthy@stericsson.com> |
12 | * Author: Imre Sunyi <imre.sunyi@sonymobile.com> | ||
11 | */ | 13 | */ |
12 | 14 | ||
13 | #include <linux/init.h> | 15 | #include <linux/init.h> |
14 | #include <linux/module.h> | 16 | #include <linux/module.h> |
15 | #include <linux/device.h> | 17 | #include <linux/device.h> |
18 | #include <linux/hrtimer.h> | ||
16 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
17 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
18 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
@@ -24,6 +27,7 @@ | |||
24 | #include <linux/of.h> | 27 | #include <linux/of.h> |
25 | #include <linux/mfd/core.h> | 28 | #include <linux/mfd/core.h> |
26 | #include <linux/mfd/abx500.h> | 29 | #include <linux/mfd/abx500.h> |
30 | #include <linux/mfd/abx500/ab8500.h> | ||
27 | #include <linux/mfd/abx500/ux500_chargalg.h> | 31 | #include <linux/mfd/abx500/ux500_chargalg.h> |
28 | #include <linux/mfd/abx500/ab8500-bm.h> | 32 | #include <linux/mfd/abx500/ab8500-bm.h> |
29 | #include <linux/notifier.h> | 33 | #include <linux/notifier.h> |
@@ -34,6 +38,12 @@ | |||
34 | /* End-of-charge criteria counter */ | 38 | /* End-of-charge criteria counter */ |
35 | #define EOC_COND_CNT 10 | 39 | #define EOC_COND_CNT 10 |
36 | 40 | ||
41 | /* One hour expressed in seconds */ | ||
42 | #define ONE_HOUR_IN_SECONDS 3600 | ||
43 | |||
44 | /* Five minutes expressed in seconds */ | ||
45 | #define FIVE_MINUTES_IN_SECONDS 300 | ||
46 | |||
37 | /* Plus margin for the low battery threshold */ | 47 | /* Plus margin for the low battery threshold */ |
38 | #define BAT_PLUS_MARGIN (100) | 48 | #define BAT_PLUS_MARGIN (100) |
39 | 49 | ||
@@ -244,8 +254,8 @@ struct abx500_chargalg { | |||
244 | struct delayed_work chargalg_periodic_work; | 254 | struct delayed_work chargalg_periodic_work; |
245 | struct delayed_work chargalg_wd_work; | 255 | struct delayed_work chargalg_wd_work; |
246 | struct work_struct chargalg_work; | 256 | struct work_struct chargalg_work; |
247 | struct timer_list safety_timer; | 257 | struct hrtimer safety_timer; |
248 | struct timer_list maintenance_timer; | 258 | struct hrtimer maintenance_timer; |
249 | struct kobject chargalg_kobject; | 259 | struct kobject chargalg_kobject; |
250 | }; | 260 | }; |
251 | 261 | ||
@@ -260,38 +270,47 @@ static enum power_supply_property abx500_chargalg_props[] = { | |||
260 | 270 | ||
261 | /** | 271 | /** |
262 | * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer | 272 | * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer |
263 | * @data: pointer to the abx500_chargalg structure | 273 | * @timer: pointer to the hrtimer structure |
264 | * | 274 | * |
265 | * This function gets called when the safety timer for the charger | 275 | * This function gets called when the safety timer for the charger |
266 | * expires | 276 | * expires |
267 | */ | 277 | */ |
268 | static void abx500_chargalg_safety_timer_expired(unsigned long data) | 278 | static enum hrtimer_restart |
279 | abx500_chargalg_safety_timer_expired(struct hrtimer *timer) | ||
269 | { | 280 | { |
270 | struct abx500_chargalg *di = (struct abx500_chargalg *) data; | 281 | struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, |
282 | safety_timer); | ||
271 | dev_err(di->dev, "Safety timer expired\n"); | 283 | dev_err(di->dev, "Safety timer expired\n"); |
272 | di->events.safety_timer_expired = true; | 284 | di->events.safety_timer_expired = true; |
273 | 285 | ||
274 | /* Trigger execution of the algorithm instantly */ | 286 | /* Trigger execution of the algorithm instantly */ |
275 | queue_work(di->chargalg_wq, &di->chargalg_work); | 287 | queue_work(di->chargalg_wq, &di->chargalg_work); |
288 | |||
289 | return HRTIMER_NORESTART; | ||
276 | } | 290 | } |
277 | 291 | ||
278 | /** | 292 | /** |
279 | * abx500_chargalg_maintenance_timer_expired() - Expiration of | 293 | * abx500_chargalg_maintenance_timer_expired() - Expiration of |
280 | * the maintenance timer | 294 | * the maintenance timer |
281 | * @i: pointer to the abx500_chargalg structure | 295 | * @timer: pointer to the timer structure |
282 | * | 296 | * |
283 | * This function gets called when the maintenence timer | 297 | * This function gets called when the maintenence timer |
284 | * expires | 298 | * expires |
285 | */ | 299 | */ |
286 | static void abx500_chargalg_maintenance_timer_expired(unsigned long data) | 300 | static enum hrtimer_restart |
301 | abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer) | ||
287 | { | 302 | { |
288 | 303 | ||
289 | struct abx500_chargalg *di = (struct abx500_chargalg *) data; | 304 | struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, |
305 | maintenance_timer); | ||
306 | |||
290 | dev_dbg(di->dev, "Maintenance timer expired\n"); | 307 | dev_dbg(di->dev, "Maintenance timer expired\n"); |
291 | di->events.maintenance_timer_expired = true; | 308 | di->events.maintenance_timer_expired = true; |
292 | 309 | ||
293 | /* Trigger execution of the algorithm instantly */ | 310 | /* Trigger execution of the algorithm instantly */ |
294 | queue_work(di->chargalg_wq, &di->chargalg_work); | 311 | queue_work(di->chargalg_wq, &di->chargalg_work); |
312 | |||
313 | return HRTIMER_NORESTART; | ||
295 | } | 314 | } |
296 | 315 | ||
297 | /** | 316 | /** |
@@ -391,19 +410,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) | |||
391 | */ | 410 | */ |
392 | static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) | 411 | static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) |
393 | { | 412 | { |
394 | unsigned long timer_expiration = 0; | 413 | /* Charger-dependent expiration time in hours*/ |
414 | int timer_expiration = 0; | ||
395 | 415 | ||
396 | switch (di->chg_info.charger_type) { | 416 | switch (di->chg_info.charger_type) { |
397 | case AC_CHG: | 417 | case AC_CHG: |
398 | timer_expiration = | 418 | timer_expiration = di->bm->main_safety_tmr_h; |
399 | round_jiffies(jiffies + | ||
400 | (di->bm->main_safety_tmr_h * 3600 * HZ)); | ||
401 | break; | 419 | break; |
402 | 420 | ||
403 | case USB_CHG: | 421 | case USB_CHG: |
404 | timer_expiration = | 422 | timer_expiration = di->bm->usb_safety_tmr_h; |
405 | round_jiffies(jiffies + | ||
406 | (di->bm->usb_safety_tmr_h * 3600 * HZ)); | ||
407 | break; | 423 | break; |
408 | 424 | ||
409 | default: | 425 | default: |
@@ -412,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) | |||
412 | } | 428 | } |
413 | 429 | ||
414 | di->events.safety_timer_expired = false; | 430 | di->events.safety_timer_expired = false; |
415 | di->safety_timer.expires = timer_expiration; | 431 | hrtimer_set_expires_range(&di->safety_timer, |
416 | if (!timer_pending(&di->safety_timer)) | 432 | ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0), |
417 | add_timer(&di->safety_timer); | 433 | ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); |
418 | else | 434 | hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL); |
419 | mod_timer(&di->safety_timer, timer_expiration); | ||
420 | } | 435 | } |
421 | 436 | ||
422 | /** | 437 | /** |
@@ -427,8 +442,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) | |||
427 | */ | 442 | */ |
428 | static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) | 443 | static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) |
429 | { | 444 | { |
430 | di->events.safety_timer_expired = false; | 445 | if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) |
431 | del_timer(&di->safety_timer); | 446 | di->events.safety_timer_expired = false; |
432 | } | 447 | } |
433 | 448 | ||
434 | /** | 449 | /** |
@@ -443,17 +458,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) | |||
443 | static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, | 458 | static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, |
444 | int duration) | 459 | int duration) |
445 | { | 460 | { |
446 | unsigned long timer_expiration; | 461 | hrtimer_set_expires_range(&di->maintenance_timer, |
447 | 462 | ktime_set(duration * ONE_HOUR_IN_SECONDS, 0), | |
448 | /* Convert from hours to jiffies */ | 463 | ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); |
449 | timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ)); | ||
450 | |||
451 | di->events.maintenance_timer_expired = false; | 464 | di->events.maintenance_timer_expired = false; |
452 | di->maintenance_timer.expires = timer_expiration; | 465 | hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL); |
453 | if (!timer_pending(&di->maintenance_timer)) | ||
454 | add_timer(&di->maintenance_timer); | ||
455 | else | ||
456 | mod_timer(&di->maintenance_timer, timer_expiration); | ||
457 | } | 466 | } |
458 | 467 | ||
459 | /** | 468 | /** |
@@ -465,8 +474,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, | |||
465 | */ | 474 | */ |
466 | static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di) | 475 | static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di) |
467 | { | 476 | { |
468 | di->events.maintenance_timer_expired = false; | 477 | if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) |
469 | del_timer(&di->maintenance_timer); | 478 | di->events.maintenance_timer_expired = false; |
470 | } | 479 | } |
471 | 480 | ||
472 | /** | 481 | /** |
@@ -1937,10 +1946,16 @@ static int abx500_chargalg_remove(struct platform_device *pdev) | |||
1937 | /* sysfs interface to enable/disbale charging from user space */ | 1946 | /* sysfs interface to enable/disbale charging from user space */ |
1938 | abx500_chargalg_sysfs_exit(di); | 1947 | abx500_chargalg_sysfs_exit(di); |
1939 | 1948 | ||
1949 | hrtimer_cancel(&di->safety_timer); | ||
1950 | hrtimer_cancel(&di->maintenance_timer); | ||
1951 | |||
1952 | cancel_delayed_work_sync(&di->chargalg_periodic_work); | ||
1953 | cancel_delayed_work_sync(&di->chargalg_wd_work); | ||
1954 | cancel_work_sync(&di->chargalg_work); | ||
1955 | |||
1940 | /* Delete the work queue */ | 1956 | /* Delete the work queue */ |
1941 | destroy_workqueue(di->chargalg_wq); | 1957 | destroy_workqueue(di->chargalg_wq); |
1942 | 1958 | ||
1943 | flush_scheduled_work(); | ||
1944 | power_supply_unregister(&di->chargalg_psy); | 1959 | power_supply_unregister(&di->chargalg_psy); |
1945 | platform_set_drvdata(pdev, NULL); | 1960 | platform_set_drvdata(pdev, NULL); |
1946 | 1961 | ||
@@ -1994,15 +2009,13 @@ static int abx500_chargalg_probe(struct platform_device *pdev) | |||
1994 | abx500_chargalg_external_power_changed; | 2009 | abx500_chargalg_external_power_changed; |
1995 | 2010 | ||
1996 | /* Initilialize safety timer */ | 2011 | /* Initilialize safety timer */ |
1997 | init_timer(&di->safety_timer); | 2012 | hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); |
1998 | di->safety_timer.function = abx500_chargalg_safety_timer_expired; | 2013 | di->safety_timer.function = abx500_chargalg_safety_timer_expired; |
1999 | di->safety_timer.data = (unsigned long) di; | ||
2000 | 2014 | ||
2001 | /* Initilialize maintenance timer */ | 2015 | /* Initilialize maintenance timer */ |
2002 | init_timer(&di->maintenance_timer); | 2016 | hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); |
2003 | di->maintenance_timer.function = | 2017 | di->maintenance_timer.function = |
2004 | abx500_chargalg_maintenance_timer_expired; | 2018 | abx500_chargalg_maintenance_timer_expired; |
2005 | di->maintenance_timer.data = (unsigned long) di; | ||
2006 | 2019 | ||
2007 | /* Create a work queue for the chargalg */ | 2020 | /* Create a work queue for the chargalg */ |
2008 | di->chargalg_wq = | 2021 | di->chargalg_wq = |