diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index c298de8a8308..e9c85c96580c 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2219,8 +2219,14 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
 	genpd_queue_power_off_work(pd);
 
 	/* Unregister the device if it was created by genpd. */
-	if (dev->bus == &genpd_bus_type)
+	if (dev->bus == &genpd_bus_type) {
+		struct opp_table *opp_table = dev_get_drvdata(dev);
+
+		if (opp_table)
+			dev_pm_opp_put_required_device(opp_table, dev);
+
 		device_unregister(dev);
+	}
 }
 
 static void genpd_dev_pm_sync(struct device *dev)
@@ -2235,7 +2241,8 @@ static void genpd_dev_pm_sync(struct device *dev)
 }
 
 static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np,
-				 unsigned int index)
+				 unsigned int index,
+				 struct generic_pm_domain **pd_ptr)
 {
 	struct of_phandle_args pd_args;
 	struct generic_pm_domain *pd;
@@ -2277,6 +2284,8 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np,
 
 	if (ret)
 		genpd_remove_device(pd, dev);
+	else if (pd_ptr)
+		*pd_ptr = pd;
 
 	return ret ? -EPROBE_DEFER : 1;
 }
@@ -2307,7 +2316,7 @@ int genpd_dev_pm_attach(struct device *dev)
 				       "#power-domain-cells") != 1)
 		return 0;
 
-	return __genpd_dev_pm_attach(dev, dev->of_node, 0);
+	return __genpd_dev_pm_attach(dev, dev->of_node, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
 
@@ -2330,6 +2339,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
 struct device *genpd_dev_pm_attach_by_id(struct device *dev,
 					 unsigned int index)
 {
+	struct generic_pm_domain *pd;
 	struct device *genpd_dev;
 	int num_domains;
 	int ret;
@@ -2359,12 +2369,25 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
 	}
 
 	/* Try to attach the device to the PM domain at the specified index. */
-	ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index);
+	ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index, &pd);
 	if (ret < 1) {
 		device_unregister(genpd_dev);
 		return ret ? ERR_PTR(ret) : NULL;
 	}
 
+	if (pd->set_performance_state) {
+		struct opp_table *opp_table;
+
+		opp_table = dev_pm_opp_set_required_device(dev, genpd_dev,
+							   index);
+		if (IS_ERR(opp_table)) {
+			genpd_dev_pm_detach(genpd_dev, true);
+			return ERR_CAST(opp_table);
+		}
+
+		dev_set_drvdata(genpd_dev, opp_table);
+	}
+
 	pm_runtime_set_active(genpd_dev);
 	pm_runtime_enable(genpd_dev);
 
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index acc34c238fd6..3a2f08c56c4e 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1532,6 +1532,75 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
 
+/**
+ * dev_pm_opp_set_required_device - Set required device for an index
+ * @dev: Device for which the required device is getting set.
+ * @required_dev: required device.
+ * @index: index for which the required device is getting added.
+ *
+ * Multiple generic power domains for a device are supported with the help of
+ * virtual devices, which are created for each dev-genpd pair. These are the
+ * device structures which are attached to the power domain and are required by
+ * the OPP core to set the performance state of the genpd.
+ *
+ * This helper needs to be called once for each genpd of a device, but only if
+ * multiple domains are present for a device. Otherwise the original device
+ * structure will be used instead by the OPP core.
+ */
+struct opp_table *dev_pm_opp_set_required_device(struct device *dev,
+						 struct device *required_dev,
+						 int index)
+{
+	struct opp_table *opp_table;
+
+	opp_table = dev_pm_opp_get_opp_table(dev);
+	if (!opp_table)
+		return ERR_PTR(-ENOMEM);
+
+	/* Make sure there are no concurrent readers while updating opp_table */
+	WARN_ON(!list_empty(&opp_table->opp_list));
+
+	if (unlikely(!opp_table->required_devices ||
+		     index >= opp_table->required_opp_count ||
+		     opp_table->required_devices[index])) {
+		dev_err(dev, "Invalid request to set required device\n");
+		dev_pm_opp_put_opp_table(opp_table);
+		return ERR_PTR(-EINVAL);
+	}
+
+	opp_table->required_devices[index] = required_dev;
+
+	return opp_table;
+}
+
+/**
+ * dev_pm_opp_put_required_device() - Releases resources blocked for required device.
+ * @opp_table: OPP table returned by dev_pm_opp_set_required_device().
+ * @required_dev: required device.
+ *
+ * This releases the resource previously acquired with a call to
+ * dev_pm_opp_set_required_device().
+ */
+void dev_pm_opp_put_required_device(struct opp_table *opp_table,
+				    struct device *required_dev)
+{
+	int i;
+
+	/* Make sure there are no concurrent readers while updating opp_table */
+	WARN_ON(!list_empty(&opp_table->opp_list));
+
+	for (i = 0; i < opp_table->required_opp_count; i++) {
+		if (opp_table->required_devices[i] != required_dev)
+			continue;
+
+		opp_table->required_devices[i] = NULL;
+		dev_pm_opp_put_opp_table(opp_table);
+		return;
+	}
+
+	dev_err(required_dev, "Failed to find required device entry\n");
+}
+
 /**
  * dev_pm_opp_add()  - Add an OPP table from a table definitions
  * @dev:	device for which we do this operation
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index ffefccfdbc26..20baba090c17 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -133,6 +133,7 @@ static struct opp_table *_get_required_opp_table(struct device_node *np)
 static void _opp_table_free_required_tables(struct opp_table *opp_table)
 {
 	struct opp_table **required_opp_tables = opp_table->required_opp_tables;
+	struct device **required_devices = opp_table->required_devices;
 	int i;
 
 	if (!required_opp_tables)
@@ -146,8 +147,10 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
 	}
 
 	kfree(required_opp_tables);
+	kfree(required_devices);
 
 	opp_table->required_opp_count = 0;
+	opp_table->required_devices = NULL;
 	opp_table->required_opp_tables = NULL;
 }
 
@@ -159,6 +162,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
 					     struct device *dev,
 					     struct device_node *opp_np)
 {
+	struct device **required_devices = NULL;
 	struct opp_table **required_opp_tables;
 	struct device_node *required_np, *np;
 	int count, i;
@@ -174,11 +178,19 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
 	if (!count)
 		goto put_np;
 
+	if (count > 1) {
+		required_devices = kcalloc(count, sizeof(*required_devices),
+					   GFP_KERNEL);
+		if (!required_devices)
+			goto put_np;
+	}
+
 	required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
 				      GFP_KERNEL);
 	if (!required_opp_tables)
 		goto put_np;
 
+	opp_table->required_devices = required_devices;
 	opp_table->required_opp_tables = required_opp_tables;
 	opp_table->required_opp_count = count;
 
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index 9efdc3e4840c..857a7f5e66d0 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -133,6 +133,7 @@ enum opp_table_access {
  * @clock_latency_ns_max: Max clock latency in nanoseconds.
  * @shared_opp: OPP is shared between multiple devices.
  * @suspend_opp: Pointer to OPP to be used during device suspend.
+ * @required_devices: List of virtual devices for multiple genpd support.
  * @required_opp_tables: List of device OPP tables that are required by OPPs in
  *		this table.
  * @required_opp_count: Number of required devices.
@@ -173,6 +174,7 @@ struct opp_table {
 	enum opp_table_access shared_opp;
 	struct dev_pm_opp *suspend_opp;
 
+	struct device **required_devices;
 	struct opp_table **required_opp_tables;
 	unsigned int required_opp_count;
 
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 099b31960dec..ffdb4a78edec 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -125,6 +125,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
 void dev_pm_opp_put_clkname(struct opp_table *opp_table);
 struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
 void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
+struct opp_table *dev_pm_opp_set_required_device(struct device *dev, struct device *required_dev, int index);
+void dev_pm_opp_put_required_device(struct opp_table *opp_table, struct device *required_device);
 int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
 int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
 int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -266,6 +268,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
 
 static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
 
+static inline struct opp_table *dev_pm_opp_set_required_device(struct device *dev, struct device *required_dev, int index)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void dev_pm_opp_put_required_device(struct opp_table *opp_table, struct device *required_device) {}
 static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
 {
 	return -ENOTSUPP;
