diff mbox series

[v2] remoteproc: q6v5: Add support to vote for rpmh power domains

Message ID 20180904071046.8152-1-rnayak@codeaurora.org (mailing list archive)
State New, archived
Headers show
Series [v2] remoteproc: q6v5: Add support to vote for rpmh power domains | expand

Commit Message

Rajendra Nayak Sept. 4, 2018, 7:10 a.m. UTC
With rpmh ARC resources being modelled as power domains with performance
state,
we need to proxy vote on these for SDM845.
Add support to vote on multiple of them, now that genpd supports
associating
mutliple power domains to a device.

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
---
Changes in v2:
* Drop the perf vote along with disabling the powerdomain
* pm_suspend_ignore_children() to not mess with runtime PM of children
* Detach powerdomains as part of .remove and in error path during probe,
issue reported by Brian Norris <briannorris@chromium.org>
* Use dev_pm_domain_attach_by_name() api

 drivers/remoteproc/qcom_q6v5_pil.c | 111 ++++++++++++++++++++++++++++-
 1 file changed, 109 insertions(+), 2 deletions(-)

Comments

Ulf Hansson Sept. 11, 2018, 11:30 a.m. UTC | #1
+Viresh

On 4 September 2018 at 09:10, Rajendra Nayak <rnayak@codeaurora.org> wrote:
> With rpmh ARC resources being modelled as power domains with performance
> state,
> we need to proxy vote on these for SDM845.
> Add support to vote on multiple of them, now that genpd supports
> associating
> mutliple power domains to a device.

I guess it's fair to mention that the votes are, at this point, done
for maximum performance!?

Is that really needed in the long run? I mean, I guess you should walk
through the OPP library to find the needed performance level? No?

>
> Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
> ---
> Changes in v2:
> * Drop the perf vote along with disabling the powerdomain
> * pm_suspend_ignore_children() to not mess with runtime PM of children
> * Detach powerdomains as part of .remove and in error path during probe,
> issue reported by Brian Norris <briannorris@chromium.org>
> * Use dev_pm_domain_attach_by_name() api
>
>  drivers/remoteproc/qcom_q6v5_pil.c | 111 ++++++++++++++++++++++++++++-
>  1 file changed, 109 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
> index d7a4b9eca5d2..f4b3a9b09a5d 100644
> --- a/drivers/remoteproc/qcom_q6v5_pil.c
> +++ b/drivers/remoteproc/qcom_q6v5_pil.c
> @@ -25,6 +25,8 @@
>  #include <linux/of_address.h>
>  #include <linux/of_device.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/remoteproc.h>
> @@ -131,6 +133,7 @@ struct rproc_hexagon_res {
>         char **proxy_clk_names;
>         char **reset_clk_names;
>         char **active_clk_names;
> +       char **pd_names;
>         int version;
>         bool need_mem_protection;
>         bool has_alt_reset;
> @@ -155,9 +158,11 @@ struct q6v5 {
>         struct clk *active_clks[8];
>         struct clk *reset_clks[4];
>         struct clk *proxy_clks[4];
> +       struct device *pd_devs[3];
>         int active_clk_count;
>         int reset_clk_count;
>         int proxy_clk_count;
> +       int pd_count;
>
>         struct reg_info active_regs[1];
>         struct reg_info proxy_regs[3];
> @@ -316,6 +321,40 @@ static void q6v5_clk_disable(struct device *dev,
>                 clk_disable_unprepare(clks[i]);
>  }
>
> +static int q6v5_powerdomain_enable(struct device *dev, struct device **devs,
> +                                  int count)

Looks like passing the qproc struct, rather than a couple of its
member could simplify the code a bit. No strong opinion though.

> +{
> +       int i;
> +
> +       if (!count)
> +               return 0;
> +
> +       if (count > 1)
> +               for (i = 0; i < count; i++)
> +                       dev_pm_genpd_set_performance_state(devs[i], INT_MAX);
> +       else
> +               dev_pm_genpd_set_performance_state(dev, INT_MAX);
> +
> +       return pm_runtime_get_sync(dev);
> +}
> +
> +static int q6v5_powerdomain_disable(struct device *dev, struct device **devs,
> +                                   int count)

Ditto.

> +{
> +       int i;
> +
> +       if (!count)
> +               return 0;
> +
> +       if (count > 1)
> +               for (i = 0; i < count; i++)
> +                       dev_pm_genpd_set_performance_state(devs[i], 0);
> +       else
> +               dev_pm_genpd_set_performance_state(dev, 0);
> +
> +       return pm_runtime_put(dev);
> +}
> +
>  static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
>                                    bool remote_owner, phys_addr_t addr,
>                                    size_t size)
> @@ -792,11 +831,18 @@ static int q6v5_start(struct rproc *rproc)
>
>         qcom_q6v5_prepare(&qproc->q6v5);
>
> +       ret = q6v5_powerdomain_enable(qproc->dev, qproc->pd_devs,
> +                                     qproc->pd_count);
> +       if (ret < 0) {
> +               dev_err(qproc->dev, "failed to enable power domains\n");
> +               goto disable_irqs;
> +       }
> +
>         ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
>                                     qproc->proxy_reg_count);
>         if (ret) {
>                 dev_err(qproc->dev, "failed to enable proxy supplies\n");
> -               goto disable_irqs;
> +               goto disable_powerdomains;
>         }
>
>         ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
> @@ -920,6 +966,8 @@ static int q6v5_start(struct rproc *rproc)
>         q6v5_regulator_disable(qproc, qproc->proxy_regs,
>                                qproc->proxy_reg_count);
>
> +disable_powerdomains:
> +       q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
>  disable_irqs:
>         qcom_q6v5_unprepare(&qproc->q6v5);
>
> @@ -972,7 +1020,7 @@ static int q6v5_stop(struct rproc *rproc)
>                          qproc->active_clk_count);
>         q6v5_regulator_disable(qproc, qproc->active_regs,
>                                qproc->active_reg_count);
> -
> +       q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
>         return 0;
>  }
>
> @@ -1063,6 +1111,48 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks,
>         return i;
>  }
>
> +static int q6v5_powerdomain_attach(struct device *dev, struct device **devs,
> +                                  char **pd_names)
> +{
> +       int i = 0, num_pds;
> +
> +       if (!pd_names)
> +               return 0;
> +
> +       while (pd_names[i])
> +               i++;
> +
> +       num_pds = i;
> +
> +       if (num_pds > 1) {
> +               for (i = 0; i < num_pds; i++) {
> +                       devs[i] = dev_pm_domain_attach_by_name(dev,
> +                                                              pd_names[i]);
> +                       if (IS_ERR(devs[i]))
> +                               return PTR_ERR(devs[i]);
> +                       if (!device_link_add(dev, devs[i], DL_FLAG_STATELESS |
> +                                            DL_FLAG_PM_RUNTIME))
> +                               return -EINVAL;
> +               }
> +       }
> +
> +       pm_suspend_ignore_children(dev, true);
> +       pm_runtime_enable(dev);
> +
> +       return num_pds;

I would rather add error handling in this function, as that's makes it
more obvious of what goes on. Moreover, it seems like you forgot to
remove the device link in the error path, that also needs to be fixed.

> +};
> +
> +static void q6v5_powerdomain_detach(struct q6v5 *qproc)
> +{
> +       int i;
> +
> +       if (qproc->pd_count > 1)
> +               for (i = 0; i < qproc->pd_count; i++)
> +                       dev_pm_domain_detach(qproc->pd_devs[i], true);
> +
> +       pm_runtime_disable(qproc->dev);
> +}
> +
>  static int q6v5_init_reset(struct q6v5 *qproc)
>  {
>         qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
> @@ -1192,6 +1282,14 @@ static int q6v5_probe(struct platform_device *pdev)
>         }
>         qproc->active_reg_count = ret;
>
> +       ret = q6v5_powerdomain_attach(&pdev->dev, qproc->pd_devs,

I would rather pass qproc instead of qproc->pd_devs and also leave
q6v5_powerdomain_attach() to set qproc->pd_count. In that way the
error handling in q6v5_powerdomain_attach() should becomes more
straight forward.

> +                                     desc->pd_names);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to init power domains\n");
> +               goto free_rproc;
> +       }
> +       qproc->pd_count = ret;
> +
>         ret = q6v5_init_reset(qproc);
>         if (ret)
>                 goto free_rproc;
> @@ -1219,6 +1317,7 @@ static int q6v5_probe(struct platform_device *pdev)
>         return 0;
>
>  free_rproc:
> +       q6v5_powerdomain_detach(qproc);
>         rproc_free(rproc);
>
>         return ret;
> @@ -1228,6 +1327,8 @@ static int q6v5_remove(struct platform_device *pdev)
>  {
>         struct q6v5 *qproc = platform_get_drvdata(pdev);
>
> +       q6v5_powerdomain_detach(qproc);
> +
>         rproc_del(qproc->rproc);
>
>         qcom_remove_sysmon_subdev(qproc->sysmon);
> @@ -1258,6 +1359,12 @@ static const struct rproc_hexagon_res sdm845_mss = {
>                         "mnoc_axi",
>                         NULL
>         },
> +       .pd_names = (char*[]){
> +                       "cx",
> +                       "mx",
> +                       "mss",
> +                       NULL
> +       },
>         .need_mem_protection = true,
>         .has_alt_reset = true,
>         .version = MSS_SDM845,
> --
> QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
> of Code Aurora Forum, hosted by The Linux Foundation
>

Kind regards
Uffe
Rajendra Nayak Sept. 25, 2018, 5:51 a.m. UTC | #2
Hi Ulf,

On 9/11/2018 5:00 PM, Ulf Hansson wrote:
> +Viresh
> 
> On 4 September 2018 at 09:10, Rajendra Nayak <rnayak@codeaurora.org> wrote:
>> With rpmh ARC resources being modelled as power domains with performance
>> state,
>> we need to proxy vote on these for SDM845.
>> Add support to vote on multiple of them, now that genpd supports
>> associating
>> mutliple power domains to a device.

thanks for the review, looks like I missed responding to this one somehow :(

> 
> I guess it's fair to mention that the votes are, at this point, done
> for maximum performance!?
> 
> Is that really needed in the long run? I mean, I guess you should walk
> through the OPP library to find the needed performance level? No?

well, yes the votes are for max perf, but these are just sort of *proxy*
votes from the remoteproc driver on behalf of the firmware, up until the
time the firmware itself is up and running and can put its own requests.
So this is general practice to just vote for max clocks/regulator/perf states
because we don't really know what meets the firmware requirements.

> 
>>
>> Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
>> ---
>> Changes in v2:
>> * Drop the perf vote along with disabling the powerdomain
>> * pm_suspend_ignore_children() to not mess with runtime PM of children
>> * Detach powerdomains as part of .remove and in error path during probe,
>> issue reported by Brian Norris <briannorris@chromium.org>
>> * Use dev_pm_domain_attach_by_name() api
>>
>>   drivers/remoteproc/qcom_q6v5_pil.c | 111 ++++++++++++++++++++++++++++-
>>   1 file changed, 109 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
>> index d7a4b9eca5d2..f4b3a9b09a5d 100644
>> --- a/drivers/remoteproc/qcom_q6v5_pil.c
>> +++ b/drivers/remoteproc/qcom_q6v5_pil.c
>> @@ -25,6 +25,8 @@
>>   #include <linux/of_address.h>
>>   #include <linux/of_device.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/pm_domain.h>
>> +#include <linux/pm_runtime.h>
>>   #include <linux/regmap.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/remoteproc.h>
>> @@ -131,6 +133,7 @@ struct rproc_hexagon_res {
>>          char **proxy_clk_names;
>>          char **reset_clk_names;
>>          char **active_clk_names;
>> +       char **pd_names;
>>          int version;
>>          bool need_mem_protection;
>>          bool has_alt_reset;
>> @@ -155,9 +158,11 @@ struct q6v5 {
>>          struct clk *active_clks[8];
>>          struct clk *reset_clks[4];
>>          struct clk *proxy_clks[4];
>> +       struct device *pd_devs[3];
>>          int active_clk_count;
>>          int reset_clk_count;
>>          int proxy_clk_count;
>> +       int pd_count;
>>
>>          struct reg_info active_regs[1];
>>          struct reg_info proxy_regs[3];
>> @@ -316,6 +321,40 @@ static void q6v5_clk_disable(struct device *dev,
>>                  clk_disable_unprepare(clks[i]);
>>   }
>>
>> +static int q6v5_powerdomain_enable(struct device *dev, struct device **devs,
>> +                                  int count)
> 
> Looks like passing the qproc struct, rather than a couple of its
> member could simplify the code a bit. No strong opinion though.

sure, I will try to simplify this one.

> 
>> +{
>> +       int i;
>> +
>> +       if (!count)
>> +               return 0;
>> +
>> +       if (count > 1)
>> +               for (i = 0; i < count; i++)
>> +                       dev_pm_genpd_set_performance_state(devs[i], INT_MAX);
>> +       else
>> +               dev_pm_genpd_set_performance_state(dev, INT_MAX);
>> +
>> +       return pm_runtime_get_sync(dev);
>> +}
>> +
>> +static int q6v5_powerdomain_disable(struct device *dev, struct device **devs,
>> +                                   int count)
> 
> Ditto.
> 
>> +{
>> +       int i;
>> +
>> +       if (!count)
>> +               return 0;
>> +
>> +       if (count > 1)
>> +               for (i = 0; i < count; i++)
>> +                       dev_pm_genpd_set_performance_state(devs[i], 0);
>> +       else
>> +               dev_pm_genpd_set_performance_state(dev, 0);
>> +
>> +       return pm_runtime_put(dev);
>> +}
>> +
>>   static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
>>                                     bool remote_owner, phys_addr_t addr,
>>                                     size_t size)
>> @@ -792,11 +831,18 @@ static int q6v5_start(struct rproc *rproc)
>>
>>          qcom_q6v5_prepare(&qproc->q6v5);
>>
>> +       ret = q6v5_powerdomain_enable(qproc->dev, qproc->pd_devs,
>> +                                     qproc->pd_count);
>> +       if (ret < 0) {
>> +               dev_err(qproc->dev, "failed to enable power domains\n");
>> +               goto disable_irqs;
>> +       }
>> +
>>          ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
>>                                      qproc->proxy_reg_count);
>>          if (ret) {
>>                  dev_err(qproc->dev, "failed to enable proxy supplies\n");
>> -               goto disable_irqs;
>> +               goto disable_powerdomains;
>>          }
>>
>>          ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
>> @@ -920,6 +966,8 @@ static int q6v5_start(struct rproc *rproc)
>>          q6v5_regulator_disable(qproc, qproc->proxy_regs,
>>                                 qproc->proxy_reg_count);
>>
>> +disable_powerdomains:
>> +       q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
>>   disable_irqs:
>>          qcom_q6v5_unprepare(&qproc->q6v5);
>>
>> @@ -972,7 +1020,7 @@ static int q6v5_stop(struct rproc *rproc)
>>                           qproc->active_clk_count);
>>          q6v5_regulator_disable(qproc, qproc->active_regs,
>>                                 qproc->active_reg_count);
>> -
>> +       q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
>>          return 0;
>>   }
>>
>> @@ -1063,6 +1111,48 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks,
>>          return i;
>>   }
>>
>> +static int q6v5_powerdomain_attach(struct device *dev, struct device **devs,
>> +                                  char **pd_names)
>> +{
>> +       int i = 0, num_pds;
>> +
>> +       if (!pd_names)
>> +               return 0;
>> +
>> +       while (pd_names[i])
>> +               i++;
>> +
>> +       num_pds = i;
>> +
>> +       if (num_pds > 1) {
>> +               for (i = 0; i < num_pds; i++) {
>> +                       devs[i] = dev_pm_domain_attach_by_name(dev,
>> +                                                              pd_names[i]);
>> +                       if (IS_ERR(devs[i]))
>> +                               return PTR_ERR(devs[i]);
>> +                       if (!device_link_add(dev, devs[i], DL_FLAG_STATELESS |
>> +                                            DL_FLAG_PM_RUNTIME))
>> +                               return -EINVAL;
>> +               }
>> +       }
>> +
>> +       pm_suspend_ignore_children(dev, true);
>> +       pm_runtime_enable(dev);
>> +
>> +       return num_pds;
> 
> I would rather add error handling in this function, as that's makes it
> more obvious of what goes on. Moreover, it seems like you forgot to
> remove the device link in the error path, that also needs to be fixed.

sure, I will add the error handling here. I will add the device link removal
too in the error path.

> 
>> +};
>> +
>> +static void q6v5_powerdomain_detach(struct q6v5 *qproc)
>> +{
>> +       int i;
>> +
>> +       if (qproc->pd_count > 1)
>> +               for (i = 0; i < qproc->pd_count; i++)
>> +                       dev_pm_domain_detach(qproc->pd_devs[i], true);
>> +
>> +       pm_runtime_disable(qproc->dev);
>> +}
>> +
>>   static int q6v5_init_reset(struct q6v5 *qproc)
>>   {
>>          qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
>> @@ -1192,6 +1282,14 @@ static int q6v5_probe(struct platform_device *pdev)
>>          }
>>          qproc->active_reg_count = ret;
>>
>> +       ret = q6v5_powerdomain_attach(&pdev->dev, qproc->pd_devs,
> 
> I would rather pass qproc instead of qproc->pd_devs and also leave
> q6v5_powerdomain_attach() to set qproc->pd_count. In that way the
> error handling in q6v5_powerdomain_attach() should becomes more
> straight forward.

sure, I will clean this up.

thanks,
Rajendra

> 
>> +                                     desc->pd_names);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "Failed to init power domains\n");
>> +               goto free_rproc;
>> +       }
>> +       qproc->pd_count = ret;
>> +
>>          ret = q6v5_init_reset(qproc);
>>          if (ret)
>>                  goto free_rproc;
>> @@ -1219,6 +1317,7 @@ static int q6v5_probe(struct platform_device *pdev)
>>          return 0;
>>
>>   free_rproc:
>> +       q6v5_powerdomain_detach(qproc);
>>          rproc_free(rproc);
>>
>>          return ret;
>> @@ -1228,6 +1327,8 @@ static int q6v5_remove(struct platform_device *pdev)
>>   {
>>          struct q6v5 *qproc = platform_get_drvdata(pdev);
>>
>> +       q6v5_powerdomain_detach(qproc);
>> +
>>          rproc_del(qproc->rproc);
>>
>>          qcom_remove_sysmon_subdev(qproc->sysmon);
>> @@ -1258,6 +1359,12 @@ static const struct rproc_hexagon_res sdm845_mss = {
>>                          "mnoc_axi",
>>                          NULL
>>          },
>> +       .pd_names = (char*[]){
>> +                       "cx",
>> +                       "mx",
>> +                       "mss",
>> +                       NULL
>> +       },
>>          .need_mem_protection = true,
>>          .has_alt_reset = true,
>>          .version = MSS_SDM845,
>> --
>> QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
>> of Code Aurora Forum, hosted by The Linux Foundation
>>
> 
> Kind regards
> Uffe
>
diff mbox series

Patch

diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index d7a4b9eca5d2..f4b3a9b09a5d 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -25,6 +25,8 @@ 
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/remoteproc.h>
@@ -131,6 +133,7 @@  struct rproc_hexagon_res {
 	char **proxy_clk_names;
 	char **reset_clk_names;
 	char **active_clk_names;
+	char **pd_names;
 	int version;
 	bool need_mem_protection;
 	bool has_alt_reset;
@@ -155,9 +158,11 @@  struct q6v5 {
 	struct clk *active_clks[8];
 	struct clk *reset_clks[4];
 	struct clk *proxy_clks[4];
+	struct device *pd_devs[3];
 	int active_clk_count;
 	int reset_clk_count;
 	int proxy_clk_count;
+	int pd_count;
 
 	struct reg_info active_regs[1];
 	struct reg_info proxy_regs[3];
@@ -316,6 +321,40 @@  static void q6v5_clk_disable(struct device *dev,
 		clk_disable_unprepare(clks[i]);
 }
 
+static int q6v5_powerdomain_enable(struct device *dev, struct device **devs,
+				   int count)
+{
+	int i;
+
+	if (!count)
+		return 0;
+
+	if (count > 1)
+		for (i = 0; i < count; i++)
+			dev_pm_genpd_set_performance_state(devs[i], INT_MAX);
+	else
+		dev_pm_genpd_set_performance_state(dev, INT_MAX);
+
+	return pm_runtime_get_sync(dev);
+}
+
+static int q6v5_powerdomain_disable(struct device *dev, struct device **devs,
+				    int count)
+{
+	int i;
+
+	if (!count)
+		return 0;
+
+	if (count > 1)
+		for (i = 0; i < count; i++)
+			dev_pm_genpd_set_performance_state(devs[i], 0);
+	else
+		dev_pm_genpd_set_performance_state(dev, 0);
+
+	return pm_runtime_put(dev);
+}
+
 static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
 				   bool remote_owner, phys_addr_t addr,
 				   size_t size)
@@ -792,11 +831,18 @@  static int q6v5_start(struct rproc *rproc)
 
 	qcom_q6v5_prepare(&qproc->q6v5);
 
+	ret = q6v5_powerdomain_enable(qproc->dev, qproc->pd_devs,
+				      qproc->pd_count);
+	if (ret < 0) {
+		dev_err(qproc->dev, "failed to enable power domains\n");
+		goto disable_irqs;
+	}
+
 	ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
 				    qproc->proxy_reg_count);
 	if (ret) {
 		dev_err(qproc->dev, "failed to enable proxy supplies\n");
-		goto disable_irqs;
+		goto disable_powerdomains;
 	}
 
 	ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
@@ -920,6 +966,8 @@  static int q6v5_start(struct rproc *rproc)
 	q6v5_regulator_disable(qproc, qproc->proxy_regs,
 			       qproc->proxy_reg_count);
 
+disable_powerdomains:
+	q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
 disable_irqs:
 	qcom_q6v5_unprepare(&qproc->q6v5);
 
@@ -972,7 +1020,7 @@  static int q6v5_stop(struct rproc *rproc)
 			 qproc->active_clk_count);
 	q6v5_regulator_disable(qproc, qproc->active_regs,
 			       qproc->active_reg_count);
-
+	q6v5_powerdomain_disable(qproc->dev, qproc->pd_devs, qproc->pd_count);
 	return 0;
 }
 
@@ -1063,6 +1111,48 @@  static int q6v5_init_clocks(struct device *dev, struct clk **clks,
 	return i;
 }
 
+static int q6v5_powerdomain_attach(struct device *dev, struct device **devs,
+				   char **pd_names)
+{
+	int i = 0, num_pds;
+
+	if (!pd_names)
+		return 0;
+
+	while (pd_names[i])
+		i++;
+
+	num_pds = i;
+
+	if (num_pds > 1) {
+		for (i = 0; i < num_pds; i++) {
+			devs[i] = dev_pm_domain_attach_by_name(dev,
+							       pd_names[i]);
+			if (IS_ERR(devs[i]))
+				return PTR_ERR(devs[i]);
+			if (!device_link_add(dev, devs[i], DL_FLAG_STATELESS |
+					     DL_FLAG_PM_RUNTIME))
+				return -EINVAL;
+		}
+	}
+
+	pm_suspend_ignore_children(dev, true);
+	pm_runtime_enable(dev);
+
+	return num_pds;
+};
+
+static void q6v5_powerdomain_detach(struct q6v5 *qproc)
+{
+	int i;
+
+	if (qproc->pd_count > 1)
+		for (i = 0; i < qproc->pd_count; i++)
+			dev_pm_domain_detach(qproc->pd_devs[i], true);
+
+	pm_runtime_disable(qproc->dev);
+}
+
 static int q6v5_init_reset(struct q6v5 *qproc)
 {
 	qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
@@ -1192,6 +1282,14 @@  static int q6v5_probe(struct platform_device *pdev)
 	}
 	qproc->active_reg_count = ret;
 
+	ret = q6v5_powerdomain_attach(&pdev->dev, qproc->pd_devs,
+				      desc->pd_names);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to init power domains\n");
+		goto free_rproc;
+	}
+	qproc->pd_count = ret;
+
 	ret = q6v5_init_reset(qproc);
 	if (ret)
 		goto free_rproc;
@@ -1219,6 +1317,7 @@  static int q6v5_probe(struct platform_device *pdev)
 	return 0;
 
 free_rproc:
+	q6v5_powerdomain_detach(qproc);
 	rproc_free(rproc);
 
 	return ret;
@@ -1228,6 +1327,8 @@  static int q6v5_remove(struct platform_device *pdev)
 {
 	struct q6v5 *qproc = platform_get_drvdata(pdev);
 
+	q6v5_powerdomain_detach(qproc);
+
 	rproc_del(qproc->rproc);
 
 	qcom_remove_sysmon_subdev(qproc->sysmon);
@@ -1258,6 +1359,12 @@  static const struct rproc_hexagon_res sdm845_mss = {
 			"mnoc_axi",
 			NULL
 	},
+	.pd_names = (char*[]){
+			"cx",
+			"mx",
+			"mss",
+			NULL
+	},
 	.need_mem_protection = true,
 	.has_alt_reset = true,
 	.version = MSS_SDM845,