mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-26 22:24:09 +08:00
PM / Domains: Support for multiple states
Some hardware (eg. OMAP), has the ability to enter different low power modes for a given power domain. This allows for more fine grained control over the power state of the platform. As a typical example, some registers of the hardware may be implemented with retention flip-flops and be able to retain their state at lower voltages allowing for faster on/off latencies and an increased window of opportunity to enter an intermediate low power state other than "off" When trying to set a power domain to off, the genpd governor will choose the deepest state that will respect the qos constraints of all the devices and sub-domains on the power domain. The state chosen by the governor is saved in the "state_idx" field of the generic_pm_domain structure and shall be used by the power_off and power_on callbacks to perform the necessary actions to set the power domain into (and out of) the state indicated by state_idx. States must be declared in ascending order from shallowest to deepest, deepest meaning the state which takes longer to enter and exit. For platforms that don't declare any states, a single a single "off" state is used. Once all platforms are converted to use the state array, the legacy on/off latencies will be removed. [ Lina: Modified genpd state initialization and remove use of save_state_latency_ns in genpd timing data ] Suggested-by: Kevin Hilman <khilman@linaro.org> Signed-off-by: Lina Iyer <lina.iyer@linaro.org> Signed-off-by: Axel Haslam <ahaslam+renesas@baylibre.com> Acked-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
18558cae02
commit
fc5cbf0c94
@ -104,6 +104,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;
|
||||
@ -120,10 +121,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);
|
||||
@ -133,6 +134,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;
|
||||
@ -149,10 +151,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);
|
||||
@ -585,6 +587,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;
|
||||
@ -1508,6 +1512,26 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
|
||||
genpd->dev_ops.start = pm_clk_resume;
|
||||
}
|
||||
|
||||
if (genpd->state_idx >= GENPD_MAX_NUM_STATES) {
|
||||
pr_warn("Initial state index out of bounds.\n");
|
||||
genpd->state_idx = GENPD_MAX_NUM_STATES - 1;
|
||||
}
|
||||
|
||||
if (genpd->state_count > GENPD_MAX_NUM_STATES) {
|
||||
pr_warn("Limiting states to %d\n", GENPD_MAX_NUM_STATES);
|
||||
genpd->state_count = GENPD_MAX_NUM_STATES;
|
||||
}
|
||||
|
||||
/* Use only one "off" state if there were no states declared */
|
||||
if (genpd->state_count == 0) {
|
||||
genpd->states[0].power_on_latency_ns =
|
||||
genpd->power_on_latency_ns;
|
||||
genpd->states[0].power_off_latency_ns =
|
||||
genpd->power_off_latency_ns;
|
||||
|
||||
genpd->state_count = 1;
|
||||
}
|
||||
|
||||
mutex_lock(&gpd_list_lock);
|
||||
list_add(&genpd->gpd_list_node, &gpd_list);
|
||||
mutex_unlock(&gpd_list_lock);
|
||||
@ -1872,7 +1896,12 @@ 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 %s", genpd->name, status_lookup[genpd->status]);
|
||||
|
||||
if (genpd->status == GPD_STATE_POWER_OFF)
|
||||
seq_printf(s, " %-13d ", genpd->state_idx);
|
||||
else
|
||||
seq_printf(s, " %-15s ", "");
|
||||
|
||||
/*
|
||||
* Modifications on the list require holding locks on both
|
||||
|
@ -98,7 +98,8 @@ static bool default_stop_ok(struct device *dev)
|
||||
*
|
||||
* This routine must be executed under the PM domain's lock.
|
||||
*/
|
||||
static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
static bool __default_power_down_ok(struct dev_pm_domain *pd,
|
||||
unsigned int state)
|
||||
{
|
||||
struct generic_pm_domain *genpd = pd_to_genpd(pd);
|
||||
struct gpd_link *link;
|
||||
@ -106,27 +107,9 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
s64 min_off_time_ns;
|
||||
s64 off_on_time_ns;
|
||||
|
||||
if (genpd->max_off_time_changed) {
|
||||
struct gpd_link *link;
|
||||
off_on_time_ns = genpd->states[state].power_off_latency_ns +
|
||||
genpd->states[state].power_on_latency_ns;
|
||||
|
||||
/*
|
||||
* 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 +
|
||||
genpd->power_on_latency_ns;
|
||||
|
||||
min_off_time_ns = -1;
|
||||
/*
|
||||
@ -186,8 +169,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
min_off_time_ns = constraint_ns;
|
||||
}
|
||||
|
||||
genpd->cached_power_down_ok = true;
|
||||
|
||||
/*
|
||||
* If the computed minimum device off time is negative, there are no
|
||||
* latency constraints, so the domain can spend arbitrary time in the
|
||||
@ -201,10 +182,45 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
* time and the time needed to turn the domain on is the maximum
|
||||
* theoretical time this domain can spend in the "off" state.
|
||||
*/
|
||||
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[state].power_on_latency_ns;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
{
|
||||
struct generic_pm_domain *genpd = pd_to_genpd(pd);
|
||||
struct gpd_link *link;
|
||||
|
||||
if (!genpd->max_off_time_changed)
|
||||
return genpd->cached_power_down_ok;
|
||||
|
||||
/*
|
||||
* 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_ns = -1;
|
||||
genpd->max_off_time_changed = false;
|
||||
genpd->cached_power_down_ok = true;
|
||||
genpd->state_idx = genpd->state_count - 1;
|
||||
|
||||
/* Find a state to power down to, starting from the deepest. */
|
||||
while (!__default_power_down_ok(pd, genpd->state_idx)) {
|
||||
if (genpd->state_idx == 0) {
|
||||
genpd->cached_power_down_ok = false;
|
||||
break;
|
||||
}
|
||||
genpd->state_idx--;
|
||||
}
|
||||
|
||||
return genpd->cached_power_down_ok;
|
||||
}
|
||||
|
||||
static bool always_on_power_down_ok(struct dev_pm_domain *domain)
|
||||
{
|
||||
return false;
|
||||
|
@ -19,6 +19,8 @@
|
||||
/* Defines used for the flags field in the struct generic_pm_domain */
|
||||
#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */
|
||||
|
||||
#define GENPD_MAX_NUM_STATES 8 /* Number of possible low power states */
|
||||
|
||||
enum gpd_status {
|
||||
GPD_STATE_ACTIVE = 0, /* PM domain is active */
|
||||
GPD_STATE_POWER_OFF, /* PM domain is off */
|
||||
@ -37,6 +39,11 @@ struct gpd_dev_ops {
|
||||
bool (*active_wakeup)(struct device *dev);
|
||||
};
|
||||
|
||||
struct genpd_power_state {
|
||||
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 +73,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[GENPD_MAX_NUM_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)
|
||||
|
Loading…
Reference in New Issue
Block a user