@@ -141,12 +141,17 @@ static void genpd_set_active(struct generic_pm_domain *genpd)
static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
{
+ int state_idx = genpd->state_idx;
s64 usecs64;
if (!genpd->cpuidle_data)
return;
- usecs64 = genpd->power_on_latency_ns;
+ if (genpd->state_count == 0)
+ usecs64 = genpd->power_on_latency_ns;
+ else
+ usecs64 = genpd->states[state_idx].power_on_latency_ns;
+
do_div(usecs64, NSEC_PER_USEC);
usecs64 += genpd->cpuidle_data->saved_exit_latency;
genpd->cpuidle_data->idle_state->exit_latency = usecs64;
@@ -154,6 +159,7 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
static int genpd_power_on(struct generic_pm_domain *genpd)
{
+ int state_idx = genpd->state_idx;
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -167,10 +173,16 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->power_on_latency_ns)
- return ret;
+ if (genpd->state_count == 0) {
+ if (elapsed_ns <= genpd->power_on_latency_ns)
+ return ret;
+ genpd->power_on_latency_ns = elapsed_ns;
+ } else {
+ if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
+ return ret;
+ genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
+ }
- genpd->power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
genpd_recalc_cpu_exit_latency(genpd);
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n",
@@ -181,6 +193,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
static int genpd_power_off(struct generic_pm_domain *genpd)
{
+ int state_idx = genpd->state_idx;
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -194,10 +207,15 @@ static int genpd_power_off(struct generic_pm_domain *genpd)
return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->power_off_latency_ns)
- return ret;
-
- genpd->power_off_latency_ns = elapsed_ns;
+ if (genpd->state_count == 0) {
+ if (elapsed_ns <= genpd->power_off_latency_ns)
+ return ret;
+ genpd->power_off_latency_ns = elapsed_ns;
+ } else {
+ if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
+ return ret;
+ genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
+ }
genpd->max_off_time_changed = true;
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns);
@@ -486,6 +504,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
struct pm_domain_data *pdd;
struct gpd_link *link;
unsigned int not_suspended;
+ int state_idx;
int ret = 0;
start:
@@ -538,6 +557,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
genpd->status = GPD_STATE_BUSY;
genpd->poweroff_task = current;
+ state_idx = genpd->state_idx;
list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
ret = atomic_read(&genpd->sd_count) == 0 ?
@@ -1438,6 +1458,54 @@ static void genpd_free_dev_data(struct device *dev,
kfree(gpd_data);
dev_pm_put_subsys_data(dev);
}
+/**
+ * genpd_alloc_states_data - Allocate and copy state parameters
+ * @genpd: PM domain use to copy and allocate data
+ */
+static struct genpd_power_state *genpd_alloc_states_data(
+ struct generic_pm_domain *genpd)
+{
+ struct genpd_power_state *states;
+ size_t states_size;
+ int i;
+
+ if (IS_ERR_OR_NULL(genpd))
+ return NULL;
+
+ if (!genpd->states) {
+ pr_err("%s Null state pointer\n", genpd->name);
+ return NULL;
+ }
+
+ if (genpd->state_count < 1) {
+ pr_err("%s Invalid number of states %d\n",
+ genpd->name, genpd->state_count);
+ return NULL;
+ }
+
+ /* Allocate the local memory to keep the states for this genpd */
+ states_size = sizeof(*states) * genpd->state_count;
+ states = kzalloc(states_size, GFP_KERNEL);
+ if (!states) {
+ pr_err("%s states Allocation error\n", genpd->name);
+ return NULL;
+ }
+
+ /* Copy the parameters passed to the allocated structure */
+ for (i = 0; i < genpd->state_count; i++) {
+ states[i].power_off_latency_ns =
+ genpd->states[i].power_off_latency_ns;
+ states[i].power_on_latency_ns =
+ genpd->states[i].power_on_latency_ns;
+ }
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ /* State name is used for debug. Avoid unnecessary allocations */
+ for (i = 0; i < genpd->state_count; i++)
+ states[i].name = kstrdup(genpd->states[i].name, GFP_KERNEL);
+#endif
+ return states;
+}
/**
* __pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -1863,6 +1931,13 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
if (IS_ERR_OR_NULL(genpd))
return;
+ if (genpd->state_count > 0) {
+ /* Copy the state data to allocated memory */
+ genpd->states = genpd_alloc_states_data(genpd);
+ if (!genpd->states)
+ return;
+ }
+
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
INIT_LIST_HEAD(&genpd->dev_list);
@@ -2251,6 +2326,7 @@ static int pm_genpd_summary_one(struct seq_file *s,
[GPD_STATE_REPEAT] = "off-in-progress",
[GPD_STATE_POWER_OFF] = "off"
};
+ int state_idx = genpd->state_idx;
struct pm_domain_data *pm_data;
const char *kobj_path;
struct gpd_link *link;
@@ -2262,7 +2338,15 @@ 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]);
+
+ if (genpd->state_count == 0)
+ seq_printf(s, "%-30s %-15s ",
+ genpd->name, status_lookup[genpd->status]);
+ else
+ 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
@@ -124,8 +124,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;
+ if (genpd->state_count == 0)
+ off_on_time_ns = genpd->power_off_latency_ns +
+ genpd->power_on_latency_ns;
+ else
+ off_on_time_ns = genpd->states[0].power_off_latency_ns +
+ genpd->states[0].power_on_latency_ns;
/*
* It doesn't make sense to remove power from the domain if saving
* the state of all devices in it and the power off/power on operations
@@ -216,7 +220,12 @@ 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;
+ if (genpd->state_count == 0)
+ genpd->max_off_time_ns =
+ min_off_time_ns - genpd->power_on_latency_ns;
+ else
+ genpd->max_off_time_ns = min_off_time_ns -
+ genpd->states[0].power_on_latency_ns;
return true;
}
@@ -46,6 +46,12 @@ struct gpd_cpuidle_data {
struct cpuidle_state *idle_state;
};
+struct genpd_power_state {
+ char *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 */
@@ -80,6 +86,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;
+ int state_count; /* number of states */
+ 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)