From patchwork Tue May 17 04:59:06 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: MyungJoo Ham X-Patchwork-Id: 790442 Received: from smtp1.linux-foundation.org (smtp1.linux-foundation.org [140.211.169.13]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p4H51JeH006743 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Tue, 17 May 2011 05:01:39 GMT Received: from daredevil.linux-foundation.org (localhost [127.0.0.1]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4H4xkHM011981; Mon, 16 May 2011 21:59:47 -0700 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4H4xArM011911 for ; Mon, 16 May 2011 21:59:14 -0700 Received: from epcpsbgm1.samsung.com (mailout3.samsung.com [203.254.224.33]) by mailout3.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0LLB00NY3PU52ZG0@mailout3.samsung.com> for linux-pm@lists.linux-foundation.org; Tue, 17 May 2011 13:59:08 +0900 (KST) X-AuditID: cbfee61a-b7ce2ae000001a8f-c0-4dd2009bd834 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (MMPCPMTA) with SMTP id FC.9E.06799.B9002DD4; Tue, 17 May 2011 13:59:08 +0900 (KST) Received: from TNRNDGASPAPP1.tn.corp.samsungelectronics.net ([165.213.149.150]) by mmp2.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0LLB00BEVPUJIN@mmp2.samsung.com> for linux-pm@lists.linux-foundation.org; Tue, 17 May 2011 13:59:08 +0900 (KST) Received: from localhost.localdomain ([165.213.219.116]) by TNRNDGASPAPP1.tn.corp.samsungelectronics.net with Microsoft SMTPSVC(6.0.3790.4675); Tue, 17 May 2011 13:59:07 +0900 Date: Tue, 17 May 2011 13:59:06 +0900 From: MyungJoo Ham In-reply-to: <1305608346-23800-1-git-send-email-myungjoo.ham@samsung.com> To: linux-pm@lists.linux-foundation.org Message-id: <1305608346-23800-2-git-send-email-myungjoo.ham@samsung.com> X-Mailer: git-send-email 1.7.4.1 References: <20110512061928.GB2460@localhost.ucw.cz> <1305608346-23800-1-git-send-email-myungjoo.ham@samsung.com> X-OriginalArrivalTime: 17 May 2011 04:59:07.0594 (UTC) FILETIME=[26FC42A0:01CC144F] X-Brightmail-Tracker: AAAAAA== Received-SPF: pass (localhost is always allowed.) X-Spam-Status: No, hits=-12.153 required=5 tests=AWL, BAYES_00, OSDL_HEADER_SUBJECT_BRACKETED, SAMSUNG_WEBMAIL_OSDL X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.21 Cc: Len Brown , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, kyungmin.park@samsung.com Subject: [linux-pm] [PATCH v4 2/2] PM / Core: partial resume/suspend API for suspend_again users. X-BeenThere: linux-pm@lists.linux-foundation.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux power management List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-pm-bounces@lists.linux-foundation.org Errors-To: linux-pm-bounces@lists.linux-foundation.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 17 May 2011 05:01:39 +0000 (UTC) The API, suspend_again, is supposed to be used by platforms when the platforms want to execute some code while suspended (wakeup by an alarm or periodic ticks, run code, suspend again). Because suspend_again is not called when every device is resumed, but is called right after suspend_enter() is called, the suspend_again callback should include device resume and suspend features. This patch provides two APIs: dpm_partial_resume and dpm_partial_suspend. They are supposed to be used in suspend_again to actiavte required devices. A usage example is: /* Devices required to run "monitor_something()". */ static device *devs[] = { &console_serial, &device_x, &device_y, ... }; bool example_suspend_again(void) { int error, monitor_result; if (!wakeup_reason_is_polling()) return false; dpm_partial_resume(PMSG_RESUME, devs, ARRAY_SIZE(devs)); resume_console(); monitor_result = monitor_something(); suspend_console(); error = dpm_partial_suspend(PMSG_SUSPEND, devs, ARRAY_SIZE(devs)); if (error || monitor_result == ERROR) return false; return true; } Tested at Exynos4-NURI. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park --- No changes from v3. --- drivers/base/power/main.c | 154 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 4 + 2 files changed, 158 insertions(+), 0 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 052dc53..71dc693 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1082,3 +1082,157 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + +/** + * __dpm_partial_resume - Execute "resume" callbacks for the listed devices. + * @state: PM transition being carried out. + * @devs: Array of pointers for the devices being resumed + * @size: The size of devs array. + */ +static void __dpm_partial_resume(pm_message_t state, struct device **devs, + int size) +{ + int i; + struct device *dev; + + for (i = 0; i < size; i++) { + int error; + + dev = devs[i]; + get_device(dev); + + error = device_resume(dev, state, false); + if (error) + pm_dev_err(dev, state, "", error); + + put_device(dev); + } +} + +/** + * __dpm_partial_complete - Complete a PM transition for the listed devices. + * @state: PM transition being carried out. + * @devs: Array of pointers for the devices being resumed + * @size: The size of devs array. + */ +static void __dpm_partial_complete(pm_message_t state, struct device **devs, + int size) +{ + int i; + struct device *dev; + + for (i = 0; i < size; i++) { + dev = devs[i]; + + get_device(dev); + dev->power.in_suspend = false; + + device_complete(dev, state); + + put_device(dev); + } +} + +/** + * dpm_partial_resume - Execute "resume" callbacks and complete system + * transaction for the chosen devices only. + * @state: PM transition being carried out. + * @devs: Array of pointers for the devices being resumed + * @size: The size of devs array. + * + * Execute "resume" callbacks for the listed devices and complete the PM + * transition of them. + * + * Because the only a part of devices will be resumed, asynchronous resume + * is dropped. + */ +void dpm_partial_resume(pm_message_t state, struct device **devs, int size) +{ + /* Partial dpm_resume */ + __dpm_partial_resume(state, devs, size); + + /* Partial dpm_complete */ + __dpm_partial_complete(state, devs, size); +} +EXPORT_SYMBOL_GPL(dpm_partial_resume); + +/** + * dpm_partial_suspend - Prepare the given devices for PM transition and + * suspend them. + * @state: PM transition being carried out. + * @devs: Array of pointers for the devices being suspended + * @size: The size of devs array. + * + * Prepare the given devices for system PM transition and execute "suspend" + * callbacks for them. + * + * The devs array is iterated in the reversed order to use the same devs + * array with dpm_partial_resume(). + */ +int dpm_partial_suspend(pm_message_t state, struct device **devs, int size) +{ + int error = 0, i; + struct device *dev; + + /* Partial dpm_prepare */ + for (i = size - 1; i >= 0; i--) { + dev = devs[i]; + + get_device(dev); + + pm_runtime_get_noresume(dev); + if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) + pm_wakeup_event(dev, 0); + + pm_runtime_put_sync(dev); + error = pm_wakeup_pending() ? + -EBUSY : device_prepare(dev, state); + + if (error) { + if (error == -EAGAIN) { + put_device(dev); + error = 0; + i++; /* Try Again */ + continue; + } + printk(KERN_INFO "PM: Device %s not prepared " + "for power transition: code %d\n", + dev_name(dev), error); + put_device(dev); + break; + } + dev->power.in_suspend = true; + put_device(dev); + } + + if (error) + goto err_prepare; + + /* Partial dpm_suspend */ + for (i = size - 1; i >= 0; i--) { + dev = devs[i]; + + get_device(dev); + + /* Synchronous suspend. The list shouldn't be long */ + error = __device_suspend(dev, pm_transition, false); + + if (error) { + pm_dev_err(dev, state, "", error); + put_device(dev); + break; + } + put_device(dev); + } + + if (!error) + return 0; + + __dpm_partial_resume(PMSG_RESUME, devs + i + 1, size - i - 1); + i = -1; +err_prepare: + __dpm_partial_complete(PMSG_RESUME, devs + i + 1, size - i - 1); + + return error; +} +EXPORT_SYMBOL_GPL(dpm_partial_suspend); diff --git a/include/linux/pm.h b/include/linux/pm.h index 512e091..b407762 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -540,10 +540,14 @@ static inline int sysdev_resume(void) { return 0; } extern void device_pm_lock(void); extern void dpm_resume_noirq(pm_message_t state); extern void dpm_resume_end(pm_message_t state); +extern void dpm_partial_resume(pm_message_t state, struct device **devs, + int size); extern void device_pm_unlock(void); extern int dpm_suspend_noirq(pm_message_t state); extern int dpm_suspend_start(pm_message_t state); +extern int dpm_partial_suspend(pm_message_t state, struct device **devs, + int size); extern void __suspend_report_result(const char *function, void *fn, int ret);