Commit e6d18093 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-domains'

* pm-domains:
  PM / Domains: Fix computation of maximum domain off time
  PM / Domains: Fix link checking when add subdomain
  PM / Domains: Cache device stop and domain power off governor results, v3
  PM / Domains: Make device removal more straightforward
  PM / QoS: Create device constraints objects on notifier registration
  PM / Runtime: Remove device fields related to suspend time, v2
  PM / Domains: Rework default domain power off governor function, v2
  PM / Domains: Rework default device stop governor function, v2
parents 3a9da04a b723b0eb
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_qos.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -38,11 +39,13 @@ ...@@ -38,11 +39,13 @@
ktime_t __start = ktime_get(); \ ktime_t __start = ktime_get(); \
type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \
s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \
struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \ struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \
if (__elapsed > __gpd_data->td.field) { \ if (!__retval && __elapsed > __td->field) { \
__gpd_data->td.field = __elapsed; \ __td->field = __elapsed; \
dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ dev_warn(dev, name " latency exceeded, new value %lld ns\n", \
__elapsed); \ __elapsed); \
genpd->max_off_time_changed = true; \
__td->constraint_changed = true; \
} \ } \
__retval; \ __retval; \
}) })
...@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) ...@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > genpd->power_on_latency_ns) { if (elapsed_ns > genpd->power_on_latency_ns) {
genpd->power_on_latency_ns = elapsed_ns; genpd->power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
if (genpd->name) if (genpd->name)
pr_warning("%s: Power-on latency exceeded, " pr_warning("%s: Power-on latency exceeded, "
"new value %lld ns\n", genpd->name, "new value %lld ns\n", genpd->name,
...@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) ...@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
unsigned long val, void *ptr)
{
struct generic_pm_domain_data *gpd_data;
struct device *dev;
gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
mutex_lock(&gpd_data->lock);
dev = gpd_data->base.dev;
if (!dev) {
mutex_unlock(&gpd_data->lock);
return NOTIFY_DONE;
}
mutex_unlock(&gpd_data->lock);
for (;;) {
struct generic_pm_domain *genpd;
struct pm_domain_data *pdd;
spin_lock_irq(&dev->power.lock);
pdd = dev->power.subsys_data ?
dev->power.subsys_data->domain_data : NULL;
if (pdd) {
to_gpd_data(pdd)->td.constraint_changed = true;
genpd = dev_to_genpd(dev);
} else {
genpd = ERR_PTR(-ENODATA);
}
spin_unlock_irq(&dev->power.lock);
if (!IS_ERR(genpd)) {
mutex_lock(&genpd->lock);
genpd->max_off_time_changed = true;
mutex_unlock(&genpd->lock);
}
dev = dev->parent;
if (!dev || dev->power.ignore_children)
break;
}
return NOTIFY_DONE;
}
/** /**
* __pm_genpd_save_device - Save the pre-suspend state of a device. * __pm_genpd_save_device - Save the pre-suspend state of a device.
* @pdd: Domain data of the device to save the state of. * @pdd: Domain data of the device to save the state of.
...@@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) ...@@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > genpd->power_off_latency_ns) { if (elapsed_ns > genpd->power_off_latency_ns) {
genpd->power_off_latency_ns = elapsed_ns; genpd->power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
if (genpd->name) if (genpd->name)
pr_warning("%s: Power-off latency exceeded, " pr_warning("%s: Power-off latency exceeded, "
"new value %lld ns\n", genpd->name, "new value %lld ns\n", genpd->name,
...@@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) ...@@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
} }
genpd->status = GPD_STATE_POWER_OFF; genpd->status = GPD_STATE_POWER_OFF;
genpd->power_off_time = ktime_get();
/* Update PM QoS information for devices in the domain. */
list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
struct gpd_timing_data *td = &to_gpd_data(pdd)->td;
pm_runtime_update_max_time_suspended(pdd->dev,
td->start_latency_ns +
td->restore_state_latency_ns +
genpd->power_on_latency_ns);
}
list_for_each_entry(link, &genpd->slave_links, slave_node) { list_for_each_entry(link, &genpd->slave_links, slave_node) {
genpd_sd_counter_dec(link->master); genpd_sd_counter_dec(link->master);
...@@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (ret) if (ret)
return ret; return ret;
pm_runtime_update_max_time_suspended(dev,
dev_gpd_data(dev)->td.start_latency_ns);
/* /*
* If power.irq_safe is set, this routine will be run with interrupts * If power.irq_safe is set, this routine will be run with interrupts
* off, so it can't use mutexes. * off, so it can't use mutexes.
...@@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void) ...@@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void)
#else #else
static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
unsigned long val, void *ptr)
{
return NOTIFY_DONE;
}
static inline void genpd_power_off_work_fn(struct work_struct *work) {} static inline void genpd_power_off_work_fn(struct work_struct *work) {}
#define pm_genpd_runtime_suspend NULL #define pm_genpd_runtime_suspend NULL
...@@ -1209,6 +1253,14 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1209,6 +1253,14 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
return -EINVAL; return -EINVAL;
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
if (!gpd_data)
return -ENOMEM;
mutex_init(&gpd_data->lock);
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
genpd_acquire_lock(genpd); genpd_acquire_lock(genpd);
if (genpd->status == GPD_STATE_POWER_OFF) { if (genpd->status == GPD_STATE_POWER_OFF) {
...@@ -1227,26 +1279,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, ...@@ -1227,26 +1279,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
goto out; goto out;
} }
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
if (!gpd_data) {
ret = -ENOMEM;
goto out;
}
genpd->device_count++; genpd->device_count++;
genpd->max_off_time_changed = true;
dev->pm_domain = &genpd->domain;
dev_pm_get_subsys_data(dev); dev_pm_get_subsys_data(dev);
mutex_lock(&gpd_data->lock);
spin_lock_irq(&dev->power.lock);
dev->pm_domain = &genpd->domain;
dev->power.subsys_data->domain_data = &gpd_data->base; dev->power.subsys_data->domain_data = &gpd_data->base;
gpd_data->base.dev = dev; gpd_data->base.dev = dev;
gpd_data->need_restore = false;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
gpd_data->need_restore = false;
if (td) if (td)
gpd_data->td = *td; gpd_data->td = *td;
gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = -1;
spin_unlock_irq(&dev->power.lock);
mutex_unlock(&gpd_data->lock);
genpd_release_lock(genpd);
return 0;
out: out:
genpd_release_lock(genpd); genpd_release_lock(genpd);
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data);
return ret; return ret;
} }
...@@ -1290,12 +1351,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, ...@@ -1290,12 +1351,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
int pm_genpd_remove_device(struct generic_pm_domain *genpd, int pm_genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev) struct device *dev)
{ {
struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd; struct pm_domain_data *pdd;
int ret = -EINVAL; int ret = 0;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
|| IS_ERR_OR_NULL(dev->pm_domain)
|| pd_to_genpd(dev->pm_domain) != genpd)
return -EINVAL; return -EINVAL;
genpd_acquire_lock(genpd); genpd_acquire_lock(genpd);
...@@ -1305,21 +1369,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, ...@@ -1305,21 +1369,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
goto out; goto out;
} }
list_for_each_entry(pdd, &genpd->dev_list, list_node) { genpd->device_count--;
if (pdd->dev != dev) genpd->max_off_time_changed = true;
continue;
spin_lock_irq(&dev->power.lock);
dev->pm_domain = NULL;
pdd = dev->power.subsys_data->domain_data;
list_del_init(&pdd->list_node); list_del_init(&pdd->list_node);
dev->power.subsys_data->domain_data = NULL;
spin_unlock_irq(&dev->power.lock);
gpd_data = to_gpd_data(pdd);
mutex_lock(&gpd_data->lock);
pdd->dev = NULL; pdd->dev = NULL;
dev_pm_put_subsys_data(dev); mutex_unlock(&gpd_data->lock);
dev->pm_domain = NULL;
kfree(to_gpd_data(pdd));
genpd->device_count--; genpd_release_lock(genpd);
ret = 0; dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
break; kfree(gpd_data);
} dev_pm_put_subsys_data(dev);
return 0;
out: out:
genpd_release_lock(genpd); genpd_release_lock(genpd);
...@@ -1378,7 +1448,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, ...@@ -1378,7 +1448,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
goto out; goto out;
} }
list_for_each_entry(link, &genpd->slave_links, slave_node) { list_for_each_entry(link, &genpd->master_links, master_node) {
if (link->slave == subdomain && link->master == genpd) { if (link->slave == subdomain && link->master == genpd) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
...@@ -1690,6 +1760,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd, ...@@ -1690,6 +1760,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->resume_count = 0; genpd->resume_count = 0;
genpd->device_count = 0; genpd->device_count = 0;
genpd->max_off_time_ns = -1; genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true;
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
......
...@@ -14,6 +14,31 @@ ...@@ -14,6 +14,31 @@
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int dev_update_qos_constraint(struct device *dev, void *data)
{
s64 *constraint_ns_p = data;
s32 constraint_ns = -1;
if (dev->power.subsys_data && dev->power.subsys_data->domain_data)
constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
if (constraint_ns < 0) {
constraint_ns = dev_pm_qos_read_value(dev);
constraint_ns *= NSEC_PER_USEC;
}
if (constraint_ns == 0)
return 0;
/*
* constraint_ns cannot be negative here, because the device has been
* suspended.
*/
if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
*constraint_ns_p = constraint_ns;
return 0;
}
/** /**
* default_stop_ok - Default PM domain governor routine for stopping devices. * default_stop_ok - Default PM domain governor routine for stopping devices.
* @dev: Device to check. * @dev: Device to check.
...@@ -21,14 +46,52 @@ ...@@ -21,14 +46,52 @@
bool default_stop_ok(struct device *dev) bool default_stop_ok(struct device *dev)
{ {
struct gpd_timing_data *td = &dev_gpd_data(dev)->td; struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
unsigned long flags;
s64 constraint_ns;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0) spin_lock_irqsave(&dev->power.lock, flags);
return true;
if (!td->constraint_changed) {
bool ret = td->cached_stop_ok;
return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns spin_unlock_irqrestore(&dev->power.lock, flags);
&& td->break_even_ns < dev->power.max_time_suspended_ns; return ret;
}
td->constraint_changed = false;
td->cached_stop_ok = false;
td->effective_constraint_ns = -1;
constraint_ns = __dev_pm_qos_read_value(dev);
spin_unlock_irqrestore(&dev->power.lock, flags);
if (constraint_ns < 0)
return false;
constraint_ns *= NSEC_PER_USEC;
/*
* We can walk the children without any additional locking, because
* they all have been suspended at this point and their
* effective_constraint_ns fields won't be modified in parallel with us.
*/
if (!dev->power.ignore_children)
device_for_each_child(dev, &constraint_ns,
dev_update_qos_constraint);
if (constraint_ns > 0) {
constraint_ns -= td->start_latency_ns;
if (constraint_ns == 0)
return false;
}
td->effective_constraint_ns = constraint_ns;
td->cached_stop_ok = constraint_ns > td->stop_latency_ns ||
constraint_ns == 0;
/*
* The children have been suspended already, so we don't need to take
* their stop latencies into account here.
*/
return td->cached_stop_ok;
} }
/** /**
...@@ -42,9 +105,27 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -42,9 +105,27 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
struct generic_pm_domain *genpd = pd_to_genpd(pd); struct generic_pm_domain *genpd = pd_to_genpd(pd);
struct gpd_link *link; struct gpd_link *link;
struct pm_domain_data *pdd; struct pm_domain_data *pdd;
s64 min_dev_off_time_ns; s64 min_off_time_ns;
s64 off_on_time_ns; s64 off_on_time_ns;
ktime_t time_now = ktime_get();
if (genpd->max_off_time_changed) {
struct gpd_link *link;
/*
* We have to invalidate the cached results for the masters, so
* use the observation that default_power_down_ok() is not
* going to be called for any master until this instance
* returns.
*/
list_for_each_entry(link, &genpd->slave_links, slave_node)
link->master->max_off_time_changed = true;
genpd->max_off_time_changed = false;
genpd->cached_power_down_ok = false;
genpd->max_off_time_ns = -1;
} else {
return genpd->cached_power_down_ok;
}
off_on_time_ns = genpd->power_off_latency_ns + off_on_time_ns = genpd->power_off_latency_ns +
genpd->power_on_latency_ns; genpd->power_on_latency_ns;
...@@ -61,6 +142,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -61,6 +142,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
to_gpd_data(pdd)->td.save_state_latency_ns; to_gpd_data(pdd)->td.save_state_latency_ns;
} }
min_off_time_ns = -1;
/* /*
* Check if subdomains can be off for enough time. * Check if subdomains can be off for enough time.
* *
...@@ -73,8 +155,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -73,8 +155,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
if (sd_max_off_ns < 0) if (sd_max_off_ns < 0)
continue; continue;
sd_max_off_ns -= ktime_to_ns(ktime_sub(time_now,
sd->power_off_time));
/* /*
* Check if the subdomain is allowed to be off long enough for * Check if the subdomain is allowed to be off long enough for
* the current domain to turn off and on (that's how much time * the current domain to turn off and on (that's how much time
...@@ -82,60 +162,64 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) ...@@ -82,60 +162,64 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
*/ */
if (sd_max_off_ns <= off_on_time_ns) if (sd_max_off_ns <= off_on_time_ns)
return false; return false;
if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0)
min_off_time_ns = sd_max_off_ns;
} }
/* /*
* Check if the devices in the domain can be off enough time. * Check if the devices in the domain can be off enough time.
*/ */
min_dev_off_time_ns = -1;
list_for_each_entry(pdd, &genpd->dev_list, list_node) { list_for_each_entry(pdd, &genpd->dev_list, list_node) {
struct gpd_timing_data *td; struct gpd_timing_data *td;
struct device *dev = pdd->dev; s64 constraint_ns;
s64 dev_off_time_ns;
if (!dev->driver || dev->power.max_time_suspended_ns < 0) if (!pdd->dev->driver)
continue; continue;
/*
* Check if the device is allowed to be off long enough for the
* domain to turn off and on (that's how much time it will
* have to wait worst case).
*/
td = &to_gpd_data(pdd)->td; td = &to_gpd_data(pdd)->td;
dev_off_time_ns = dev->power.max_time_suspended_ns - constraint_ns = td->effective_constraint_ns;
(td->start_latency_ns + td->restore_state_latency_ns + /* default_stop_ok() need not be called before us. */
ktime_to_ns(ktime_sub(time_now, if (constraint_ns < 0) {
dev->power.suspend_time))); constraint_ns = dev_pm_qos_read_value(pdd->dev);
if (dev_off_time_ns <= off_on_time_ns) constraint_ns *= NSEC_PER_USEC;
return false;
if (min_dev_off_time_ns > dev_off_time_ns
|| min_dev_off_time_ns < 0)
min_dev_off_time_ns = dev_off_time_ns;
} }
if (constraint_ns == 0)
continue;
if (min_dev_off_time_ns < 0) {
/* /*
* There are no latency constraints, so the domain can spend * constraint_ns cannot be negative here, because the device has
* arbitrary time in the "off" state. * been suspended.
*/ */
genpd->max_off_time_ns = -1; constraint_ns -= td->restore_state_latency_ns;
return true; if (constraint_ns <= off_on_time_ns)
return false;
if (min_off_time_ns > constraint_ns || min_off_time_ns < 0)
min_off_time_ns = constraint_ns;
} }
genpd->cached_power_down_ok = true;
/* /*
* The difference between the computed minimum delta and the time needed * If the computed minimum device off time is negative, there are no
* to turn the domain on is the maximum theoretical time this domain can * latency constraints, so the domain can spend arbitrary time in the
* spend in the "off" state. * "off" state.
*/ */
min_dev_off_time_ns -= genpd->power_on_latency_ns; if (min_off_time_ns < 0)
return true;
/* /*
* If the difference between the computed minimum delta and the time * The difference between the computed minimum subdomain or device off
* needed to turn the domain off and back on on is smaller than the * time and the time needed to turn the domain on is the maximum
* domain's power break even time, removing power from the domain is not * theoretical time this domain can spend in the "off" state.
* worth it.
*/ */
if (genpd->break_even_ns > genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns;
min_dev_off_time_ns - genpd->power_off_latency_ns)
return false;
genpd->max_off_time_ns = min_dev_off_time_ns;
return true; return true;
} }
......
...@@ -352,21 +352,26 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); ...@@ -352,21 +352,26 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
* *
* Will register the notifier into a notification chain that gets called * Will register the notifier into a notification chain that gets called
* upon changes to the target value for the device. * upon changes to the target value for the device.
*
* If the device's constraints object doesn't exist when this routine is called,
* it will be created (or error code will be returned if that fails).
*/ */
int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
{ {
int retval = 0; int ret = 0;
mutex_lock(&dev_pm_qos_mtx); mutex_lock(&dev_pm_qos_mtx);
/* Silently return if the constraints object is not present. */ if (!dev->power.constraints)
if (dev->power.constraints) ret = dev->power.power_state.event != PM_EVENT_INVALID ?
retval = blocking_notifier_chain_register( dev_pm_qos_constraints_allocate(dev) : -ENODEV;
dev->power.constraints->notifiers,
notifier); if (!ret)
ret = blocking_notifier_chain_register(
dev->power.constraints->notifiers, notifier);
mutex_unlock(&dev_pm_qos_mtx); mutex_unlock(&dev_pm_qos_mtx);
return retval; return ret;
} }
EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
......
...@@ -282,47 +282,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) ...@@ -282,47 +282,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
return retval != -EACCES ? retval : -EIO; return retval != -EACCES ? retval : -EIO;
} }
struct rpm_qos_data {
ktime_t time_now;
s64 constraint_ns;
};
/**
* rpm_update_qos_constraint - Update a given PM QoS constraint data.
* @dev: Device whose timing data to use.
* @data: PM QoS constraint data to update.
*
* Use the suspend timing data of @dev to update PM QoS constraint data pointed
* to by @data.
*/
static int rpm_update_qos_constraint(struct device *dev, void *data)
{
struct rpm_qos_data *qos = data;
unsigned long flags;
s64 delta_ns;
int ret = 0;
spin_lock_irqsave(&dev->power.lock, flags);
if (dev->power.max_time_suspended_ns < 0)
goto out;
delta_ns = dev->power.max_time_suspended_ns -
ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time));
if (delta_ns <= 0) {
ret = -EBUSY;
goto out;
}
if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0)
qos->constraint_ns = delta_ns;
out:
spin_unlock_irqrestore(&dev->power.lock, flags);
return ret;
}
/** /**
* rpm_suspend - Carry out runtime suspend of given device. * rpm_suspend - Carry out runtime suspend of given device.
* @dev: Device to suspend. * @dev: Device to suspend.
...@@ -349,7 +308,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -349,7 +308,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
{ {
int (*callback)(struct device *); int (*callback)(struct device *);
struct device *parent = NULL; struct device *parent = NULL;
struct rpm_qos_data qos;
int retval; int retval;
trace_rpm_suspend(dev, rpmflags); trace_rpm_suspend(dev, rpmflags);
...@@ -445,38 +403,14 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -445,38 +403,14 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto out; goto out;
} }
qos.constraint_ns = __dev_pm_qos_read_value(dev); if (__dev_pm_qos_read_value(dev) < 0) {
if (qos.constraint_ns < 0) { /* Negative PM QoS constraint means "never suspend". */
/* Negative constraint means "never suspend". */
retval = -EPERM; retval = -EPERM;
goto out; goto out;
} }
qos.constraint_ns *= NSEC_PER_USEC;
qos.time_now = ktime_get();
__update_runtime_status(dev, RPM_SUSPENDING); __update_runtime_status(dev, RPM_SUSPENDING);
if (!dev->power.ignore_children) {
if (dev->power.irq_safe)
spin_unlock(&dev->power.lock);
else
spin_unlock_irq(&dev->power.lock);
retval = device_for_each_child(dev, &qos,
rpm_update_qos_constraint);
if (dev->power.irq_safe)
spin_lock(&dev->power.lock);
else
spin_lock_irq(&dev->power.lock);
if (retval)
goto fail;
}
dev->power.suspend_time = qos.time_now;
dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1;
if (dev->pm_domain) if (dev->pm_domain)
callback = dev->pm_domain->ops.runtime_suspend; callback = dev->pm_domain->ops.runtime_suspend;
else if (dev->type && dev->type->pm) else if (dev->type && dev->type->pm)
...@@ -529,8 +463,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -529,8 +463,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
fail: fail:
__update_runtime_status(dev, RPM_ACTIVE); __update_runtime_status(dev, RPM_ACTIVE);
dev->power.suspend_time = ktime_set(0, 0);
dev->power.max_time_suspended_ns = -1;
dev->power.deferred_resume = false; dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue); wake_up_all(&dev->power.wait_queue);
...@@ -704,9 +636,6 @@ static int rpm_resume(struct device *dev, int rpmflags) ...@@ -704,9 +636,6 @@ static int rpm_resume(struct device *dev, int rpmflags)
if (dev->power.no_callbacks) if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */ goto no_callback; /* Assume success. */
dev->power.suspend_time = ktime_set(0, 0);
dev->power.max_time_suspended_ns = -1;
__update_runtime_status(dev, RPM_RESUMING); __update_runtime_status(dev, RPM_RESUMING);
if (dev->pm_domain) if (dev->pm_domain)
...@@ -1369,9 +1298,6 @@ void pm_runtime_init(struct device *dev) ...@@ -1369,9 +1298,6 @@ void pm_runtime_init(struct device *dev)
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
(unsigned long)dev); (unsigned long)dev);
dev->power.suspend_time = ktime_set(0, 0);
dev->power.max_time_suspended_ns = -1;
init_waitqueue_head(&dev->power.wait_queue); init_waitqueue_head(&dev->power.wait_queue);
} }
...@@ -1389,28 +1315,3 @@ void pm_runtime_remove(struct device *dev) ...@@ -1389,28 +1315,3 @@ void pm_runtime_remove(struct device *dev)
if (dev->power.irq_safe && dev->parent) if (dev->power.irq_safe && dev->parent)
pm_runtime_put_sync(dev->parent); pm_runtime_put_sync(dev->parent);
} }
/**
* pm_runtime_update_max_time_suspended - Update device's suspend time data.
* @dev: Device to handle.
* @delta_ns: Value to subtract from the device's max_time_suspended_ns field.
*
* Update the device's power.max_time_suspended_ns field by subtracting
* @delta_ns from it. The resulting value of power.max_time_suspended_ns is
* never negative.
*/
void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns)
{
unsigned long flags;
spin_lock_irqsave(&dev->power.lock, flags);
if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) {
if (dev->power.max_time_suspended_ns > delta_ns)
dev->power.max_time_suspended_ns -= delta_ns;
else
dev->power.max_time_suspended_ns = 0;
}
spin_unlock_irqrestore(&dev->power.lock, flags);
}
...@@ -544,8 +544,6 @@ struct dev_pm_info { ...@@ -544,8 +544,6 @@ struct dev_pm_info {
unsigned long active_jiffies; unsigned long active_jiffies;
unsigned long suspended_jiffies; unsigned long suspended_jiffies;
unsigned long accounting_timestamp; unsigned long accounting_timestamp;
ktime_t suspend_time;
s64 max_time_suspended_ns;
struct dev_pm_qos_request *pq_req; struct dev_pm_qos_request *pq_req;
#endif #endif
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/notifier.h>
enum gpd_status { enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */ GPD_STATE_ACTIVE = 0, /* PM domain is active */
...@@ -70,9 +71,9 @@ struct generic_pm_domain { ...@@ -70,9 +71,9 @@ struct generic_pm_domain {
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
s64 power_on_latency_ns; s64 power_on_latency_ns;
struct gpd_dev_ops dev_ops; struct gpd_dev_ops dev_ops;
s64 break_even_ns; /* Power break even for the entire domain. */
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
ktime_t power_off_time; bool max_off_time_changed;
bool cached_power_down_ok;
struct device_node *of_node; /* Node in device tree */ struct device_node *of_node; /* Node in device tree */
}; };
...@@ -93,13 +94,17 @@ struct gpd_timing_data { ...@@ -93,13 +94,17 @@ struct gpd_timing_data {
s64 start_latency_ns; s64 start_latency_ns;
s64 save_state_latency_ns; s64 save_state_latency_ns;
s64 restore_state_latency_ns; s64 restore_state_latency_ns;
s64 break_even_ns; s64 effective_constraint_ns;
bool constraint_changed;
bool cached_stop_ok;
}; };
struct generic_pm_domain_data { struct generic_pm_domain_data {
struct pm_domain_data base; struct pm_domain_data base;
struct gpd_dev_ops ops; struct gpd_dev_ops ops;
struct gpd_timing_data td; struct gpd_timing_data td;
struct notifier_block nb;
struct mutex lock;
bool need_restore; bool need_restore;
bool always_on; bool always_on;
}; };
......
...@@ -150,9 +150,6 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, ...@@ -150,9 +150,6 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev,
static inline unsigned long pm_runtime_autosuspend_expiration( static inline unsigned long pm_runtime_autosuspend_expiration(
struct device *dev) { return 0; } struct device *dev) { return 0; }
static inline void pm_runtime_update_max_time_suspended(struct device *dev,
s64 delta_ns) {}
#endif /* !CONFIG_PM_RUNTIME */ #endif /* !CONFIG_PM_RUNTIME */
static inline int pm_runtime_idle(struct device *dev) static inline int pm_runtime_idle(struct device *dev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment