[PATCH v9 2/6] PM / Domains: core changes for multiple states
From: ahaslam
Date: Mon Oct 19 2015 - 10:53:52 EST
From: Axel Haslam <ahaslam+renesas@xxxxxxxxxxxx>
Add the core changes to be able to declare multiple states.
When trying to set a power domain to off, genpd will be able to choose
from an array of states declared by the platform. The power on and off
latencies are now tied to a state.
States should be declared in ascending order from shallowest to deepest,
deepest meaning the state which takes longer to enter and exit.
the power_off and power_on function can use the 'state_idx' field of the
generic_pm_domain structure, to distinguish between the different states
and act accordingly.
Example:
static int pd1_power_on(struct generic_pm_domain *domain)
{
/* domain->state_idx = state the domain is coming from */
}
static int pd1_power_off(struct generic_pm_domain *domain)
{
/* domain->state_idx = desired powered off state */
}
const struct genpd_power_state pd_states[] = {
{
.name = "RET",
.power_on_latency_ns = ON_LATENCY_FAST,
.power_off_latency_ns = OFF_LATENCY_FAST,
},
{
.name = "DEEP_RET",
.power_on_latency_ns = ON_LATENCY_MED,
.power_off_latency_ns = OFF_LATENCY_MED,
},
{
.name = "OFF",
.power_on_latency_ns = ON_LATENCY_SLOW,
.power_off_latency_ns = OFF_LATENCY_SLOW,
}
};
struct generic_pm_domain pd1 = {
.name = "PD1",
.power_on = pd1_power_on,
.power_off = pd1_power_off,
[...]
};
int xxx_init_pm_domain(){
pm_genpd_init(struct generic_pm_domain,
pd1, pd_states, ARRAY_SIZE(pd_states), true);
}
Signed-off-by: Axel Haslam <ahaslam+renesas@xxxxxxxxxxxx>
---
drivers/base/power/domain.c | 141 +++++++++++++++++++++++++++++++++--
drivers/base/power/domain_governor.c | 13 +++-
include/linux/pm_domain.h | 4 +
3 files changed, 149 insertions(+), 9 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index bb7f415..8362b2c 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -50,6 +50,12 @@
__retval; \
})
+#define GENPD_MAX_NAME_SIZE 20
+
+static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *st,
+ unsigned int st_count);
+
static LIST_HEAD(gpd_list);
static DEFINE_MUTEX(gpd_list_lock);
@@ -124,6 +130,7 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{
+ unsigned int state_idx = genpd->state_idx;
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -140,10 +147,10 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->power_on_latency_ns)
+ if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
return ret;
- genpd->power_on_latency_ns = elapsed_ns;
+ genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "on", elapsed_ns);
@@ -153,6 +160,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
{
+ unsigned int state_idx = genpd->state_idx;
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -169,10 +177,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->power_off_latency_ns)
+ if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
return ret;
- genpd->power_off_latency_ns = elapsed_ns;
+ genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns);
@@ -576,6 +584,8 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
|| atomic_read(&genpd->sd_count) > 0)
return;
+ /* Choose the deepest state when suspending */
+ genpd->state_idx = genpd->state_count - 1;
genpd_power_off(genpd, timed);
genpd->status = GPD_STATE_POWER_OFF;
@@ -1198,6 +1208,61 @@ static void genpd_free_dev_data(struct device *dev,
dev_pm_put_subsys_data(dev);
}
+static int genpd_alloc_states_data(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *st,
+ unsigned int st_count)
+{
+ int ret = 0;
+ unsigned int i;
+
+ if (IS_ERR_OR_NULL(genpd)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!st || (st_count < 1)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Allocate the local memory to keep the states for this genpd */
+ genpd->states = kcalloc(st_count, sizeof(*st), GFP_KERNEL);
+ if (!genpd->states) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < st_count; i++) {
+ genpd->states[i].power_on_latency_ns =
+ st[i].power_on_latency_ns;
+ genpd->states[i].power_off_latency_ns =
+ st[i].power_off_latency_ns;
+ }
+
+ /*
+ * Copy the latency values To keep compatibility with
+ * platforms that are not converted to use the multiple states.
+ * This will be removed once all platforms are converted to use
+ * multiple states. note that non converted platforms will use the
+ * default single off state.
+ */
+ if (genpd->power_on_latency_ns != 0)
+ genpd->states[0].power_on_latency_ns =
+ genpd->power_on_latency_ns;
+
+ if (genpd->power_off_latency_ns != 0)
+ genpd->states[0].power_off_latency_ns =
+ genpd->power_off_latency_ns;
+
+ genpd->state_count = st_count;
+
+ /* to save memory, Name allocation will happen if debug is enabled */
+ pm_genpd_alloc_states_names(genpd, st, st_count);
+
+err:
+ return ret;
+}
+
/**
* __pm_genpd_add_device - Add a device to an I/O PM domain.
* @genpd: PM domain to add the device to.
@@ -1454,9 +1519,23 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
const struct genpd_power_state *states,
unsigned int state_count, bool is_off)
{
+ int ret;
+
if (IS_ERR_OR_NULL(genpd))
return;
+ /* State data should be provided */
+ if (!states || (state_count < 1)) {
+ pr_err("Invalid state data\n");
+ return;
+ }
+
+ ret = genpd_alloc_states_data(genpd, states, state_count);
+ if (ret) {
+ pr_err("Failed to allocate states for %s\n", genpd->name);
+ return;
+ }
+
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
INIT_LIST_HEAD(&genpd->dev_list);
@@ -1468,6 +1547,8 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->device_count = 0;
genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true;
+ /* Assume the deepest state on init*/
+ genpd->state_idx = genpd->state_count - 1;
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
genpd->domain.ops.prepare = pm_genpd_prepare;
@@ -1513,7 +1594,16 @@ EXPORT_SYMBOL_GPL(pm_genpd_init);
void pm_genpd_init_simple(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off)
{
- pm_genpd_init(genpd, gov, NULL, 0, is_off);
+ static const struct genpd_power_state genpd_default_idle_states[] = {
+ {
+ .name = "OFF",
+ .power_off_latency_ns = 0,
+ .power_on_latency_ns = 0,
+ },
+ };
+
+ pm_genpd_init(genpd, gov, genpd_default_idle_states,
+ ARRAY_SIZE(genpd_default_idle_states), is_off);
}
EXPORT_SYMBOL_GPL(pm_genpd_init_simple);
@@ -1830,6 +1920,33 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
#include <linux/kobject.h>
static struct dentry *pm_genpd_debugfs_dir;
+static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *st,
+ unsigned int st_count)
+{
+ unsigned int i;
+
+ if (IS_ERR_OR_NULL(genpd))
+ return -EINVAL;
+
+ if (genpd->state_count != st_count) {
+ pr_err("Invalid allocated state count\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < st_count; i++) {
+ genpd->states[i].name = kstrndup(st[i].name,
+ GENPD_MAX_NAME_SIZE, GFP_KERNEL);
+ if (!genpd->states[i].name) {
+ pr_err("%s Failed to allocate state %d name.\n",
+ genpd->name, i);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
/*
* TODO: This function is a slightly modified version of rtpm_status_show
* from sysfs.c, so generalize it.
@@ -1863,6 +1980,7 @@ static int pm_genpd_summary_one(struct seq_file *s,
[GPD_STATE_ACTIVE] = "on",
[GPD_STATE_POWER_OFF] = "off"
};
+ unsigned int state_idx = genpd->state_idx;
struct pm_domain_data *pm_data;
const char *kobj_path;
struct gpd_link *link;
@@ -1874,7 +1992,11 @@ static int pm_genpd_summary_one(struct seq_file *s,
if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
goto exit;
- seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]);
+
+ seq_printf(s, "%-30s %-15s ", genpd->name,
+ (genpd->status == GPD_STATE_POWER_OFF) ?
+ genpd->states[state_idx].name :
+ status_lookup[genpd->status]);
/*
* Modifications on the list require holding locks on both
@@ -1962,4 +2084,11 @@ static void __exit pm_genpd_debug_exit(void)
debugfs_remove_recursive(pm_genpd_debugfs_dir);
}
__exitcall(pm_genpd_debug_exit);
+#else
+static inline int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *st,
+ unsigned int st_count)
+{
+ return 0;
+}
#endif /* CONFIG_PM_ADVANCED_DEBUG */
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 85e17ba..bf3ac68 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -127,8 +127,12 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
return genpd->cached_power_down_ok;
}
- off_on_time_ns = genpd->power_off_latency_ns +
- genpd->power_on_latency_ns;
+ /*
+ * Use the only available state, until multiple state support is added
+ * to the governor.
+ */
+ off_on_time_ns = genpd->states[0].power_off_latency_ns +
+ genpd->states[0].power_on_latency_ns;
min_off_time_ns = -1;
/*
@@ -205,8 +209,11 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
* The difference between the computed minimum subdomain or device off
* time and the time needed to turn the domain on is the maximum
* theoretical time this domain can spend in the "off" state.
+ * Use the only available state, until multiple state support is added
+ * to the governor.
*/
- genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns;
+ genpd->max_off_time_ns = min_off_time_ns -
+ genpd->states[0].power_on_latency_ns;
return true;
}
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index f37faec..3129f85 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -77,6 +77,10 @@ struct generic_pm_domain {
void (*detach_dev)(struct generic_pm_domain *domain,
struct device *dev);
unsigned int flags; /* Bit field of configs for genpd */
+ struct genpd_power_state *states;
+ unsigned int state_count; /* number of states */
+ unsigned int state_idx; /* state that genpd will go to when off */
+
};
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
--
2.4.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/