diff mbox series

[RESEND,2/2] soc: qcom: rpmhpd: Make power_on actually enable the domain

Message ID 20210703025449.2687201-1-bjorn.andersson@linaro.org (mailing list archive)
State Changes Requested
Headers show
Series None | expand

Commit Message

Bjorn Andersson July 3, 2021, 2:54 a.m. UTC
The general expectation is that powering on a power-domain should make
the power domain deliver some power, and if a specific performace state
is needed further requests has to be made.

But in contrast with other power-domain implementations (e.g. rpmpd) the
RPMh does not have an interface to enable the power, so the driver has
to vote for a particular corner (performance level) in rpmh_power_on().

But the corner is never initialized, so a typical request to simply
enable the power domain would not actually turn on the hardware. Further
more, when no more clients vote for a performance state (i.e. the
aggregated vote is 0) the power domain would be turn off.

Fix both of these issues by always voting for a corner with non-zero
value, when the power domain is enabled.

The tracking of the lowest non-zero corner is performed to handle the
corner case if there's ever a domain with a non-zero lowest corner, in
which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
would be allowed to use this lowest corner.

Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---

Resending because the hunk in rpmhpd_update_level_mapping() was left in the
index.

 drivers/soc/qcom/rpmhpd.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

Comments

Stephen Boyd July 8, 2021, 12:23 a.m. UTC | #1
Quoting Bjorn Andersson (2021-07-02 19:54:49)
> The general expectation is that powering on a power-domain should make
> the power domain deliver some power, and if a specific performace state
> is needed further requests has to be made.
>
> But in contrast with other power-domain implementations (e.g. rpmpd) the
> RPMh does not have an interface to enable the power, so the driver has
> to vote for a particular corner (performance level) in rpmh_power_on().
>
> But the corner is never initialized, so a typical request to simply
> enable the power domain would not actually turn on the hardware. Further
> more, when no more clients vote for a performance state (i.e. the
> aggregated vote is 0) the power domain would be turn off.

s/turn/turned/

>
> Fix both of these issues by always voting for a corner with non-zero
> value, when the power domain is enabled.
>
> The tracking of the lowest non-zero corner is performed to handle the
> corner case if there's ever a domain with a non-zero lowest corner, in
> which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
> would be allowed to use this lowest corner.
>
> Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---

Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Stephen Boyd July 8, 2021, 12:25 a.m. UTC | #2
Quoting Bjorn Andersson (2021-07-02 19:54:49)
> @@ -472,6 +478,10 @@ static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
>         for (i = 0; i < rpmhpd->level_count; i++) {
>                 rpmhpd->level[i] = buf[i];

BTW, this looks like it should actually be an __le16 pointer and then we
should do le16_to_cpup() here. Hooray for void pointers.

>
> +               /* Remember the first non-zero corner */
> +               if (!rpmhpd->enable_corner)
> +                       rpmhpd->enable_corner = i;
> +
Rajendra Nayak July 14, 2021, 9:22 a.m. UTC | #3
On 7/3/2021 8:24 AM, Bjorn Andersson wrote:
> The general expectation is that powering on a power-domain should make
> the power domain deliver some power, and if a specific performace state
> is needed further requests has to be made.
> 
> But in contrast with other power-domain implementations (e.g. rpmpd) the
> RPMh does not have an interface to enable the power, so the driver has
> to vote for a particular corner (performance level) in rpmh_power_on().
> 
> But the corner is never initialized, so a typical request to simply
> enable the power domain would not actually turn on the hardware. Further
> more, when no more clients vote for a performance state (i.e. the
> aggregated vote is 0) the power domain would be turn off.
> 
> Fix both of these issues by always voting for a corner with non-zero
> value, when the power domain is enabled.
> 
> The tracking of the lowest non-zero corner is performed to handle the
> corner case if there's ever a domain with a non-zero lowest corner, in
> which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
> would be allowed to use this lowest corner.
> 
> Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

Did some testing on sc7180 and sc7280 devices, no regressions seen,
Reviewed-by: Rajendra Nayak <rnayak@codeaurora.org>
Tested-by: Rajendra Nayak <rnayak@codeaurora.org>

> ---
> 
> Resending because the hunk in rpmhpd_update_level_mapping() was left in the
> index.
> 
>   drivers/soc/qcom/rpmhpd.c | 18 ++++++++++++++----
>   1 file changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
> index fa209b479ab3..76ea6b053ef0 100644
> --- a/drivers/soc/qcom/rpmhpd.c
> +++ b/drivers/soc/qcom/rpmhpd.c
> @@ -30,6 +30,7 @@
>    * @active_only:	True if it represents an Active only peer
>    * @corner:		current corner
>    * @active_corner:	current active corner
> + * @enable_corner:	lowest non-zero corner
>    * @level:		An array of level (vlvl) to corner (hlvl) mappings
>    *			derived from cmd-db
>    * @level_count:	Number of levels supported by the power domain. max
> @@ -47,6 +48,7 @@ struct rpmhpd {
>   	const bool	active_only;
>   	unsigned int	corner;
>   	unsigned int	active_corner;
> +	unsigned int	enable_corner;
>   	u32		level[RPMH_ARC_MAX_LEVELS];
>   	size_t		level_count;
>   	bool		enabled;
> @@ -385,13 +387,13 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
>   static int rpmhpd_power_on(struct generic_pm_domain *domain)
>   {
>   	struct rpmhpd *pd = domain_to_rpmhpd(domain);
> -	int ret = 0;
> +	unsigned int corner;
> +	int ret;
>   
>   	mutex_lock(&rpmhpd_lock);
>   
> -	if (pd->corner)
> -		ret = rpmhpd_aggregate_corner(pd, pd->corner);
> -
> +	corner = max(pd->corner, pd->enable_corner);
> +	ret = rpmhpd_aggregate_corner(pd, corner);
>   	if (!ret)
>   		pd->enabled = true;
>   
> @@ -436,6 +438,10 @@ static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
>   		i--;
>   
>   	if (pd->enabled) {
> +		/* Ensure that the domain isn't turn off */
> +		if (i < pd->enable_corner)
> +			i = pd->enable_corner;
> +
>   		ret = rpmhpd_aggregate_corner(pd, i);
>   		if (ret)
>   			goto out;
> @@ -472,6 +478,10 @@ static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
>   	for (i = 0; i < rpmhpd->level_count; i++) {
>   		rpmhpd->level[i] = buf[i];
>   
> +		/* Remember the first non-zero corner */
> +		if (!rpmhpd->enable_corner)
> +			rpmhpd->enable_corner = i;
> +
>   		/*
>   		 * The AUX data may be zero padded.  These 0 valued entries at
>   		 * the end of the map must be ignored.
>
Sibi Sankar July 15, 2021, 12:16 p.m. UTC | #4
Hey Bjorn,

Thanks for the patch.

On 2021-07-03 08:24, Bjorn Andersson wrote:
> The general expectation is that powering on a power-domain should make
> the power domain deliver some power, and if a specific performace state

s/performace/performance/

> is needed further requests has to be made.
> 
> But in contrast with other power-domain implementations (e.g. rpmpd) 
> the
> RPMh does not have an interface to enable the power, so the driver has
> to vote for a particular corner (performance level) in rpmh_power_on().
> 
> But the corner is never initialized, so a typical request to simply
> enable the power domain would not actually turn on the hardware. 
> Further
> more, when no more clients vote for a performance state (i.e. the
> aggregated vote is 0) the power domain would be turn off.
> 
> Fix both of these issues by always voting for a corner with non-zero
> value, when the power domain is enabled.
> 
> The tracking of the lowest non-zero corner is performed to handle the
> corner case if there's ever a domain with a non-zero lowest corner, in
> which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
> would be allowed to use this lowest corner.
> 
> Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
> 
> Resending because the hunk in rpmhpd_update_level_mapping() was left in 
> the
> index.
> 
>  drivers/soc/qcom/rpmhpd.c | 18 ++++++++++++++----
>  1 file changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
> index fa209b479ab3..76ea6b053ef0 100644
> --- a/drivers/soc/qcom/rpmhpd.c
> +++ b/drivers/soc/qcom/rpmhpd.c
> @@ -30,6 +30,7 @@
>   * @active_only:	True if it represents an Active only peer
>   * @corner:		current corner
>   * @active_corner:	current active corner
> + * @enable_corner:	lowest non-zero corner
>   * @level:		An array of level (vlvl) to corner (hlvl) mappings
>   *			derived from cmd-db
>   * @level_count:	Number of levels supported by the power domain. max
> @@ -47,6 +48,7 @@ struct rpmhpd {
>  	const bool	active_only;
>  	unsigned int	corner;
>  	unsigned int	active_corner;
> +	unsigned int	enable_corner;
>  	u32		level[RPMH_ARC_MAX_LEVELS];
>  	size_t		level_count;
>  	bool		enabled;
> @@ -385,13 +387,13 @@ static int rpmhpd_aggregate_corner(struct rpmhpd
> *pd, unsigned int corner)
>  static int rpmhpd_power_on(struct generic_pm_domain *domain)
>  {
>  	struct rpmhpd *pd = domain_to_rpmhpd(domain);
> -	int ret = 0;
> +	unsigned int corner;
> +	int ret;
> 
>  	mutex_lock(&rpmhpd_lock);
> 
> -	if (pd->corner)
> -		ret = rpmhpd_aggregate_corner(pd, pd->corner);
> -
> +	corner = max(pd->corner, pd->enable_corner);
> +	ret = rpmhpd_aggregate_corner(pd, corner);
>  	if (!ret)
>  		pd->enabled = true;
> 
> @@ -436,6 +438,10 @@ static int rpmhpd_set_performance_state(struct
> generic_pm_domain *domain,
>  		i--;
> 
>  	if (pd->enabled) {
> +		/* Ensure that the domain isn't turn off */
> +		if (i < pd->enable_corner)
> +			i = pd->enable_corner;
> +
>  		ret = rpmhpd_aggregate_corner(pd, i);
>  		if (ret)
>  			goto out;
> @@ -472,6 +478,10 @@ static int rpmhpd_update_level_mapping(struct
> rpmhpd *rpmhpd)
>  	for (i = 0; i < rpmhpd->level_count; i++) {
>  		rpmhpd->level[i] = buf[i];
> 
> +		/* Remember the first non-zero corner */

Shouldn't we be tracking the corner that
corresponds to the first non-zero level
instead?

> +		if (!rpmhpd->enable_corner)
> +			rpmhpd->enable_corner = i;
> +
>  		/*
>  		 * The AUX data may be zero padded.  These 0 valued entries at
>  		 * the end of the map must be ignored.
Rajendra Nayak July 15, 2021, 12:24 p.m. UTC | #5
On 7/15/2021 5:46 PM, Sibi Sankar wrote:
> Hey Bjorn,
> 
> Thanks for the patch.
> 
> On 2021-07-03 08:24, Bjorn Andersson wrote:
>> The general expectation is that powering on a power-domain should make
>> the power domain deliver some power, and if a specific performace state
> 
> s/performace/performance/
> 
>> is needed further requests has to be made.
>>
>> But in contrast with other power-domain implementations (e.g. rpmpd) the
>> RPMh does not have an interface to enable the power, so the driver has
>> to vote for a particular corner (performance level) in rpmh_power_on().
>>
>> But the corner is never initialized, so a typical request to simply
>> enable the power domain would not actually turn on the hardware. Further
>> more, when no more clients vote for a performance state (i.e. the
>> aggregated vote is 0) the power domain would be turn off.
>>
>> Fix both of these issues by always voting for a corner with non-zero
>> value, when the power domain is enabled.
>>
>> The tracking of the lowest non-zero corner is performed to handle the
>> corner case if there's ever a domain with a non-zero lowest corner, in
>> which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
>> would be allowed to use this lowest corner.
>>
>> Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
>> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
>> ---
>>
>> Resending because the hunk in rpmhpd_update_level_mapping() was left in the
>> index.
>>
>>  drivers/soc/qcom/rpmhpd.c | 18 ++++++++++++++----
>>  1 file changed, 14 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
>> index fa209b479ab3..76ea6b053ef0 100644
>> --- a/drivers/soc/qcom/rpmhpd.c
>> +++ b/drivers/soc/qcom/rpmhpd.c
>> @@ -30,6 +30,7 @@
>>   * @active_only:    True if it represents an Active only peer
>>   * @corner:        current corner
>>   * @active_corner:    current active corner
>> + * @enable_corner:    lowest non-zero corner
>>   * @level:        An array of level (vlvl) to corner (hlvl) mappings
>>   *            derived from cmd-db
>>   * @level_count:    Number of levels supported by the power domain. max
>> @@ -47,6 +48,7 @@ struct rpmhpd {
>>      const bool    active_only;
>>      unsigned int    corner;
>>      unsigned int    active_corner;
>> +    unsigned int    enable_corner;
>>      u32        level[RPMH_ARC_MAX_LEVELS];
>>      size_t        level_count;
>>      bool        enabled;
>> @@ -385,13 +387,13 @@ static int rpmhpd_aggregate_corner(struct rpmhpd
>> *pd, unsigned int corner)
>>  static int rpmhpd_power_on(struct generic_pm_domain *domain)
>>  {
>>      struct rpmhpd *pd = domain_to_rpmhpd(domain);
>> -    int ret = 0;
>> +    unsigned int corner;
>> +    int ret;
>>
>>      mutex_lock(&rpmhpd_lock);
>>
>> -    if (pd->corner)
>> -        ret = rpmhpd_aggregate_corner(pd, pd->corner);
>> -
>> +    corner = max(pd->corner, pd->enable_corner);
>> +    ret = rpmhpd_aggregate_corner(pd, corner);
>>      if (!ret)
>>          pd->enabled = true;
>>
>> @@ -436,6 +438,10 @@ static int rpmhpd_set_performance_state(struct
>> generic_pm_domain *domain,
>>          i--;
>>
>>      if (pd->enabled) {
>> +        /* Ensure that the domain isn't turn off */
>> +        if (i < pd->enable_corner)
>> +            i = pd->enable_corner;
>> +
>>          ret = rpmhpd_aggregate_corner(pd, i);
>>          if (ret)
>>              goto out;
>> @@ -472,6 +478,10 @@ static int rpmhpd_update_level_mapping(struct
>> rpmhpd *rpmhpd)
>>      for (i = 0; i < rpmhpd->level_count; i++) {
>>          rpmhpd->level[i] = buf[i];
>>
>> +        /* Remember the first non-zero corner */
> 
> Shouldn't we be tracking the corner that
> corresponds to the first non-zero level
> instead?

Thats correct, thanks for spotting this, the first non-zero
corner is always 1 :)

> 
>> +        if (!rpmhpd->enable_corner)
>> +            rpmhpd->enable_corner = i;
>> +
>>          /*
>>           * The AUX data may be zero padded.  These 0 valued entries at
>>           * the end of the map must be ignored.
>
Dmitry Baryshkov Aug. 12, 2021, 1:21 p.m. UTC | #6
On 03/07/2021 05:54, Bjorn Andersson wrote:
> The general expectation is that powering on a power-domain should make
> the power domain deliver some power, and if a specific performace state
> is needed further requests has to be made.
> 
> But in contrast with other power-domain implementations (e.g. rpmpd) the
> RPMh does not have an interface to enable the power, so the driver has
> to vote for a particular corner (performance level) in rpmh_power_on().
> 
> But the corner is never initialized, so a typical request to simply
> enable the power domain would not actually turn on the hardware. Further
> more, when no more clients vote for a performance state (i.e. the
> aggregated vote is 0) the power domain would be turn off.
> 
> Fix both of these issues by always voting for a corner with non-zero
> value, when the power domain is enabled.
> 
> The tracking of the lowest non-zero corner is performed to handle the
> corner case if there's ever a domain with a non-zero lowest corner, in
> which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
> would be allowed to use this lowest corner.
> 
> Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
> 
> Resending because the hunk in rpmhpd_update_level_mapping() was left in the
> index.

So, colleagues, what is the fate of this patch? Is it going to be 
applied? Or we agree that current approach (power_on + 
set_performance_state) is the expected behaviour? My patches on gdsc 
rework depend on this patch, but I can rework in them in favour of 
required-opp approach.

> 
>   drivers/soc/qcom/rpmhpd.c | 18 ++++++++++++++----
>   1 file changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
> index fa209b479ab3..76ea6b053ef0 100644
> --- a/drivers/soc/qcom/rpmhpd.c
> +++ b/drivers/soc/qcom/rpmhpd.c
> @@ -30,6 +30,7 @@
>    * @active_only:	True if it represents an Active only peer
>    * @corner:		current corner
>    * @active_corner:	current active corner
> + * @enable_corner:	lowest non-zero corner
>    * @level:		An array of level (vlvl) to corner (hlvl) mappings
>    *			derived from cmd-db
>    * @level_count:	Number of levels supported by the power domain. max
> @@ -47,6 +48,7 @@ struct rpmhpd {
>   	const bool	active_only;
>   	unsigned int	corner;
>   	unsigned int	active_corner;
> +	unsigned int	enable_corner;
>   	u32		level[RPMH_ARC_MAX_LEVELS];
>   	size_t		level_count;
>   	bool		enabled;
> @@ -385,13 +387,13 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
>   static int rpmhpd_power_on(struct generic_pm_domain *domain)
>   {
>   	struct rpmhpd *pd = domain_to_rpmhpd(domain);
> -	int ret = 0;
> +	unsigned int corner;
> +	int ret;
>   
>   	mutex_lock(&rpmhpd_lock);
>   
> -	if (pd->corner)
> -		ret = rpmhpd_aggregate_corner(pd, pd->corner);
> -
> +	corner = max(pd->corner, pd->enable_corner);
> +	ret = rpmhpd_aggregate_corner(pd, corner);
>   	if (!ret)
>   		pd->enabled = true;
>   
> @@ -436,6 +438,10 @@ static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
>   		i--;
>   
>   	if (pd->enabled) {
> +		/* Ensure that the domain isn't turn off */
> +		if (i < pd->enable_corner)
> +			i = pd->enable_corner;
> +
>   		ret = rpmhpd_aggregate_corner(pd, i);
>   		if (ret)
>   			goto out;
> @@ -472,6 +478,10 @@ static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
>   	for (i = 0; i < rpmhpd->level_count; i++) {
>   		rpmhpd->level[i] = buf[i];
>   
> +		/* Remember the first non-zero corner */
> +		if (!rpmhpd->enable_corner)
> +			rpmhpd->enable_corner = i;
> +
>   		/*
>   		 * The AUX data may be zero padded.  These 0 valued entries at
>   		 * the end of the map must be ignored.
>
Ulf Hansson Aug. 13, 2021, 9:45 a.m. UTC | #7
On Thu, 12 Aug 2021 at 15:21, Dmitry Baryshkov
<dmitry.baryshkov@linaro.org> wrote:
>
> On 03/07/2021 05:54, Bjorn Andersson wrote:
> > The general expectation is that powering on a power-domain should make
> > the power domain deliver some power, and if a specific performace state
> > is needed further requests has to be made.
> >
> > But in contrast with other power-domain implementations (e.g. rpmpd) the
> > RPMh does not have an interface to enable the power, so the driver has
> > to vote for a particular corner (performance level) in rpmh_power_on().
> >
> > But the corner is never initialized, so a typical request to simply
> > enable the power domain would not actually turn on the hardware. Further
> > more, when no more clients vote for a performance state (i.e. the
> > aggregated vote is 0) the power domain would be turn off.
> >
> > Fix both of these issues by always voting for a corner with non-zero
> > value, when the power domain is enabled.
> >
> > The tracking of the lowest non-zero corner is performed to handle the
> > corner case if there's ever a domain with a non-zero lowest corner, in
> > which case both rpmh_power_on() and rpmh_rpmhpd_set_performance_state()
> > would be allowed to use this lowest corner.
> >
> > Fixes: 279b7e8a62cc ("soc: qcom: rpmhpd: Add RPMh power domain driver")
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> > ---
> >
> > Resending because the hunk in rpmhpd_update_level_mapping() was left in the
> > index.
>
> So, colleagues, what is the fate of this patch? Is it going to be
> applied? Or we agree that current approach (power_on +
> set_performance_state) is the expected behaviour? My patches on gdsc
> rework depend on this patch, but I can rework in them in favour of
> required-opp approach.

Today, genpd treats performance states and power on/off states as
orthogonal. You know this already, ofcourse.

Although, to clarify, this means that the genpd provider has to deal
with the scenario when its ->set_performance_state() callback may be
invoked, while the PM domain is turned off, for example. Similarly,
genpd may power on the PM domain by invoking the ->power_on()
callback, before the ->set_performance_state() has been invoked. And
finally, the power domain may be turned off even if there are some
active votes for a performance state.

So for now, the genpd provider needs to deal with these cases. Yes, we
have discussed changing the behaviour in genpd around this and I think
there have been some good reasons for it, but we are not there, at
least yet.

[...]

Kind regards
Uffe
diff mbox series

Patch

diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
index fa209b479ab3..76ea6b053ef0 100644
--- a/drivers/soc/qcom/rpmhpd.c
+++ b/drivers/soc/qcom/rpmhpd.c
@@ -30,6 +30,7 @@ 
  * @active_only:	True if it represents an Active only peer
  * @corner:		current corner
  * @active_corner:	current active corner
+ * @enable_corner:	lowest non-zero corner
  * @level:		An array of level (vlvl) to corner (hlvl) mappings
  *			derived from cmd-db
  * @level_count:	Number of levels supported by the power domain. max
@@ -47,6 +48,7 @@  struct rpmhpd {
 	const bool	active_only;
 	unsigned int	corner;
 	unsigned int	active_corner;
+	unsigned int	enable_corner;
 	u32		level[RPMH_ARC_MAX_LEVELS];
 	size_t		level_count;
 	bool		enabled;
@@ -385,13 +387,13 @@  static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
 static int rpmhpd_power_on(struct generic_pm_domain *domain)
 {
 	struct rpmhpd *pd = domain_to_rpmhpd(domain);
-	int ret = 0;
+	unsigned int corner;
+	int ret;
 
 	mutex_lock(&rpmhpd_lock);
 
-	if (pd->corner)
-		ret = rpmhpd_aggregate_corner(pd, pd->corner);
-
+	corner = max(pd->corner, pd->enable_corner);
+	ret = rpmhpd_aggregate_corner(pd, corner);
 	if (!ret)
 		pd->enabled = true;
 
@@ -436,6 +438,10 @@  static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
 		i--;
 
 	if (pd->enabled) {
+		/* Ensure that the domain isn't turn off */
+		if (i < pd->enable_corner)
+			i = pd->enable_corner;
+
 		ret = rpmhpd_aggregate_corner(pd, i);
 		if (ret)
 			goto out;
@@ -472,6 +478,10 @@  static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
 	for (i = 0; i < rpmhpd->level_count; i++) {
 		rpmhpd->level[i] = buf[i];
 
+		/* Remember the first non-zero corner */
+		if (!rpmhpd->enable_corner)
+			rpmhpd->enable_corner = i;
+
 		/*
 		 * The AUX data may be zero padded.  These 0 valued entries at
 		 * the end of the map must be ignored.