diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 45937f8..b519926 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -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
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 2a4154a..9d74885 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -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;
 }
 
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 080e778..25082f6 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -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)
