@@ -11,10 +11,18 @@
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
+#include <linux/decompress/mm.h>
#include <trace/events/rpm.h>
#include "power.h"
+struct rpm_suspend_retval;
typedef int (*pm_callback_t)(struct device *);
+typedef void (*_rpm_suspend_func)(struct device *, int, \
+ struct rpm_suspend_retval *);
+typedef struct rpm_suspend_retval{
+ int retval;
+ void (*pfun)(struct device *, int, struct rpm_suspend_retval *);
+} rpm_susp_rv;
static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
{
@@ -49,6 +57,17 @@ static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
static int rpm_resume(struct device *dev, int rpmflags);
static int rpm_suspend(struct device *dev, int rpmflags);
+static void _rpm_suspend_auto(struct device *dev, int rpmflags, \
+ struct rpm_suspend_retval *prv);
+static void _rpm_suspend_wait(struct device *dev, int rpmflags, \
+ struct rpm_suspend_retval *prv);
+static void _rpm_suspend_call(struct device *dev, int rpmflags, \
+ struct rpm_suspend_retval *prv);
+static void _rpm_suspend_success(struct device *dev, int rpmflags, \
+ struct rpm_suspend_retval *prv);
+static void _rpm_suspend_fail(struct device *dev, int rpmflags, \
+ struct rpm_suspend_retval *prv);
+
/**
* update_pm_runtime_accounting - Update the time accounting of power states
* @dev: Device to update the accounting for
@@ -415,13 +434,15 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
static int rpm_suspend(struct device *dev, int rpmflags)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
- int (*callback)(struct device *);
- struct device *parent = NULL;
+ rpm_susp_rv *prv = (rpm_susp_rv *)malloc(sizeof(struct rpm_suspend_retval));
+ _rpm_suspend_func pfun;
int retval;
+ if (NULL == prv)
+ return -ENOMEM;
+
trace_rpm_suspend(dev, rpmflags);
- repeat:
retval = rpm_check_suspend_allowed(dev);
if (retval < 0)
@@ -429,14 +450,35 @@ static int rpm_suspend(struct device *dev, int rpmflags)
/* Synchronous suspends are not allowed in the RPM_RESUMING state. */
else if (dev->power.runtime_status == RPM_RESUMING &&
- !(rpmflags & RPM_ASYNC))
+ !(rpmflags & RPM_ASYNC))
retval = -EAGAIN;
+
if (retval)
- goto out;
+ ;
+ else{
+ prv->retval = 0;
+ /*start the process from auto*/
+ pfun = _rpm_suspend_auto;
+ while (pfun) {
+ pfun(dev, rpmflags, prv);
+ pfun = prv->pfun;
+ }
+ retval = prv->retval;
+ }
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
+
+ free(prv);
+ return retval;
+}
+
+static void _rpm_suspend_auto(struct device *dev, int rpmflags,
+ struct rpm_suspend_retval *prv)
+{
+ prv->pfun = _rpm_suspend_wait;
/* If the autosuspend_delay time hasn't expired yet, reschedule. */
if ((rpmflags & RPM_AUTO)
- && dev->power.runtime_status != RPM_SUSPENDING) {
+ && dev->power.runtime_status != RPM_SUSPENDING) {
unsigned long expires = pm_runtime_autosuspend_expiration(dev);
if (expires != 0) {
@@ -444,21 +486,29 @@ static int rpm_suspend(struct device *dev, int rpmflags)
dev->power.request = RPM_REQ_NONE;
/*
- * Optimization: If the timer is already running and is
- * set to expire at or before the autosuspend delay,
- * avoid the overhead of resetting it. Just let it
- * expire; pm_suspend_timer_fn() will take care of the
- * rest.
- */
+ * Optimization: If the timer is already running and is
+ * set to expire at or before the autosuspend delay,
+ * avoid the overhead of resetting it. Just let it
+ * expire; pm_suspend_timer_fn() will take care of the
+ * rest.
+ */
if (!(dev->power.timer_expires && time_before_eq(
- dev->power.timer_expires, expires))) {
+ dev->power.timer_expires, expires))) {
dev->power.timer_expires = expires;
mod_timer(&dev->power.suspend_timer, expires);
}
dev->power.timer_autosuspends = 1;
- goto out;
+ prv->pfun = NULL;
}
}
+}
+
+static void _rpm_suspend_wait(struct device *dev, int rpmflags,
+ struct rpm_suspend_retval *prv)
+{
+ unsigned long expires = 0;
+
+ prv->pfun = _rpm_suspend_call;
/* Other scheduled or pending requests need to be canceled. */
pm_runtime_cancel_pending(dev);
@@ -467,24 +517,31 @@ static int rpm_suspend(struct device *dev, int rpmflags)
DEFINE_WAIT(wait);
if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {
- retval = -EINPROGRESS;
- goto out;
+ prv->retval = -EINPROGRESS;
+ prv->pfun = NULL;
+ return;
}
if (dev->power.irq_safe) {
- spin_unlock(&dev->power.lock);
+ while (dev->power.runtime_status ==
+ RPM_SUSPENDING) {
+ spin_unlock(&dev->power.lock);
- cpu_relax();
+ cpu_relax();
- spin_lock(&dev->power.lock);
- goto repeat;
+ spin_lock(&dev->power.lock);
+ continue;
+ }
}
- /* Wait for the other suspend running in parallel with us. */
+ /*Wait for the other suspend
+ *running in parallel with us
+ */
for (;;) {
- prepare_to_wait(&dev->power.wait_queue, &wait,
- TASK_UNINTERRUPTIBLE);
- if (dev->power.runtime_status != RPM_SUSPENDING)
+ prepare_to_wait(&dev->power.wait_queue,
+ &wait, TASK_UNINTERRUPTIBLE);
+ if (dev->power.runtime_status
+ != RPM_SUSPENDING)
break;
spin_unlock_irq(&dev->power.lock);
@@ -494,35 +551,60 @@ static int rpm_suspend(struct device *dev, int rpmflags)
spin_lock_irq(&dev->power.lock);
}
finish_wait(&dev->power.wait_queue, &wait);
- goto repeat;
+
+ /*check expires firstly for auto suspend mode,
+ *if not, just go ahead to the async
+ */
+ expires = pm_runtime_autosuspend_expiration(dev);
+ if (expires != 0)
+ prv->pfun = _rpm_suspend_auto;
}
+}
- if (dev->power.no_callbacks)
- goto no_callback; /* Assume success. */
+/*call the function async or sync by the callback*/
+static void _rpm_suspend_call(struct device *dev, int rpmflags,
+ struct rpm_suspend_retval *prv)
+{
+ pm_callback_t callback;
+
+ prv->pfun = _rpm_suspend_success;
+
+ /*if there is no callbacks, no meaning to place a work into workqueue,
+ *go ahead to check the deferd resume and if the parent can suspend
+ */
+ if (!dev->power.no_callbacks) {
+ /* Carry out an asynchronous or a synchronous suspend. */
+ if (rpmflags & RPM_ASYNC) {
+ dev->power.request = (rpmflags & RPM_AUTO) ?
+ RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;
+ if (!dev->power.request_pending) {
+ dev->power.request_pending = true;
+ queue_work(pm_wq, &dev->power.work);
+ }
+ prv->pfun = NULL;
+ } else {
+ callback = RPM_GET_CALLBACK(dev, runtime_suspend);
- /* Carry out an asynchronous or a synchronous suspend. */
- if (rpmflags & RPM_ASYNC) {
- dev->power.request = (rpmflags & RPM_AUTO) ?
- RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;
- if (!dev->power.request_pending) {
- dev->power.request_pending = true;
- queue_work(pm_wq, &dev->power.work);
- }
- goto out;
- }
+ __update_runtime_status(dev, RPM_SUSPENDING);
- __update_runtime_status(dev, RPM_SUSPENDING);
+ dev_pm_enable_wake_irq(dev);
- callback = RPM_GET_CALLBACK(dev, runtime_suspend);
+ prv->retval = rpm_callback(callback, dev);
- dev_pm_enable_wake_irq(dev);
- retval = rpm_callback(callback, dev);
- if (retval)
- goto fail;
+ if (prv->retval)
+ prv->pfun = _rpm_suspend_fail;
+ }
+ }
+}
+
+static void _rpm_suspend_success(struct device *dev, int rpmflags,
+ struct rpm_suspend_retval *prv)
+{
+ struct device *parent;
- no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
+ prv->pfun = NULL;
if (dev->parent) {
parent = dev->parent;
@@ -533,8 +615,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
if (dev->power.deferred_resume) {
dev->power.deferred_resume = false;
rpm_resume(dev, 0);
- retval = -EAGAIN;
- goto out;
+ prv->retval = -EAGAIN;
}
/* Maybe the parent is now able to suspend. */
@@ -547,34 +628,32 @@ static int rpm_suspend(struct device *dev, int rpmflags)
spin_lock(&dev->power.lock);
}
+}
- out:
- trace_rpm_return_int(dev, _THIS_IP_, retval);
-
- return retval;
-
- fail:
+static void _rpm_suspend_fail(struct device *dev, int rpmflags,
+ struct rpm_suspend_retval *prv)
+{
dev_pm_disable_wake_irq(dev);
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
- if (retval == -EAGAIN || retval == -EBUSY) {
+ if (prv->retval == -EAGAIN || prv->retval == -EBUSY) {
dev->power.runtime_error = 0;
/*
- * If the callback routine failed an autosuspend, and
- * if the last_busy time has been updated so that there
- * is a new autosuspend expiration time, automatically
- * reschedule another autosuspend.
- */
+ * If the callback routine failed an autosuspend, and
+ * if the last_busy time has been updated so that there
+ * is a new autosuspend expiration time, automatically
+ * reschedule another autosuspend.
+ */
if ((rpmflags & RPM_AUTO) &&
- pm_runtime_autosuspend_expiration(dev) != 0)
- goto repeat;
+ pm_runtime_autosuspend_expiration(dev) != 0)
+ prv->pfun = _rpm_suspend_auto;
} else {
pm_runtime_cancel_pending(dev);
+ prv->pfun = NULL;
}
- goto out;
}
/**
There are 11 'goto' and 4 'label' within he original rpm_suspend funciton, which make the code a little bit hard for understanding and debugging.Just try to remove all 'goto' and 'label', and split the function into 6 small ones which take one phase of the process each(_rpm_suspend_xxx). By removing the 'goto' and 'label', rpm_suspend changes to quite simple, where there is just one function call inside a while loop like bellowing. The pfun here works as a current pointer which point to the on duty function of active phase so far. ... while (pfun) { pfun(dev, rpmflags, prv); pfun = prv->pfun; } ... The original rpm_suspend function is divied into bellowing functions, which will transfer to corresponding ones like a state machine. _rpm_suspend_auto _rpm_suspend_wait _rpm_suspend_call _rpm_suspend_success _rpm_suspend_fail The switching path of the state machine like process is as bellowing, _rpm_suspend_auto <-- <------- | | | | | | V | | _rpm_suspend_wait----- | | | | | V | _rpm_suspend_call---->_rpm_suspend_fail | | | | V V _rpm_suspend_success------->END Signed-off-by: Zhaoyang Huang <zhaoyang.huang@spreadtrum.com> --- drivers/base/power/runtime.c | 201 +++++++++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 61 deletions(-) -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html