[PATCH RFC 01/27] PM / Domains: core changes for multiple states
Axel Haslam
ahaslam at baylibre.com
Thu Dec 17 09:58:40 PST 2015
On Wed, Dec 9, 2015 at 2:58 PM, Ulf Hansson <ulf.hansson at linaro.org> wrote:
> On 17 November 2015 at 23:37, Lina Iyer <lina.iyer at linaro.org> wrote:
>> From: Axel Haslam <ahaslam+renesas at baylibre.com>
>>
>> From: Axel Haslam <ahaslam at baylibre.com>
>>
>> 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.
>
> I would like to get an answer to *why* we need this.
>
> For example, we should mention that some HWs supports these multiple
> states - at least.
>
>>
>> States should be declared in ascending order from shallowest to deepest,
>
> I guess *should* isn't good enough. Perhaps *shall* is better?
>
>> 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.
>
> This needs some more explanation.
>
> First, please use the wording of "callbacks", like ->power_on() to
> better describe what function you are talking about.
> Second, what is "state_idx"? What's does it really tell the SOC PM
> domain driver here?
>
>>
>> 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(){
>>
>> pd1->state = copy_of(pd_states);
>> pd1->state_count = ARRAY_SIZE(pd_states);
>> pm_genpd_init(pd1, true);
>>
>> }
>
> Well, even if the above describes this quite good, I think it better
> belongs in the Documentation rather than in the change log.
>
> Can we try to the improve the text in the change-log instead?
>
>>
>> Signed-off-by: Axel Haslam <ahaslam+renesas at baylibre.com>
>> Signed-off-by: Lina Iyer <lina.iyer at linaro.org>
>> [Lina: Modified genpd state initialization and remove use of
>> save_state_latency_ns in genpd timing data]
>>
>> Signed-off-by: Lina Iyer <lina.iyer at linaro.org>
>> ---
>> drivers/base/power/domain.c | 136 +++++++++++++++++++++++++++++++++--
>> drivers/base/power/domain_governor.c | 13 +++-
>> include/linux/pm_domain.h | 10 +++
>> 3 files changed, 151 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index e03b1ad..3242854 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -34,6 +34,12 @@
>> __ret; \
>> })
>>
>> +#define GENPD_MAX_NAME_SIZE 20
>> +
>> +static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,
>
> Please avoid forward declarations.
>
> Also, we have just got rid of all *name* based genpd API/functions. I
> don't want us to start adding another.
>
> Or perhaps it's just the name of the function that I don't like!? :-)
>
>> + const struct genpd_power_state *st,
>> + unsigned int st_count);
>> +
>> static LIST_HEAD(gpd_list);
>> static DEFINE_MUTEX(gpd_list_lock);
>>
>> @@ -102,6 +108,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;
>> @@ -118,10 +125,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);
>> @@ -131,6 +138,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;
>> @@ -147,10 +155,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);
>> @@ -582,6 +590,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;
>> @@ -1205,6 +1215,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,
>
> In this patch I would rather just add *one* new "alloc" function and
> name it like below.
>
> static int genpd_alloc_default_state(struct generic_pm_domain *genpd)
>
> I assume the name I suggest for it, indicates what it needs to do and
> *not* needs to do.
Hi Ulf,
Thanks for your thorough review!
i will implement your suggestions for the next spin,
However I have a doubt about this comment, do you mean that i should get rid of
genpd_alloc_states_data completly? i had a static number of states before
but that was droped because o wasted memory if no states were needed.
if you just meant that it should be added in a separate patch, since i
will be sqashing
this patch with "PM / Domains: make governor select deepest state" can
i keep it here? maybe it should go with the dt parsing patch from Marc?
Regards
Axel
>
>> + 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 */
>
> Urgh. :-)
>
> I instead suggest we skip the entire "name" attribute" and just use
> the state_idx to provide the same information.
>
> For sure at this point, this information won't be interpreted by
> "anybody". Reading an integer value instead of string, shouldn't be
> that difficult to understand.
>
> Future wise, if we see that it could be beneficial to add the "name"
> attribute, we can do that then.
>
> Removing the name attribute will also simplify this patch, quite much.
> Can you please do that.
>
>> + 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.
>> @@ -1459,6 +1524,15 @@ static int pm_genpd_default_restore_state(struct device *dev)
>> void pm_genpd_init(struct generic_pm_domain *genpd,
>> struct dev_power_governor *gov, bool is_off)
>> {
>> + int ret;
>> + static const struct genpd_power_state genpd_default_off[] = {
>> + {
>> + .name = "OFF",
>> + .power_off_latency_ns = 0,
>> + .power_on_latency_ns = 0,
>> + },
>> + };
>
> I suggest you remove this and instead handle that within
> genpd_alloc_default_state().
>
>> +
>> if (IS_ERR_OR_NULL(genpd))
>> return;
>>
>> @@ -1503,6 +1577,19 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
>> genpd->dev_ops.start = pm_clk_resume;
>> }
>>
>> + /*
>> + * Allocate the default state if genpd states
>> + * are not already defined.
>> + */
>> + if (!genpd->state_count) {
>> + ret = genpd_alloc_states_data(genpd, genpd_default_off, 1);
>
> Call the new "genpd_alloc_default_state()" instead.
>
>> + if (ret)
>> + return;
>> + }
>> +
>> + /* Assume the deepest state on init*/
>> + genpd->state_idx = genpd->state_count - 1;
>
> When the PM domain is powered on, in other words the genpd->status ==
> GPD_STATE_ACTIVE, I guess this value doesn't matter much.
>
> Although, what if the PM domain is powered off, don't you need to
> respect the value that might have been assigned by the SoC PM domain
> driver? Else you may at the next power on, indicate that you are
> coming from a state that's not the correct one.
>
> Perhaps, you should only set genpd->state_idx = 0, from
> genpd_alloc_default_state() and in the other case leave the value as
> is?
>
>> +
>> mutex_lock(&gpd_list_lock);
>> list_add(&genpd->gpd_list_node, &gpd_list);
>> mutex_unlock(&gpd_list_lock);
>> @@ -1822,6 +1909,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.
>> @@ -1855,6 +1969,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;
>> @@ -1866,7 +1981,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
>> @@ -1954,4 +2073,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 e60dd12..b4984f5 100644
>> --- a/drivers/base/power/domain_governor.c
>> +++ b/drivers/base/power/domain_governor.c
>> @@ -125,8 +125,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.
>> + */
>
> So you want to delay this step into a later patch. I am fine with
> that, but you need to state that in the change-log.
>
> Perhaps it's better to say that we will always try with the
> shallowest/deepest off state, and just ignore the other states in this
> phase?
>
>> + off_on_time_ns = genpd->states[0].power_off_latency_ns +
>> + genpd->states[0].power_on_latency_ns;
>
> If you think my above idea seems reasonable, that should change the above to:
>
> off_on_time_ns = genpd->states[genpd->state_count - 1].power_off_latency_ns +
> genpd->states[genpd->state_count - 1].power_on_latency_ns;
>
>>
>> min_off_time_ns = -1;
>> /*
>> @@ -203,8 +207,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;
>
> Ditto.
>
>> return true;
>> }
>>
>> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>> index ba4ced3..11763cf 100644
>> --- a/include/linux/pm_domain.h
>> +++ b/include/linux/pm_domain.h
>> @@ -37,6 +37,12 @@ struct gpd_dev_ops {
>> bool (*active_wakeup)(struct device *dev);
>> };
>>
>> +struct genpd_power_state {
>> + char *name;
>
> As stated, suggest to remove "name".
>
>> + s64 power_off_latency_ns;
>> + s64 power_on_latency_ns;
>> +};
>> +
>> struct generic_pm_domain {
>> struct dev_pm_domain domain; /* PM domain operations */
>> struct list_head gpd_list_node; /* Node in the global PM domains list */
>> @@ -66,6 +72,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.1.4
>>
>
> Kind regards
> Uffe
More information about the linux-arm-kernel
mailing list