diff mbox series

[v3,4/7] clk: qcom: gdsc: call runtime PM functions for the provider device

Message ID 20210709173202.667820-5-dmitry.baryshkov@linaro.org
State New
Headers show
Series clk: qcom: use power-domain for sm8250's clock controllers | expand

Commit Message

Dmitry Baryshkov July 9, 2021, 5:31 p.m. UTC
In order to properly handle runtime PM status of the provider device,
call pm_runtime_get/pm_runtime_put on the clock controller device.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

---
 drivers/clk/qcom/gdsc.c | 66 ++++++++++++++++++++++++++++++++++++++---
 drivers/clk/qcom/gdsc.h |  2 ++
 2 files changed, 64 insertions(+), 4 deletions(-)

-- 
2.30.2

Comments

Bjorn Andersson July 9, 2021, 6:54 p.m. UTC | #1
On Fri 09 Jul 12:31 CDT 2021, Dmitry Baryshkov wrote:

> In order to properly handle runtime PM status of the provider device,

> call pm_runtime_get/pm_runtime_put on the clock controller device.

> 

> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---

>  drivers/clk/qcom/gdsc.c | 66 ++++++++++++++++++++++++++++++++++++++---

>  drivers/clk/qcom/gdsc.h |  2 ++

>  2 files changed, 64 insertions(+), 4 deletions(-)

> 

> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c

> index ccd36617d067..6bec31fccb09 100644

> --- a/drivers/clk/qcom/gdsc.c

> +++ b/drivers/clk/qcom/gdsc.c

> @@ -11,6 +11,7 @@

>  #include <linux/kernel.h>

>  #include <linux/ktime.h>

>  #include <linux/pm_domain.h>

> +#include <linux/pm_runtime.h>

>  #include <linux/regmap.h>

>  #include <linux/regulator/consumer.h>

>  #include <linux/reset-controller.h>

> @@ -50,6 +51,30 @@ enum gdsc_status {

>  	GDSC_ON

>  };

>  

> +static int gdsc_pm_runtime_get(struct gdsc *sc)

> +{

> +	int ret;

> +

> +	if (!sc->rpm_dev)

> +		return 0;

> +

> +	ret = pm_runtime_get_sync(sc->rpm_dev);

> +	if (ret < 0) {

> +		pm_runtime_put_noidle(sc->rpm_dev);

> +		return ret;

> +	}

> +

> +	return 0;

> +}

> +

> +static int gdsc_pm_runtime_put(struct gdsc *sc)

> +{

> +	if (!sc->rpm_dev)

> +		return 0;

> +

> +	return pm_runtime_put_sync(sc->rpm_dev);

> +}

> +

>  /* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */

>  static int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)

>  {

> @@ -232,9 +257,8 @@ static void gdsc_retain_ff_on(struct gdsc *sc)

>  	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);

>  }

>  

> -static int gdsc_enable(struct generic_pm_domain *domain)

> +static int _gdsc_enable(struct gdsc *sc)

>  {

> -	struct gdsc *sc = domain_to_gdsc(domain);

>  	int ret;

>  

>  	if (sc->pwrsts == PWRSTS_ON)

> @@ -290,11 +314,28 @@ static int gdsc_enable(struct generic_pm_domain *domain)

>  	return 0;

>  }

>  

> -static int gdsc_disable(struct generic_pm_domain *domain)

> +static int gdsc_enable(struct generic_pm_domain *domain)

>  {

>  	struct gdsc *sc = domain_to_gdsc(domain);

>  	int ret;

>  

> +	ret = gdsc_pm_runtime_get(sc);

> +	if (ret)

> +		return ret;

> +

> +	ret = _gdsc_enable(sc);

> +	if (ret) {

> +		gdsc_pm_runtime_put(sc);


I presume what you do here is to leave the pm_runtime state of dispcc
active if we succeeded in enabling the gdsc. But the gdsc is a subdomain
of the parent domain, so the framework should take case of its
dependency.

So the reason for gdsc_pm_runtime_get()/put() in this code path is so
that you can access the dispcc registers, i.e. I think you should
get()/put() regardless of the return value.

> +		return ret;

> +	}

> +

> +	return 0;

> +}

> +

> +static int _gdsc_disable(struct gdsc *sc)

> +{

> +	int ret;

> +

>  	if (sc->pwrsts == PWRSTS_ON)

>  		return gdsc_assert_reset(sc);

>  

> @@ -329,6 +370,18 @@ static int gdsc_disable(struct generic_pm_domain *domain)

>  	return 0;

>  }

>  

> +static int gdsc_disable(struct generic_pm_domain *domain)

> +{

> +	struct gdsc *sc = domain_to_gdsc(domain);

> +	int ret;

> +


If the gdsc is found to be on at initialization, the next operation that
will happen is gdsc_disable() and as you didn't activate the pm_runtime
state in gdsc_init() you would in theory get here with registers
unaccessible.

In practice though, the active gdsc should through the being a subdomain
of the parent domain keep power on for you, so you won't notice this
issue.

But as above, I think you should wrap _gdsc_disable() in a get()/put()
pair.

> +	ret = _gdsc_disable(sc);

> +	if (ret)

> +		return ret;

> +

> +	return gdsc_pm_runtime_put(sc);

> +}

> +

>  static int gdsc_init(struct gdsc *sc)

>  {

>  	u32 mask, val;

> @@ -425,6 +478,8 @@ int gdsc_register(struct gdsc_desc *desc,

>  	for (i = 0; i < num; i++) {

>  		if (!scs[i])

>  			continue;

> +		if (pm_runtime_enabled(dev))

> +			scs[i]->rpm_dev = dev;

>  		scs[i]->regmap = regmap;

>  		scs[i]->rcdev = rcdev;

>  		ret = gdsc_init(scs[i]);

> @@ -486,7 +541,10 @@ void gdsc_unregister(struct gdsc_desc *desc)

>   */

>  int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain)

>  {

> +	struct gdsc *sc = domain_to_gdsc(domain);

> +

>  	/* Do nothing but give genpd the impression that we were successful */

> -	return 0;

> +	/* Get the runtime PM device only */

> +	return gdsc_pm_runtime_get(sc);


Per above, if you let the framework deal with the gdsc's dependencies on
the parent domain and you only get()/put() for the sake of dispcc then
you don't need you don't need to do this to keep the subsequent
gdsc_disable() in balance.

>  }

>  EXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable);

> diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h

> index 5bb396b344d1..a82982df0a55 100644

> --- a/drivers/clk/qcom/gdsc.h

> +++ b/drivers/clk/qcom/gdsc.h

> @@ -25,6 +25,7 @@ struct reset_controller_dev;

>   * @resets: ids of resets associated with this gdsc

>   * @reset_count: number of @resets

>   * @rcdev: reset controller

> + * @rpm_dev: runtime PM device

>   */

>  struct gdsc {

>  	struct generic_pm_domain	pd;

> @@ -58,6 +59,7 @@ struct gdsc {

>  

>  	const char 			*supply;

>  	struct regulator		*rsupply;

> +	struct device 			*rpm_dev;


This isn't just the "runtime pm device", it's the device this gdsc is
associated with. So "dev" sounds sufficient to me, but that requires
that you have a separate bool rpm_enabled to remember if
pm_runtime_enabled() was true during probe.

So unless we need "dev" for something else this might be sufficient.

Regards,
Bjorn

>  };

>  

>  struct gdsc_desc {

> -- 

> 2.30.2

>
Dmitry Baryshkov July 9, 2021, 10:10 p.m. UTC | #2
On 09/07/2021 21:54, Bjorn Andersson wrote:
> On Fri 09 Jul 12:31 CDT 2021, Dmitry Baryshkov wrote:

> 

>> In order to properly handle runtime PM status of the provider device,

>> call pm_runtime_get/pm_runtime_put on the clock controller device.

>>

>> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

>> ---

>>   drivers/clk/qcom/gdsc.c | 66 ++++++++++++++++++++++++++++++++++++++---

>>   drivers/clk/qcom/gdsc.h |  2 ++

>>   2 files changed, 64 insertions(+), 4 deletions(-)

>>

>> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c

>> index ccd36617d067..6bec31fccb09 100644

>> --- a/drivers/clk/qcom/gdsc.c

>> +++ b/drivers/clk/qcom/gdsc.c

>> @@ -11,6 +11,7 @@

>>   #include <linux/kernel.h>

>>   #include <linux/ktime.h>

>>   #include <linux/pm_domain.h>

>> +#include <linux/pm_runtime.h>

>>   #include <linux/regmap.h>

>>   #include <linux/regulator/consumer.h>

>>   #include <linux/reset-controller.h>

>> @@ -50,6 +51,30 @@ enum gdsc_status {

>>   	GDSC_ON

>>   };

>>   

>> +static int gdsc_pm_runtime_get(struct gdsc *sc)

>> +{

>> +	int ret;

>> +

>> +	if (!sc->rpm_dev)

>> +		return 0;

>> +

>> +	ret = pm_runtime_get_sync(sc->rpm_dev);

>> +	if (ret < 0) {

>> +		pm_runtime_put_noidle(sc->rpm_dev);

>> +		return ret;

>> +	}

>> +

>> +	return 0;

>> +}

>> +

>> +static int gdsc_pm_runtime_put(struct gdsc *sc)

>> +{

>> +	if (!sc->rpm_dev)

>> +		return 0;

>> +

>> +	return pm_runtime_put_sync(sc->rpm_dev);

>> +}

>> +

>>   /* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */

>>   static int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)

>>   {

>> @@ -232,9 +257,8 @@ static void gdsc_retain_ff_on(struct gdsc *sc)

>>   	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);

>>   }

>>   

>> -static int gdsc_enable(struct generic_pm_domain *domain)

>> +static int _gdsc_enable(struct gdsc *sc)

>>   {

>> -	struct gdsc *sc = domain_to_gdsc(domain);

>>   	int ret;

>>   

>>   	if (sc->pwrsts == PWRSTS_ON)

>> @@ -290,11 +314,28 @@ static int gdsc_enable(struct generic_pm_domain *domain)

>>   	return 0;

>>   }

>>   

>> -static int gdsc_disable(struct generic_pm_domain *domain)

>> +static int gdsc_enable(struct generic_pm_domain *domain)

>>   {

>>   	struct gdsc *sc = domain_to_gdsc(domain);

>>   	int ret;

>>   

>> +	ret = gdsc_pm_runtime_get(sc);

>> +	if (ret)

>> +		return ret;

>> +

>> +	ret = _gdsc_enable(sc);

>> +	if (ret) {

>> +		gdsc_pm_runtime_put(sc);

> 

> I presume what you do here is to leave the pm_runtime state of dispcc

> active if we succeeded in enabling the gdsc. But the gdsc is a subdomain

> of the parent domain, so the framework should take case of its

> dependency.

> 

> So the reason for gdsc_pm_runtime_get()/put() in this code path is so

> that you can access the dispcc registers, i.e. I think you should

> get()/put() regardless of the return value.


pm domain code will handle enabling MMCX, so this code is not required 
strictly speaking. Ulf suggested adding it back, so I followed the 
suggestion. Maybe I misunderstood his suggestion.

putting pm_runtime after gdsc_enable does not sound like a logical case. 
However it would simplify code a bit. Let me try...

> 

>> +		return ret;

>> +	}

>> +

>> +	return 0;

>> +}

>> +

>> +static int _gdsc_disable(struct gdsc *sc)

>> +{

>> +	int ret;

>> +

>>   	if (sc->pwrsts == PWRSTS_ON)

>>   		return gdsc_assert_reset(sc);

>>   

>> @@ -329,6 +370,18 @@ static int gdsc_disable(struct generic_pm_domain *domain)

>>   	return 0;

>>   }

>>   

>> +static int gdsc_disable(struct generic_pm_domain *domain)

>> +{

>> +	struct gdsc *sc = domain_to_gdsc(domain);

>> +	int ret;

>> +

> 

> If the gdsc is found to be on at initialization, the next operation that

> will happen is gdsc_disable() and as you didn't activate the pm_runtime

> state in gdsc_init() you would in theory get here with registers

> unaccessible.

> 

> In practice though, the active gdsc should through the being a subdomain

> of the parent domain keep power on for you, so you won't notice this

> issue.


Nice catch.

> 

> But as above, I think you should wrap _gdsc_disable() in a get()/put()

> pair.

> 

>> +	ret = _gdsc_disable(sc);

>> +	if (ret)

>> +		return ret;

>> +

>> +	return gdsc_pm_runtime_put(sc);

>> +}

>> +

>>   static int gdsc_init(struct gdsc *sc)

>>   {

>>   	u32 mask, val;

>> @@ -425,6 +478,8 @@ int gdsc_register(struct gdsc_desc *desc,

>>   	for (i = 0; i < num; i++) {

>>   		if (!scs[i])

>>   			continue;

>> +		if (pm_runtime_enabled(dev))

>> +			scs[i]->rpm_dev = dev;

>>   		scs[i]->regmap = regmap;

>>   		scs[i]->rcdev = rcdev;

>>   		ret = gdsc_init(scs[i]);

>> @@ -486,7 +541,10 @@ void gdsc_unregister(struct gdsc_desc *desc)

>>    */

>>   int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain)

>>   {

>> +	struct gdsc *sc = domain_to_gdsc(domain);

>> +

>>   	/* Do nothing but give genpd the impression that we were successful */

>> -	return 0;

>> +	/* Get the runtime PM device only */

>> +	return gdsc_pm_runtime_get(sc);

> 

> Per above, if you let the framework deal with the gdsc's dependencies on

> the parent domain and you only get()/put() for the sake of dispcc then

> you don't need you don't need to do this to keep the subsequent

> gdsc_disable() in balance.

> 

>>   }

>>   EXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable);

>> diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h

>> index 5bb396b344d1..a82982df0a55 100644

>> --- a/drivers/clk/qcom/gdsc.h

>> +++ b/drivers/clk/qcom/gdsc.h

>> @@ -25,6 +25,7 @@ struct reset_controller_dev;

>>    * @resets: ids of resets associated with this gdsc

>>    * @reset_count: number of @resets

>>    * @rcdev: reset controller

>> + * @rpm_dev: runtime PM device

>>    */

>>   struct gdsc {

>>   	struct generic_pm_domain	pd;

>> @@ -58,6 +59,7 @@ struct gdsc {

>>   

>>   	const char 			*supply;

>>   	struct regulator		*rsupply;

>> +	struct device 			*rpm_dev;

> 

> This isn't just the "runtime pm device", it's the device this gdsc is

> associated with. So "dev" sounds sufficient to me, but that requires

> that you have a separate bool rpm_enabled to remember if

> pm_runtime_enabled() was true during probe.

> 

> So unless we need "dev" for something else this might be sufficient.

> 

> Regards,

> Bjorn

> 

>>   };

>>   

>>   struct gdsc_desc {

>> -- 

>> 2.30.2

>>



-- 
With best wishes
Dmitry
Bjorn Andersson July 10, 2021, 12:53 a.m. UTC | #3
On Fri 09 Jul 17:10 CDT 2021, Dmitry Baryshkov wrote:

> On 09/07/2021 21:54, Bjorn Andersson wrote:

> > On Fri 09 Jul 12:31 CDT 2021, Dmitry Baryshkov wrote:

> > 

> > > In order to properly handle runtime PM status of the provider device,

> > > call pm_runtime_get/pm_runtime_put on the clock controller device.

> > > 

> > > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> > > ---

> > >   drivers/clk/qcom/gdsc.c | 66 ++++++++++++++++++++++++++++++++++++++---

> > >   drivers/clk/qcom/gdsc.h |  2 ++

> > >   2 files changed, 64 insertions(+), 4 deletions(-)

> > > 

> > > diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c

> > > index ccd36617d067..6bec31fccb09 100644

> > > --- a/drivers/clk/qcom/gdsc.c

> > > +++ b/drivers/clk/qcom/gdsc.c

> > > @@ -11,6 +11,7 @@

> > >   #include <linux/kernel.h>

> > >   #include <linux/ktime.h>

> > >   #include <linux/pm_domain.h>

> > > +#include <linux/pm_runtime.h>

> > >   #include <linux/regmap.h>

> > >   #include <linux/regulator/consumer.h>

> > >   #include <linux/reset-controller.h>

> > > @@ -50,6 +51,30 @@ enum gdsc_status {

> > >   	GDSC_ON

> > >   };

> > > +static int gdsc_pm_runtime_get(struct gdsc *sc)

> > > +{

> > > +	int ret;

> > > +

> > > +	if (!sc->rpm_dev)

> > > +		return 0;

> > > +

> > > +	ret = pm_runtime_get_sync(sc->rpm_dev);

> > > +	if (ret < 0) {

> > > +		pm_runtime_put_noidle(sc->rpm_dev);

> > > +		return ret;

> > > +	}

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static int gdsc_pm_runtime_put(struct gdsc *sc)

> > > +{

> > > +	if (!sc->rpm_dev)

> > > +		return 0;

> > > +

> > > +	return pm_runtime_put_sync(sc->rpm_dev);

> > > +}

> > > +

> > >   /* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */

> > >   static int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)

> > >   {

> > > @@ -232,9 +257,8 @@ static void gdsc_retain_ff_on(struct gdsc *sc)

> > >   	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);

> > >   }

> > > -static int gdsc_enable(struct generic_pm_domain *domain)

> > > +static int _gdsc_enable(struct gdsc *sc)

> > >   {

> > > -	struct gdsc *sc = domain_to_gdsc(domain);

> > >   	int ret;

> > >   	if (sc->pwrsts == PWRSTS_ON)

> > > @@ -290,11 +314,28 @@ static int gdsc_enable(struct generic_pm_domain *domain)

> > >   	return 0;

> > >   }

> > > -static int gdsc_disable(struct generic_pm_domain *domain)

> > > +static int gdsc_enable(struct generic_pm_domain *domain)

> > >   {

> > >   	struct gdsc *sc = domain_to_gdsc(domain);

> > >   	int ret;

> > > +	ret = gdsc_pm_runtime_get(sc);

> > > +	if (ret)

> > > +		return ret;

> > > +

> > > +	ret = _gdsc_enable(sc);

> > > +	if (ret) {

> > > +		gdsc_pm_runtime_put(sc);

> > 

> > I presume what you do here is to leave the pm_runtime state of dispcc

> > active if we succeeded in enabling the gdsc. But the gdsc is a subdomain

> > of the parent domain, so the framework should take case of its

> > dependency.

> > 

> > So the reason for gdsc_pm_runtime_get()/put() in this code path is so

> > that you can access the dispcc registers, i.e. I think you should

> > get()/put() regardless of the return value.

> 

> pm domain code will handle enabling MMCX, so this code is not required

> strictly speaking. Ulf suggested adding it back, so I followed the

> suggestion. Maybe I misunderstood his suggestion.

> 

> putting pm_runtime after gdsc_enable does not sound like a logical case.

> However it would simplify code a bit. Let me try...

> 


If you consider this device's and the individual gdsc's power state as
separate consumers of MMCX, then it perhaps makes more sense?

Similar to how a regulator driver would rely on the regulator framework
to deal with parent regulators...

Regards,
Bjorn

> > 

> > > +		return ret;

> > > +	}

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static int _gdsc_disable(struct gdsc *sc)

> > > +{

> > > +	int ret;

> > > +

> > >   	if (sc->pwrsts == PWRSTS_ON)

> > >   		return gdsc_assert_reset(sc);

> > > @@ -329,6 +370,18 @@ static int gdsc_disable(struct generic_pm_domain *domain)

> > >   	return 0;

> > >   }

> > > +static int gdsc_disable(struct generic_pm_domain *domain)

> > > +{

> > > +	struct gdsc *sc = domain_to_gdsc(domain);

> > > +	int ret;

> > > +

> > 

> > If the gdsc is found to be on at initialization, the next operation that

> > will happen is gdsc_disable() and as you didn't activate the pm_runtime

> > state in gdsc_init() you would in theory get here with registers

> > unaccessible.

> > 

> > In practice though, the active gdsc should through the being a subdomain

> > of the parent domain keep power on for you, so you won't notice this

> > issue.

> 

> Nice catch.

> 

> > 

> > But as above, I think you should wrap _gdsc_disable() in a get()/put()

> > pair.

> > 

> > > +	ret = _gdsc_disable(sc);

> > > +	if (ret)

> > > +		return ret;

> > > +

> > > +	return gdsc_pm_runtime_put(sc);

> > > +}

> > > +

> > >   static int gdsc_init(struct gdsc *sc)

> > >   {

> > >   	u32 mask, val;

> > > @@ -425,6 +478,8 @@ int gdsc_register(struct gdsc_desc *desc,

> > >   	for (i = 0; i < num; i++) {

> > >   		if (!scs[i])

> > >   			continue;

> > > +		if (pm_runtime_enabled(dev))

> > > +			scs[i]->rpm_dev = dev;

> > >   		scs[i]->regmap = regmap;

> > >   		scs[i]->rcdev = rcdev;

> > >   		ret = gdsc_init(scs[i]);

> > > @@ -486,7 +541,10 @@ void gdsc_unregister(struct gdsc_desc *desc)

> > >    */

> > >   int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain)

> > >   {

> > > +	struct gdsc *sc = domain_to_gdsc(domain);

> > > +

> > >   	/* Do nothing but give genpd the impression that we were successful */

> > > -	return 0;

> > > +	/* Get the runtime PM device only */

> > > +	return gdsc_pm_runtime_get(sc);

> > 

> > Per above, if you let the framework deal with the gdsc's dependencies on

> > the parent domain and you only get()/put() for the sake of dispcc then

> > you don't need you don't need to do this to keep the subsequent

> > gdsc_disable() in balance.

> > 

> > >   }

> > >   EXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable);

> > > diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h

> > > index 5bb396b344d1..a82982df0a55 100644

> > > --- a/drivers/clk/qcom/gdsc.h

> > > +++ b/drivers/clk/qcom/gdsc.h

> > > @@ -25,6 +25,7 @@ struct reset_controller_dev;

> > >    * @resets: ids of resets associated with this gdsc

> > >    * @reset_count: number of @resets

> > >    * @rcdev: reset controller

> > > + * @rpm_dev: runtime PM device

> > >    */

> > >   struct gdsc {

> > >   	struct generic_pm_domain	pd;

> > > @@ -58,6 +59,7 @@ struct gdsc {

> > >   	const char 			*supply;

> > >   	struct regulator		*rsupply;

> > > +	struct device 			*rpm_dev;

> > 

> > This isn't just the "runtime pm device", it's the device this gdsc is

> > associated with. So "dev" sounds sufficient to me, but that requires

> > that you have a separate bool rpm_enabled to remember if

> > pm_runtime_enabled() was true during probe.

> > 

> > So unless we need "dev" for something else this might be sufficient.

> > 

> > Regards,

> > Bjorn

> > 

> > >   };

> > >   struct gdsc_desc {

> > > -- 

> > > 2.30.2

> > > 

> 

> 

> -- 

> With best wishes

> Dmitry
diff mbox series

Patch

diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index ccd36617d067..6bec31fccb09 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -11,6 +11,7 @@ 
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset-controller.h>
@@ -50,6 +51,30 @@  enum gdsc_status {
 	GDSC_ON
 };
 
+static int gdsc_pm_runtime_get(struct gdsc *sc)
+{
+	int ret;
+
+	if (!sc->rpm_dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(sc->rpm_dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(sc->rpm_dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int gdsc_pm_runtime_put(struct gdsc *sc)
+{
+	if (!sc->rpm_dev)
+		return 0;
+
+	return pm_runtime_put_sync(sc->rpm_dev);
+}
+
 /* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */
 static int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)
 {
@@ -232,9 +257,8 @@  static void gdsc_retain_ff_on(struct gdsc *sc)
 	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);
 }
 
-static int gdsc_enable(struct generic_pm_domain *domain)
+static int _gdsc_enable(struct gdsc *sc)
 {
-	struct gdsc *sc = domain_to_gdsc(domain);
 	int ret;
 
 	if (sc->pwrsts == PWRSTS_ON)
@@ -290,11 +314,28 @@  static int gdsc_enable(struct generic_pm_domain *domain)
 	return 0;
 }
 
-static int gdsc_disable(struct generic_pm_domain *domain)
+static int gdsc_enable(struct generic_pm_domain *domain)
 {
 	struct gdsc *sc = domain_to_gdsc(domain);
 	int ret;
 
+	ret = gdsc_pm_runtime_get(sc);
+	if (ret)
+		return ret;
+
+	ret = _gdsc_enable(sc);
+	if (ret) {
+		gdsc_pm_runtime_put(sc);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int _gdsc_disable(struct gdsc *sc)
+{
+	int ret;
+
 	if (sc->pwrsts == PWRSTS_ON)
 		return gdsc_assert_reset(sc);
 
@@ -329,6 +370,18 @@  static int gdsc_disable(struct generic_pm_domain *domain)
 	return 0;
 }
 
+static int gdsc_disable(struct generic_pm_domain *domain)
+{
+	struct gdsc *sc = domain_to_gdsc(domain);
+	int ret;
+
+	ret = _gdsc_disable(sc);
+	if (ret)
+		return ret;
+
+	return gdsc_pm_runtime_put(sc);
+}
+
 static int gdsc_init(struct gdsc *sc)
 {
 	u32 mask, val;
@@ -425,6 +478,8 @@  int gdsc_register(struct gdsc_desc *desc,
 	for (i = 0; i < num; i++) {
 		if (!scs[i])
 			continue;
+		if (pm_runtime_enabled(dev))
+			scs[i]->rpm_dev = dev;
 		scs[i]->regmap = regmap;
 		scs[i]->rcdev = rcdev;
 		ret = gdsc_init(scs[i]);
@@ -486,7 +541,10 @@  void gdsc_unregister(struct gdsc_desc *desc)
  */
 int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain)
 {
+	struct gdsc *sc = domain_to_gdsc(domain);
+
 	/* Do nothing but give genpd the impression that we were successful */
-	return 0;
+	/* Get the runtime PM device only */
+	return gdsc_pm_runtime_get(sc);
 }
 EXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable);
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 5bb396b344d1..a82982df0a55 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -25,6 +25,7 @@  struct reset_controller_dev;
  * @resets: ids of resets associated with this gdsc
  * @reset_count: number of @resets
  * @rcdev: reset controller
+ * @rpm_dev: runtime PM device
  */
 struct gdsc {
 	struct generic_pm_domain	pd;
@@ -58,6 +59,7 @@  struct gdsc {
 
 	const char 			*supply;
 	struct regulator		*rsupply;
+	struct device 			*rpm_dev;
 };
 
 struct gdsc_desc {