Skip to content

Commit

Permalink
Merge branch 'pm-domains'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Rafael J. Wysocki committed May 11, 2012
2 parents 3a9da04 + b723b0e commit e6d1809
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 197 deletions.
151 changes: 111 additions & 40 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/sched.h>
Expand Down Expand Up @@ -38,11 +39,13 @@
ktime_t __start = ktime_get(); \
type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \
s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \
struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \
if (__elapsed > __gpd_data->td.field) { \
__gpd_data->td.field = __elapsed; \
struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \
if (!__retval && __elapsed > __td->field) { \
__td->field = __elapsed; \
dev_warn(dev, name " latency exceeded, new value %lld ns\n", \
__elapsed); \
genpd->max_off_time_changed = true; \
__td->constraint_changed = true; \
} \
__retval; \
})
Expand Down Expand Up @@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > genpd->power_on_latency_ns) {
genpd->power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
if (genpd->name)
pr_warning("%s: Power-on latency exceeded, "
"new value %lld ns\n", genpd->name,
Expand Down Expand Up @@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)

#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.
* @pdd: Domain data of the device to save the state of.
Expand Down Expand Up @@ -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));
if (elapsed_ns > genpd->power_off_latency_ns) {
genpd->power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
if (genpd->name)
pr_warning("%s: Power-off latency exceeded, "
"new value %lld ns\n", genpd->name,
Expand All @@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
}

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) {
genpd_sd_counter_dec(link->master);
Expand Down Expand Up @@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (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
* off, so it can't use mutexes.
Expand Down Expand Up @@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void)

#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) {}

#define pm_genpd_runtime_suspend NULL
Expand Down Expand Up @@ -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))
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);

if (genpd->status == GPD_STATE_POWER_OFF) {
Expand All @@ -1227,26 +1279,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
goto out;
}

gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
if (!gpd_data) {
ret = -ENOMEM;
goto out;
}

genpd->device_count++;
genpd->max_off_time_changed = true;

dev->pm_domain = &genpd->domain;
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;
gpd_data->base.dev = dev;
gpd_data->need_restore = false;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
gpd_data->need_restore = false;
if (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:
genpd_release_lock(genpd);

dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data);
return ret;
}

Expand Down Expand Up @@ -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,
struct device *dev)
{
struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd;
int ret = -EINVAL;
int ret = 0;

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;

genpd_acquire_lock(genpd);
Expand All @@ -1305,21 +1369,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
goto out;
}

list_for_each_entry(pdd, &genpd->dev_list, list_node) {
if (pdd->dev != dev)
continue;
genpd->device_count--;
genpd->max_off_time_changed = true;

list_del_init(&pdd->list_node);
pdd->dev = NULL;
dev_pm_put_subsys_data(dev);
dev->pm_domain = NULL;
kfree(to_gpd_data(pdd));
spin_lock_irq(&dev->power.lock);
dev->pm_domain = NULL;
pdd = dev->power.subsys_data->domain_data;
list_del_init(&pdd->list_node);
dev->power.subsys_data->domain_data = NULL;
spin_unlock_irq(&dev->power.lock);

genpd->device_count--;
gpd_data = to_gpd_data(pdd);
mutex_lock(&gpd_data->lock);
pdd->dev = NULL;
mutex_unlock(&gpd_data->lock);

ret = 0;
break;
}
genpd_release_lock(genpd);

dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data);
dev_pm_put_subsys_data(dev);
return 0;

out:
genpd_release_lock(genpd);
Expand Down Expand Up @@ -1378,7 +1448,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
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) {
ret = -EINVAL;
goto out;
Expand Down Expand Up @@ -1690,6 +1760,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->resume_count = 0;
genpd->device_count = 0;
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_resume = pm_genpd_runtime_resume;
genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
Expand Down
Loading

0 comments on commit e6d1809

Please sign in to comment.