PM: Make power domain callbacks take precedence over subsystem ones
Change the PM core's behavior related to power domains in such a way that, if a power domain is defined for a given device, its callbacks will be executed instead of and not in addition to the device subsystem's PM callbacks. The idea behind the initial implementation of power domains handling by the PM core was that power domain callbacks would be executed in addition to subsystem callbacks, so that it would be possible to extend the subsystem callbacks by using power domains. It turns out, however, that this wouldn't be really convenient in some important situations. For example, there are systems in which power can only be removed from entire power domains. On those systems it is not desirable to execute device drivers' PM callbacks until it is known that power is going to be removed from the devices in question, which means that they should be executed by power domain callbacks rather then by subsystem (e.g. bus type) PM callbacks, because subsystems generally have no information about what devices belong to which power domain. Thus, for instance, if the bus type in question is the platform bus type, its PM callbacks generally should not be called in addition to power domain callbacks, because they run device drivers' callbacks unconditionally if defined. While in principle the default subsystem PM callbacks, or a subset of them, may be replaced with different functions, it doesn't seem correct to do so, because that would change the subsystem's behavior with respect to all devices in the system, regardless of whether or not they belong to any power domains. Thus, the only remaining option is to make power domain callbacks take precedence over subsystem callbacks. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Grant Likely <grant.likely@secretlab.ca> Acked-by: Kevin Hilman <khilman@ti.com>
This commit is contained in:
parent
fafc9929c6
commit
4d27e9dcff
|
@ -426,10 +426,8 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
|
||||||
|
|
||||||
if (dev->pwr_domain) {
|
if (dev->pwr_domain) {
|
||||||
pm_dev_dbg(dev, state, "EARLY power domain ");
|
pm_dev_dbg(dev, state, "EARLY power domain ");
|
||||||
pm_noirq_op(dev, &dev->pwr_domain->ops, state);
|
error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
|
||||||
}
|
} else if (dev->type && dev->type->pm) {
|
||||||
|
|
||||||
if (dev->type && dev->type->pm) {
|
|
||||||
pm_dev_dbg(dev, state, "EARLY type ");
|
pm_dev_dbg(dev, state, "EARLY type ");
|
||||||
error = pm_noirq_op(dev, dev->type->pm, state);
|
error = pm_noirq_op(dev, dev->type->pm, state);
|
||||||
} else if (dev->class && dev->class->pm) {
|
} else if (dev->class && dev->class->pm) {
|
||||||
|
@ -517,7 +515,8 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
|
||||||
|
|
||||||
if (dev->pwr_domain) {
|
if (dev->pwr_domain) {
|
||||||
pm_dev_dbg(dev, state, "power domain ");
|
pm_dev_dbg(dev, state, "power domain ");
|
||||||
pm_op(dev, &dev->pwr_domain->ops, state);
|
error = pm_op(dev, &dev->pwr_domain->ops, state);
|
||||||
|
goto End;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->type && dev->type->pm) {
|
if (dev->type && dev->type->pm) {
|
||||||
|
@ -629,12 +628,11 @@ static void device_complete(struct device *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
device_lock(dev);
|
device_lock(dev);
|
||||||
|
|
||||||
if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
|
if (dev->pwr_domain) {
|
||||||
pm_dev_dbg(dev, state, "completing power domain ");
|
pm_dev_dbg(dev, state, "completing power domain ");
|
||||||
dev->pwr_domain->ops.complete(dev);
|
if (dev->pwr_domain->ops.complete)
|
||||||
}
|
dev->pwr_domain->ops.complete(dev);
|
||||||
|
} else if (dev->type && dev->type->pm) {
|
||||||
if (dev->type && dev->type->pm) {
|
|
||||||
pm_dev_dbg(dev, state, "completing type ");
|
pm_dev_dbg(dev, state, "completing type ");
|
||||||
if (dev->type->pm->complete)
|
if (dev->type->pm->complete)
|
||||||
dev->type->pm->complete(dev);
|
dev->type->pm->complete(dev);
|
||||||
|
@ -732,7 +730,12 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (dev->type && dev->type->pm) {
|
if (dev->pwr_domain) {
|
||||||
|
pm_dev_dbg(dev, state, "LATE power domain ");
|
||||||
|
error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
} else if (dev->type && dev->type->pm) {
|
||||||
pm_dev_dbg(dev, state, "LATE type ");
|
pm_dev_dbg(dev, state, "LATE type ");
|
||||||
error = pm_noirq_op(dev, dev->type->pm, state);
|
error = pm_noirq_op(dev, dev->type->pm, state);
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -749,11 +752,6 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->pwr_domain) {
|
|
||||||
pm_dev_dbg(dev, state, "LATE power domain ");
|
|
||||||
pm_noirq_op(dev, &dev->pwr_domain->ops, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,21 +839,27 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev->pwr_domain) {
|
||||||
|
pm_dev_dbg(dev, state, "power domain ");
|
||||||
|
error = pm_op(dev, &dev->pwr_domain->ops, state);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
if (dev->type && dev->type->pm) {
|
if (dev->type && dev->type->pm) {
|
||||||
pm_dev_dbg(dev, state, "type ");
|
pm_dev_dbg(dev, state, "type ");
|
||||||
error = pm_op(dev, dev->type->pm, state);
|
error = pm_op(dev, dev->type->pm, state);
|
||||||
goto Domain;
|
goto End;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->class) {
|
if (dev->class) {
|
||||||
if (dev->class->pm) {
|
if (dev->class->pm) {
|
||||||
pm_dev_dbg(dev, state, "class ");
|
pm_dev_dbg(dev, state, "class ");
|
||||||
error = pm_op(dev, dev->class->pm, state);
|
error = pm_op(dev, dev->class->pm, state);
|
||||||
goto Domain;
|
goto End;
|
||||||
} else if (dev->class->suspend) {
|
} else if (dev->class->suspend) {
|
||||||
pm_dev_dbg(dev, state, "legacy class ");
|
pm_dev_dbg(dev, state, "legacy class ");
|
||||||
error = legacy_suspend(dev, state, dev->class->suspend);
|
error = legacy_suspend(dev, state, dev->class->suspend);
|
||||||
goto Domain;
|
goto End;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,12 +873,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Domain:
|
|
||||||
if (!error && dev->pwr_domain) {
|
|
||||||
pm_dev_dbg(dev, state, "power domain ");
|
|
||||||
pm_op(dev, &dev->pwr_domain->ops, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
End:
|
End:
|
||||||
device_unlock(dev);
|
device_unlock(dev);
|
||||||
complete_all(&dev->power.completion);
|
complete_all(&dev->power.completion);
|
||||||
|
@ -965,7 +963,14 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
||||||
|
|
||||||
device_lock(dev);
|
device_lock(dev);
|
||||||
|
|
||||||
if (dev->type && dev->type->pm) {
|
if (dev->pwr_domain) {
|
||||||
|
pm_dev_dbg(dev, state, "preparing power domain ");
|
||||||
|
if (dev->pwr_domain->ops.prepare)
|
||||||
|
error = dev->pwr_domain->ops.prepare(dev);
|
||||||
|
suspend_report_result(dev->pwr_domain->ops.prepare, error);
|
||||||
|
if (error)
|
||||||
|
goto End;
|
||||||
|
} else if (dev->type && dev->type->pm) {
|
||||||
pm_dev_dbg(dev, state, "preparing type ");
|
pm_dev_dbg(dev, state, "preparing type ");
|
||||||
if (dev->type->pm->prepare)
|
if (dev->type->pm->prepare)
|
||||||
error = dev->type->pm->prepare(dev);
|
error = dev->type->pm->prepare(dev);
|
||||||
|
@ -984,13 +989,6 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
||||||
if (dev->bus->pm->prepare)
|
if (dev->bus->pm->prepare)
|
||||||
error = dev->bus->pm->prepare(dev);
|
error = dev->bus->pm->prepare(dev);
|
||||||
suspend_report_result(dev->bus->pm->prepare, error);
|
suspend_report_result(dev->bus->pm->prepare, error);
|
||||||
if (error)
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
|
|
||||||
pm_dev_dbg(dev, state, "preparing power domain ");
|
|
||||||
dev->pwr_domain->ops.prepare(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
End:
|
End:
|
||||||
|
|
|
@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(struct device *dev)
|
||||||
static int rpm_idle(struct device *dev, int rpmflags)
|
static int rpm_idle(struct device *dev, int rpmflags)
|
||||||
{
|
{
|
||||||
int (*callback)(struct device *);
|
int (*callback)(struct device *);
|
||||||
int (*domain_callback)(struct device *);
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = rpm_check_suspend_allowed(dev);
|
retval = rpm_check_suspend_allowed(dev);
|
||||||
|
@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev, int rpmflags)
|
||||||
|
|
||||||
dev->power.idle_notification = true;
|
dev->power.idle_notification = true;
|
||||||
|
|
||||||
if (dev->type && dev->type->pm)
|
if (dev->pwr_domain)
|
||||||
|
callback = dev->pwr_domain->ops.runtime_idle;
|
||||||
|
else if (dev->type && dev->type->pm)
|
||||||
callback = dev->type->pm->runtime_idle;
|
callback = dev->type->pm->runtime_idle;
|
||||||
else if (dev->class && dev->class->pm)
|
else if (dev->class && dev->class->pm)
|
||||||
callback = dev->class->pm->runtime_idle;
|
callback = dev->class->pm->runtime_idle;
|
||||||
|
@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev, int rpmflags)
|
||||||
else
|
else
|
||||||
callback = NULL;
|
callback = NULL;
|
||||||
|
|
||||||
if (dev->pwr_domain)
|
if (callback) {
|
||||||
domain_callback = dev->pwr_domain->ops.runtime_idle;
|
|
||||||
else
|
|
||||||
domain_callback = NULL;
|
|
||||||
|
|
||||||
if (callback || domain_callback) {
|
|
||||||
spin_unlock_irq(&dev->power.lock);
|
spin_unlock_irq(&dev->power.lock);
|
||||||
|
|
||||||
if (domain_callback)
|
callback(dev);
|
||||||
retval = domain_callback(dev);
|
|
||||||
|
|
||||||
if (!retval && callback)
|
|
||||||
callback(dev);
|
|
||||||
|
|
||||||
spin_lock_irq(&dev->power.lock);
|
spin_lock_irq(&dev->power.lock);
|
||||||
}
|
}
|
||||||
|
@ -382,7 +374,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
|
|
||||||
__update_runtime_status(dev, RPM_SUSPENDING);
|
__update_runtime_status(dev, RPM_SUSPENDING);
|
||||||
|
|
||||||
if (dev->type && dev->type->pm)
|
if (dev->pwr_domain)
|
||||||
|
callback = dev->pwr_domain->ops.runtime_suspend;
|
||||||
|
else if (dev->type && dev->type->pm)
|
||||||
callback = dev->type->pm->runtime_suspend;
|
callback = dev->type->pm->runtime_suspend;
|
||||||
else if (dev->class && dev->class->pm)
|
else if (dev->class && dev->class->pm)
|
||||||
callback = dev->class->pm->runtime_suspend;
|
callback = dev->class->pm->runtime_suspend;
|
||||||
|
@ -400,8 +394,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
else
|
else
|
||||||
pm_runtime_cancel_pending(dev);
|
pm_runtime_cancel_pending(dev);
|
||||||
} else {
|
} else {
|
||||||
if (dev->pwr_domain)
|
|
||||||
rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
|
|
||||||
no_callback:
|
no_callback:
|
||||||
__update_runtime_status(dev, RPM_SUSPENDED);
|
__update_runtime_status(dev, RPM_SUSPENDED);
|
||||||
pm_runtime_deactivate_timer(dev);
|
pm_runtime_deactivate_timer(dev);
|
||||||
|
@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||||
__update_runtime_status(dev, RPM_RESUMING);
|
__update_runtime_status(dev, RPM_RESUMING);
|
||||||
|
|
||||||
if (dev->pwr_domain)
|
if (dev->pwr_domain)
|
||||||
rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
|
callback = dev->pwr_domain->ops.runtime_resume;
|
||||||
|
else if (dev->type && dev->type->pm)
|
||||||
if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_resume;
|
callback = dev->type->pm->runtime_resume;
|
||||||
else if (dev->class && dev->class->pm)
|
else if (dev->class && dev->class->pm)
|
||||||
callback = dev->class->pm->runtime_resume;
|
callback = dev->class->pm->runtime_resume;
|
||||||
|
|
Loading…
Reference in New Issue