From patchwork Thu Sep 27 20:09:09 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Poirier X-Patchwork-Id: 11825 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 17C8E24140 for ; Thu, 27 Sep 2012 20:10:32 +0000 (UTC) Received: from mail-ie0-f180.google.com (mail-ie0-f180.google.com [209.85.223.180]) by fiordland.canonical.com (Postfix) with ESMTP id C3038A18351 for ; Thu, 27 Sep 2012 20:10:31 +0000 (UTC) Received: by mail-ie0-f180.google.com with SMTP id e10so5234801iej.11 for ; Thu, 27 Sep 2012 13:10:31 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:from:to :subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=kvi1W+NRmU1h/zX1WSQDgemoXD+ciK0zEaOUNq+L9NQ=; b=CVkbK8tWs1zovOFxUdaHY4c4lcQPfNf9SOT7ki1JmPcbuZ5d8McTNfsNSMUXnRg7PR RWs/5RynIDsep9FIxvpPQze4HMhfNwXgxxk83BQ5dPYHe1p1xmo7HtZYc4NYbFuwxiYS sirsR/qHNlkBb+6RFkBhrQW6F4SA3bqgXrRTWcc/Qw0i0CUolcla/FREtcL/IUtl5qtH kSkEyowI16TuW4cgtvXvptyoGrOvTgoyEXcCfeW6cvE+/DtjoOhyjxlxQECL9m48/S4O qVPVGDzM5xAtk9onMeOAzxw6/ncSD+31WhkMyiIDUtbky6NGbc4B0NHSUAL70nq+FvrD xkQA== Received: by 10.50.150.198 with SMTP id uk6mr4651986igb.43.1348776631445; Thu, 27 Sep 2012 13:10:31 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.184.232 with SMTP id ex8csp436405igc; Thu, 27 Sep 2012 13:10:30 -0700 (PDT) Received: by 10.68.242.97 with SMTP id wp1mr14463181pbc.159.1348776630786; Thu, 27 Sep 2012 13:10:30 -0700 (PDT) Received: from mail-pa0-f50.google.com (mail-pa0-f50.google.com [209.85.220.50]) by mx.google.com with ESMTPS id vn9si8390651pbc.17.2012.09.27.13.10.30 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:10:30 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) client-ip=209.85.220.50; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.50 is neither permitted nor denied by best guess record for domain of mathieu.poirier@linaro.org) smtp.mail=mathieu.poirier@linaro.org Received: by mail-pa0-f50.google.com with SMTP id hz11so1847989pad.37 for ; Thu, 27 Sep 2012 13:10:30 -0700 (PDT) Received: by 10.66.74.40 with SMTP id q8mr12155991pav.29.1348776630498; Thu, 27 Sep 2012 13:10:30 -0700 (PDT) Received: from localhost.localdomain (S0106002369de4dac.cg.shawcable.net. [70.73.24.112]) by mx.google.com with ESMTPS id sa2sm1587890pbc.4.2012.09.27.13.10.29 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 27 Sep 2012 13:10:29 -0700 (PDT) From: mathieu.poirier@linaro.org To: patches@linaro.org Subject: [PATCH 52/57] power: abx500_chargalg: Use hrtimer Date: Thu, 27 Sep 2012 14:09:09 -0600 Message-Id: <1348776554-10019-53-git-send-email-mathieu.poirier@linaro.org> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> References: <1348776554-10019-1-git-send-email-mathieu.poirier@linaro.org> X-Gm-Message-State: ALoCoQm/7pAhCDyV8N8+QQyv1/2mKnJ2L7xIuX/J9k/OXJj5r9XSlAwWtvSkzUYkVQeNpxy69y97 From: Hakan Berg Timers used for charging safety and maintenance must work even when CPU is power collapsed. By using hrtimers with realtime clock, system is able to trigger an alarm that wakes the CPU up and make it possible to handle the event. Allow a little slack of 5 minutes to the hrtimers to allow CPU to be waked up in a more optimal power saving way. A 5 minute delay to time out timers on hours does not impact on safety. Signed-off-by: Hakan Berg Signed-off-by: Mathieu Poirier Reviewed-by: Mian Yousaf KAUKAB --- drivers/power/abx500_chargalg.c | 94 ++++++++++++++++++++++----------------- 1 files changed, 53 insertions(+), 41 deletions(-) diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 636d970..c8849af 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -1,5 +1,6 @@ /* * Copyright (C) ST-Ericsson SA 2012 + * Copyright (c) 2012 Sony Mobile Communications AB * * Charging algorithm driver for abx500 variants * @@ -8,11 +9,13 @@ * Johan Palsson * Karl Komierowski * Arun R Murthy + * Imre Sunyi */ #include #include #include +#include #include #include #include @@ -32,6 +35,12 @@ /* End-of-charge criteria counter */ #define EOC_COND_CNT 10 +/* One hour expressed in seconds */ +#define ONE_HOUR_IN_SECONDS 3600 + +/* Five minutes expressed in seconds */ +#define FIVE_MINUTES_IN_SECONDS 300 + #define to_abx500_chargalg_device_info(x) container_of((x), \ struct abx500_chargalg, chargalg_psy); @@ -245,8 +254,8 @@ struct abx500_chargalg { struct delayed_work chargalg_periodic_work; struct delayed_work chargalg_wd_work; struct work_struct chargalg_work; - struct timer_list safety_timer; - struct timer_list maintenance_timer; + struct hrtimer safety_timer; + struct hrtimer maintenance_timer; struct kobject chargalg_kobject; }; @@ -261,38 +270,47 @@ BLOCKING_NOTIFIER_HEAD(charger_notifier_list); /** * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer - * @data: pointer to the abx500_chargalg structure + * @timer: pointer to the hrtimer structure * * This function gets called when the safety timer for the charger * expires */ -static void abx500_chargalg_safety_timer_expired(unsigned long data) +static enum hrtimer_restart +abx500_chargalg_safety_timer_expired(struct hrtimer *timer) { - struct abx500_chargalg *di = (struct abx500_chargalg *) data; + struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, + safety_timer); dev_err(di->dev, "Safety timer expired\n"); di->events.safety_timer_expired = true; /* Trigger execution of the algorithm instantly */ queue_work(di->chargalg_wq, &di->chargalg_work); + + return HRTIMER_NORESTART; } /** * abx500_chargalg_maintenance_timer_expired() - Expiration of * the maintenance timer - * @i: pointer to the abx500_chargalg structure + * @timer: pointer to the timer structure * * This function gets called when the maintenence timer * expires */ -static void abx500_chargalg_maintenance_timer_expired(unsigned long data) +static enum hrtimer_restart +abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer) + { - struct abx500_chargalg *di = (struct abx500_chargalg *) data; + struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, + maintenance_timer); dev_dbg(di->dev, "Maintenance timer expired\n"); di->events.maintenance_timer_expired = true; /* Trigger execution of the algorithm instantly */ queue_work(di->chargalg_wq, &di->chargalg_work); + + return HRTIMER_NORESTART; } /** @@ -392,19 +410,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) */ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) { - unsigned long timer_expiration = 0; + /* Charger-dependent expiration time in hours*/ + int timer_expiration = 0; switch (di->chg_info.charger_type) { case AC_CHG: - timer_expiration = - round_jiffies(jiffies + - (di->bat->main_safety_tmr_h * 3600 * HZ)); + timer_expiration = di->bat->main_safety_tmr_h; break; case USB_CHG: - timer_expiration = - round_jiffies(jiffies + - (di->bat->usb_safety_tmr_h * 3600 * HZ)); + timer_expiration = di->bat->usb_safety_tmr_h; break; default: @@ -413,11 +428,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) } di->events.safety_timer_expired = false; - di->safety_timer.expires = timer_expiration; - if (!timer_pending(&di->safety_timer)) - add_timer(&di->safety_timer); - else - mod_timer(&di->safety_timer, timer_expiration); + hrtimer_set_expires_range(&di->safety_timer, + ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0), + ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); + hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL); } /** @@ -428,8 +442,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) */ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) { - di->events.safety_timer_expired = false; - del_timer(&di->safety_timer); + if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) + di->events.safety_timer_expired = false; } /** @@ -444,17 +458,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, int duration) { - unsigned long timer_expiration; - - /* Convert from hours to jiffies */ - timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ)); - + hrtimer_set_expires_range(&di->maintenance_timer, + ktime_set(duration * ONE_HOUR_IN_SECONDS, 0), + ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); di->events.maintenance_timer_expired = false; - di->maintenance_timer.expires = timer_expiration; - if (!timer_pending(&di->maintenance_timer)) - add_timer(&di->maintenance_timer); - else - mod_timer(&di->maintenance_timer, timer_expiration); + hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL); } /** @@ -466,8 +474,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, */ static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di) { - di->events.maintenance_timer_expired = false; - del_timer(&di->maintenance_timer); + if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) + di->events.maintenance_timer_expired = false; } /** @@ -910,11 +918,11 @@ static void abx500_chargalg_check_safety_timer(struct abx500_chargalg *di) * and charging will be stopped to protect the battery. */ if (di->batt_data.percent == 100 && - !timer_pending(&di->safety_timer)) { + !hrtimer_active(&di->safety_timer)) { abx500_chargalg_start_safety_timer(di); dev_dbg(di->dev, "start safety timer\n"); } else if (di->batt_data.percent != 100 && - timer_pending(&di->safety_timer)) { + hrtimer_active(&di->safety_timer)) { abx500_chargalg_stop_safety_timer(di); dev_dbg(di->dev, "stop safety timer\n"); } @@ -1932,10 +1940,16 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev) /* sysfs interface to enable/disbale charging from user space */ abx500_chargalg_sysfs_exit(di); + hrtimer_cancel(&di->safety_timer); + hrtimer_cancel(&di->maintenance_timer); + + cancel_delayed_work_sync(&di->chargalg_periodic_work); + cancel_delayed_work_sync(&di->chargalg_wd_work); + cancel_work_sync(&di->chargalg_work); + /* Delete the work queue */ destroy_workqueue(di->chargalg_wq); - flush_scheduled_work(); power_supply_unregister(&di->chargalg_psy); platform_set_drvdata(pdev, NULL); kfree(di); @@ -1987,15 +2001,13 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) abx500_chargalg_external_power_changed; /* Initilialize safety timer */ - init_timer(&di->safety_timer); + hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); di->safety_timer.function = abx500_chargalg_safety_timer_expired; - di->safety_timer.data = (unsigned long) di; /* Initilialize maintenance timer */ - init_timer(&di->maintenance_timer); + hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); di->maintenance_timer.function = abx500_chargalg_maintenance_timer_expired; - di->maintenance_timer.data = (unsigned long) di; /* Create a work queue for the chargalg */ di->chargalg_wq =