summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/domain.c1
-rw-r--r--drivers/base/power/domain_governor.c53
-rw-r--r--include/linux/pm_domain.h2
3 files changed, 51 insertions, 5 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 73ce9fbe983..3c6e94fe058 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -506,6 +506,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (dev_gpd_data(dev)->always_on)
return -EBUSY;
+ dev_gpd_data(dev)->td.effective_constraint_ns = -1;
stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
if (stop_ok && !stop_ok(dev))
return -EBUSY;
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 66a265bf586..a67f157a7a7 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -14,6 +14,31 @@
#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.
* @dev: Device to check.
@@ -21,14 +46,34 @@
bool default_stop_ok(struct device *dev)
{
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ s64 constraint_ns;
dev_dbg(dev, "%s()\n", __func__);
- if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0)
- return true;
+ constraint_ns = dev_pm_qos_read_value(dev);
+ 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.
+ */
+ if (!dev->power.ignore_children)
+ device_for_each_child(dev, &constraint_ns,
+ dev_update_qos_constraint);
- return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns
- && td->break_even_ns < dev->power.max_time_suspended_ns;
+ if (constraint_ns > 0) {
+ constraint_ns -= td->start_latency_ns;
+ if (constraint_ns == 0)
+ return false;
+ }
+ td->effective_constraint_ns = constraint_ns;
+ /*
+ * The children have been suspended already, so we don't need to take
+ * their stop latencies into account here.
+ */
+ return constraint_ns > td->stop_latency_ns || constraint_ns == 0;
}
/**
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 91f8286106e..9c25219458c 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -93,7 +93,7 @@ struct gpd_timing_data {
s64 start_latency_ns;
s64 save_state_latency_ns;
s64 restore_state_latency_ns;
- s64 break_even_ns;
+ s64 effective_constraint_ns;
};
struct generic_pm_domain_data {