diff mbox series

[v4,3/5] clk: qcom: gdsc: Add set and get hwmode callbacks to switch GDSC mode

Message ID 20240122-gdsc-hwctrl-v4-3-9061e8a7aa07@linaro.org (mailing list archive)
State Handled Elsewhere, archived
Headers show
Series PM: domains: Add control for switching back and forth to HW control | expand

Commit Message

Abel Vesa Jan. 22, 2024, 8:47 a.m. UTC
From: Jagadeesh Kona <quic_jkona@quicinc.com>

Add support for set and get hwmode callbacks to switch the GDSC between
SW and HW modes. Currently, the GDSC is moved to HW control mode
using HW_CTRL flag and if this flag is present, GDSC is moved to HW
mode as part of GDSC enable itself. The intention is to keep the
HW_CTRL flag functionality as is, since many older chipsets still use
this flag.

But consumer drivers also require the GDSC mode to be switched dynamically
at runtime based on requirement for certain usecases. Some of these
usecases are switching the GDSC to SW mode to keep it ON during the
enablement of clocks that are dependent on GDSC and while programming
certain configurations that require GDSC to be ON. Introduce a new
HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev
callbacks which allows the consumer drivers to switch the GDSC back and
forth between HW/SW modes dynamically at runtime using new
dev_pm_genpd_set_hwmode API.

Signed-off-by: Jagadeesh Kona <quic_jkona@quicinc.com>
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
---
 drivers/clk/qcom/gdsc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/gdsc.h |  1 +
 2 files changed, 55 insertions(+)

Comments

Bjorn Andersson Jan. 30, 2024, 11 p.m. UTC | #1
On Mon, Jan 22, 2024 at 10:47:03AM +0200, Abel Vesa wrote:
> From: Jagadeesh Kona <quic_jkona@quicinc.com>
> 
> Add support for set and get hwmode callbacks to switch the GDSC between
> SW and HW modes. Currently, the GDSC is moved to HW control mode
> using HW_CTRL flag and if this flag is present, GDSC is moved to HW
> mode as part of GDSC enable itself. The intention is to keep the
> HW_CTRL flag functionality as is, since many older chipsets still use
> this flag.
> 

This provides insight into why we end up with both HW_CTRL and
HW_CTRL_TRIGGER. This doesn't describe why this change is needed, but
rather just an implementation detail.

> But consumer drivers also require the GDSC mode to be switched dynamically
> at runtime based on requirement for certain usecases. Some of these
> usecases are switching the GDSC to SW mode to keep it ON during the
> enablement of clocks that are dependent on GDSC and while programming
> certain configurations that require GDSC to be ON. Introduce a new
> HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev
> callbacks which allows the consumer drivers to switch the GDSC back and
> forth between HW/SW modes dynamically at runtime using new
> dev_pm_genpd_set_hwmode API.
> 

This still expresses the need for HW_CTRL_TRIGGER in terms of "some
drivers need for some use case". We don't need these many words to say:
"Introduce HW_CTRL_TRIGGER for client drivers that need it."


I find that it would be useful to document that every time a GDSC is
turned on the mode will be switched to SW...

> Signed-off-by: Jagadeesh Kona <quic_jkona@quicinc.com>
> Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
> ---
>  drivers/clk/qcom/gdsc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/qcom/gdsc.h |  1 +
>  2 files changed, 55 insertions(+)
> 
> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
> index 5358e28122ab..71626eb20101 100644
> --- a/drivers/clk/qcom/gdsc.c
> +++ b/drivers/clk/qcom/gdsc.c
> @@ -363,6 +363,56 @@ static int gdsc_disable(struct generic_pm_domain *domain)
>  	return 0;
>  }
>  
> +static int gdsc_set_hwmode(struct generic_pm_domain *domain, struct device *dev, bool mode)
> +{
> +	struct gdsc *sc = domain_to_gdsc(domain);
> +	u32 val;
> +	int ret;
> +
> +	if (sc->rsupply && !regulator_is_enabled(sc->rsupply)) {

Why is this a restriction only for GDSCs supplied by regulators? I don't
find anything preventing this API from being called on GDSCs supplied by
other genpd instances.

Also note that regulator_is_enabled() is racy, in that it tells us if
the regulator is currently turned on, not if we're the one holding that
vote. As such this might change at any moment - and hence shouldn't be
significant here.

> +		pr_err("Cannot set mode while parent is disabled\n");
> +		return -EIO;
> +	}
> +
> +	ret = gdsc_hwctrl(sc, mode);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for 1usec for mode transition to properly complete */
> +	udelay(1);
> +
> +	if (!mode) {
> +		ret = regmap_read(sc->regmap, sc->gdscr, &val);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * While switching from HW to SW mode, if GDSC is in enabled
> +		 * state, poll for GDSC to complete the power up.
> +		 */

I had to give this some thought, to conclude that this is relevant if HW
has the GDSC disabled and we're switching to SW - which would then
enable it. I think this comment can be improved slightly, to save the
reader the need for figuring out this on their own.

> +		if (!(val & SW_COLLAPSE_MASK))

This not being true, would imply that gdsc_disable() has been called
already, in which case there's no guarantee that the parent still
supplies power.

In the introduced API power on and hw control are orthogonal states, but
not so in this implementation. This need to made clear, to reduce future
surprises.

> +			return gdsc_poll_status(sc, GDSC_ON);
> +	}
> +
> +	return 0;
> +}
> +
> +static bool gdsc_get_hwmode(struct generic_pm_domain *domain, struct device *dev)
> +{
> +	struct gdsc *sc = domain_to_gdsc(domain);
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(sc->regmap, sc->gdscr, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & HW_CONTROL_MASK)
> +		return true;
> +
> +	return false;

return !!(val & HW_CONTROL_MASK);

Regards,
Bjorn

> +}
> +
>  static int gdsc_init(struct gdsc *sc)
>  {
>  	u32 mask, val;
> @@ -451,6 +501,10 @@ static int gdsc_init(struct gdsc *sc)
>  		sc->pd.power_off = gdsc_disable;
>  	if (!sc->pd.power_on)
>  		sc->pd.power_on = gdsc_enable;
> +	if (sc->flags & HW_CTRL_TRIGGER) {
> +		sc->pd.set_hwmode_dev = gdsc_set_hwmode;
> +		sc->pd.get_hwmode_dev = gdsc_get_hwmode;
> +	}
>  
>  	ret = pm_genpd_init(&sc->pd, NULL, !on);
>  	if (ret)
> diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
> index 803512688336..1e2779b823d1 100644
> --- a/drivers/clk/qcom/gdsc.h
> +++ b/drivers/clk/qcom/gdsc.h
> @@ -67,6 +67,7 @@ struct gdsc {
>  #define ALWAYS_ON	BIT(6)
>  #define RETAIN_FF_ENABLE	BIT(7)
>  #define NO_RET_PERIPH	BIT(8)
> +#define HW_CTRL_TRIGGER	BIT(9)
>  	struct reset_controller_dev	*rcdev;
>  	unsigned int			*resets;
>  	unsigned int			reset_count;
> 
> -- 
> 2.34.1
>
Bjorn Andersson Jan. 31, 2024, 12:19 a.m. UTC | #2
On Tue, Jan 30, 2024 at 05:00:28PM -0600, Bjorn Andersson wrote:
> On Mon, Jan 22, 2024 at 10:47:03AM +0200, Abel Vesa wrote:
> > From: Jagadeesh Kona <quic_jkona@quicinc.com>
> > 
> > Add support for set and get hwmode callbacks to switch the GDSC between
> > SW and HW modes. Currently, the GDSC is moved to HW control mode
> > using HW_CTRL flag and if this flag is present, GDSC is moved to HW
> > mode as part of GDSC enable itself. The intention is to keep the
> > HW_CTRL flag functionality as is, since many older chipsets still use
> > this flag.
> > 
> 
> This provides insight into why we end up with both HW_CTRL and
> HW_CTRL_TRIGGER. This doesn't describe why this change is needed, but
> rather just an implementation detail.
> 
> > But consumer drivers also require the GDSC mode to be switched dynamically
> > at runtime based on requirement for certain usecases. Some of these
> > usecases are switching the GDSC to SW mode to keep it ON during the
> > enablement of clocks that are dependent on GDSC and while programming
> > certain configurations that require GDSC to be ON. Introduce a new
> > HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev
> > callbacks which allows the consumer drivers to switch the GDSC back and
> > forth between HW/SW modes dynamically at runtime using new
> > dev_pm_genpd_set_hwmode API.
> > 
> 
> This still expresses the need for HW_CTRL_TRIGGER in terms of "some
> drivers need for some use case". We don't need these many words to say:
> "Introduce HW_CTRL_TRIGGER for client drivers that need it."
> 
> 
> I find that it would be useful to document that every time a GDSC is
> turned on the mode will be switched to SW...
> 

I believe I'm wrong here. Reading the patch again, I think we might
retain the mode across a disable/enable cycle. I at least don't see
anything explicit returning us to SW mode.

According to Linux though, the GDSC is off, so as described below, there
will be no votes for supplying resources.

Regards,
Bjorn
Jagadeesh Kona Feb. 13, 2024, 1:04 p.m. UTC | #3
On 1/31/2024 4:30 AM, Bjorn Andersson wrote:
> On Mon, Jan 22, 2024 at 10:47:03AM +0200, Abel Vesa wrote:
>> From: Jagadeesh Kona <quic_jkona@quicinc.com>
>>
>> Add support for set and get hwmode callbacks to switch the GDSC between
>> SW and HW modes. Currently, the GDSC is moved to HW control mode
>> using HW_CTRL flag and if this flag is present, GDSC is moved to HW
>> mode as part of GDSC enable itself. The intention is to keep the
>> HW_CTRL flag functionality as is, since many older chipsets still use
>> this flag.
>>
> 
> This provides insight into why we end up with both HW_CTRL and
> HW_CTRL_TRIGGER. This doesn't describe why this change is needed, but
> rather just an implementation detail.
> 
>> But consumer drivers also require the GDSC mode to be switched dynamically
>> at runtime based on requirement for certain usecases. Some of these
>> usecases are switching the GDSC to SW mode to keep it ON during the
>> enablement of clocks that are dependent on GDSC and while programming
>> certain configurations that require GDSC to be ON. Introduce a new
>> HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev
>> callbacks which allows the consumer drivers to switch the GDSC back and
>> forth between HW/SW modes dynamically at runtime using new
>> dev_pm_genpd_set_hwmode API.
>>
> 
> This still expresses the need for HW_CTRL_TRIGGER in terms of "some
> drivers need for some use case". We don't need these many words to say:
> "Introduce HW_CTRL_TRIGGER for client drivers that need it."
> 

Thanks Bjorn for your review.

Sure will update the commit text to be more precise in next series.

> 
> I find that it would be useful to document that every time a GDSC is
> turned on the mode will be switched to SW...
> 
>> Signed-off-by: Jagadeesh Kona <quic_jkona@quicinc.com>
>> Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
>> ---
>>   drivers/clk/qcom/gdsc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   drivers/clk/qcom/gdsc.h |  1 +
>>   2 files changed, 55 insertions(+)
>>
>> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
>> index 5358e28122ab..71626eb20101 100644
>> --- a/drivers/clk/qcom/gdsc.c
>> +++ b/drivers/clk/qcom/gdsc.c
>> @@ -363,6 +363,56 @@ static int gdsc_disable(struct generic_pm_domain *domain)
>>   	return 0;
>>   }
>>   
>> +static int gdsc_set_hwmode(struct generic_pm_domain *domain, struct device *dev, bool mode)
>> +{
>> +	struct gdsc *sc = domain_to_gdsc(domain);
>> +	u32 val;
>> +	int ret;
>> +
>> +	if (sc->rsupply && !regulator_is_enabled(sc->rsupply)) {
> 
> Why is this a restriction only for GDSCs supplied by regulators? I don't
> find anything preventing this API from being called on GDSCs supplied by
> other genpd instances.

> 
> Also note that regulator_is_enabled() is racy, in that it tells us if
> the regulator is currently turned on, not if we're the one holding that
> vote. As such this might change at any moment - and hence shouldn't be
> significant here.
>
Below is the consumer's sequence that switch the GDSC's b/w HW & SW modes:-
1) Enable the GDSC in SW mode
2) Enable required clocks
3) Switch the GDSC to HW mode using dev_pm_genpd_set_hwmode(true)
4) Usecase start
5) Usecase end
6) Switch the GDSC back to SW mode using dev_pm_genpd_set_hwmode(false)
7) Disable clocks
8) Disable GDSC

Hence the new API dev_pm_genpd_set_hwmode() will always be called 
between gdsc_enable() and gdsc_disable(), which ensures GDSC's parent 
power domain/regulator is ON when this callback is being called. Also, 
we can remove the above regulator_is_enabled() check as well.

>> +		pr_err("Cannot set mode while parent is disabled\n");
>> +		return -EIO;
>> +	}
>> +
>> +	ret = gdsc_hwctrl(sc, mode);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait for 1usec for mode transition to properly complete */
>> +	udelay(1);
>> +
>> +	if (!mode) {
>> +		ret = regmap_read(sc->regmap, sc->gdscr, &val);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/*
>> +		 * While switching from HW to SW mode, if GDSC is in enabled
>> +		 * state, poll for GDSC to complete the power up.
>> +		 */
> 
> I had to give this some thought, to conclude that this is relevant if HW
> has the GDSC disabled and we're switching to SW - which would then
> enable it. I think this comment can be improved slightly, to save the
> reader the need for figuring out this on their own.
> 

Sure, I will improvise the comment in next series.

>> +		if (!(val & SW_COLLAPSE_MASK))
> 
> This not being true, would imply that gdsc_disable() has been called
> already, in which case there's no guarantee that the parent still
> supplies power.
> 
> In the introduced API power on and hw control are orthogonal states, but
> not so in this implementation. This need to made clear, to reduce future
> surprises.
> 

Yes, above SW_COLLAPSE_MASK check is also not required and will remove 
it in next series.

>> +			return gdsc_poll_status(sc, GDSC_ON);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static bool gdsc_get_hwmode(struct generic_pm_domain *domain, struct device *dev)
>> +{
>> +	struct gdsc *sc = domain_to_gdsc(domain);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = regmap_read(sc->regmap, sc->gdscr, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val & HW_CONTROL_MASK)
>> +		return true;
>> +
>> +	return false;
> 
> return !!(val & HW_CONTROL_MASK);
> 

Sure, will update this in the next series.

> Regards,
> Bjorn
> 
>> +}
>> +
>>   static int gdsc_init(struct gdsc *sc)
>>   {
>>   	u32 mask, val;
>> @@ -451,6 +501,10 @@ static int gdsc_init(struct gdsc *sc)
>>   		sc->pd.power_off = gdsc_disable;
>>   	if (!sc->pd.power_on)
>>   		sc->pd.power_on = gdsc_enable;
>> +	if (sc->flags & HW_CTRL_TRIGGER) {
>> +		sc->pd.set_hwmode_dev = gdsc_set_hwmode;
>> +		sc->pd.get_hwmode_dev = gdsc_get_hwmode;
>> +	}
>>   
>>   	ret = pm_genpd_init(&sc->pd, NULL, !on);
>>   	if (ret)
>> diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
>> index 803512688336..1e2779b823d1 100644
>> --- a/drivers/clk/qcom/gdsc.h
>> +++ b/drivers/clk/qcom/gdsc.h
>> @@ -67,6 +67,7 @@ struct gdsc {
>>   #define ALWAYS_ON	BIT(6)
>>   #define RETAIN_FF_ENABLE	BIT(7)
>>   #define NO_RET_PERIPH	BIT(8)
>> +#define HW_CTRL_TRIGGER	BIT(9)
>>   	struct reset_controller_dev	*rcdev;
>>   	unsigned int			*resets;
>>   	unsigned int			reset_count;
>>
>> -- 
>> 2.34.1
>>
>
Jagadeesh Kona Feb. 13, 2024, 1:08 p.m. UTC | #4
On 1/31/2024 5:49 AM, Bjorn Andersson wrote:
> On Tue, Jan 30, 2024 at 05:00:28PM -0600, Bjorn Andersson wrote:
>> On Mon, Jan 22, 2024 at 10:47:03AM +0200, Abel Vesa wrote:
>>> From: Jagadeesh Kona <quic_jkona@quicinc.com>
>>>
>>> Add support for set and get hwmode callbacks to switch the GDSC between
>>> SW and HW modes. Currently, the GDSC is moved to HW control mode
>>> using HW_CTRL flag and if this flag is present, GDSC is moved to HW
>>> mode as part of GDSC enable itself. The intention is to keep the
>>> HW_CTRL flag functionality as is, since many older chipsets still use
>>> this flag.
>>>
>>
>> This provides insight into why we end up with both HW_CTRL and
>> HW_CTRL_TRIGGER. This doesn't describe why this change is needed, but
>> rather just an implementation detail.
>>
>>> But consumer drivers also require the GDSC mode to be switched dynamically
>>> at runtime based on requirement for certain usecases. Some of these
>>> usecases are switching the GDSC to SW mode to keep it ON during the
>>> enablement of clocks that are dependent on GDSC and while programming
>>> certain configurations that require GDSC to be ON. Introduce a new
>>> HW_CTRL_TRIGGER flag to register the set_hwmode_dev and get_hwmode_dev
>>> callbacks which allows the consumer drivers to switch the GDSC back and
>>> forth between HW/SW modes dynamically at runtime using new
>>> dev_pm_genpd_set_hwmode API.
>>>
>>
>> This still expresses the need for HW_CTRL_TRIGGER in terms of "some
>> drivers need for some use case". We don't need these many words to say:
>> "Introduce HW_CTRL_TRIGGER for client drivers that need it."
>>
>>
>> I find that it would be useful to document that every time a GDSC is
>> turned on the mode will be switched to SW...
>>
> 
> I believe I'm wrong here. Reading the patch again, I think we might
> retain the mode across a disable/enable cycle. I at least don't see
> anything explicit returning us to SW mode.
> > According to Linux though, the GDSC is off, so as described below, there
> will be no votes for supplying resources.
> 

Yes, With HW_CTRL_TRIGGER flag, the GDSC mode gets switched only in new 
set_hwmode_dev() callback when consumers explicitly call for it. This is 
to ensure GenPD f/w's hwmode aligns with the actual GDSC mode in HW.

With the new API, the expectation is consumer drivers should move the 
GDSC to SW control mode before disabling the GDSC, which ensures that, 
GDSC is turned ON in SW mode every time.

Thanks,
Jagadeesh

> Regards,
> Bjorn
diff mbox series

Patch

diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index 5358e28122ab..71626eb20101 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -363,6 +363,56 @@  static int gdsc_disable(struct generic_pm_domain *domain)
 	return 0;
 }
 
+static int gdsc_set_hwmode(struct generic_pm_domain *domain, struct device *dev, bool mode)
+{
+	struct gdsc *sc = domain_to_gdsc(domain);
+	u32 val;
+	int ret;
+
+	if (sc->rsupply && !regulator_is_enabled(sc->rsupply)) {
+		pr_err("Cannot set mode while parent is disabled\n");
+		return -EIO;
+	}
+
+	ret = gdsc_hwctrl(sc, mode);
+	if (ret)
+		return ret;
+
+	/* Wait for 1usec for mode transition to properly complete */
+	udelay(1);
+
+	if (!mode) {
+		ret = regmap_read(sc->regmap, sc->gdscr, &val);
+		if (ret)
+			return ret;
+
+		/*
+		 * While switching from HW to SW mode, if GDSC is in enabled
+		 * state, poll for GDSC to complete the power up.
+		 */
+		if (!(val & SW_COLLAPSE_MASK))
+			return gdsc_poll_status(sc, GDSC_ON);
+	}
+
+	return 0;
+}
+
+static bool gdsc_get_hwmode(struct generic_pm_domain *domain, struct device *dev)
+{
+	struct gdsc *sc = domain_to_gdsc(domain);
+	u32 val;
+	int ret;
+
+	ret = regmap_read(sc->regmap, sc->gdscr, &val);
+	if (ret)
+		return ret;
+
+	if (val & HW_CONTROL_MASK)
+		return true;
+
+	return false;
+}
+
 static int gdsc_init(struct gdsc *sc)
 {
 	u32 mask, val;
@@ -451,6 +501,10 @@  static int gdsc_init(struct gdsc *sc)
 		sc->pd.power_off = gdsc_disable;
 	if (!sc->pd.power_on)
 		sc->pd.power_on = gdsc_enable;
+	if (sc->flags & HW_CTRL_TRIGGER) {
+		sc->pd.set_hwmode_dev = gdsc_set_hwmode;
+		sc->pd.get_hwmode_dev = gdsc_get_hwmode;
+	}
 
 	ret = pm_genpd_init(&sc->pd, NULL, !on);
 	if (ret)
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 803512688336..1e2779b823d1 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -67,6 +67,7 @@  struct gdsc {
 #define ALWAYS_ON	BIT(6)
 #define RETAIN_FF_ENABLE	BIT(7)
 #define NO_RET_PERIPH	BIT(8)
+#define HW_CTRL_TRIGGER	BIT(9)
 	struct reset_controller_dev	*rcdev;
 	unsigned int			*resets;
 	unsigned int			reset_count;