diff mbox

[3/3] clk: qcom: gdsc: Add support to poll CFG register to check GDSC state

Message ID 1522665945-28147-4-git-send-email-tdas@codeaurora.org (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

Taniya Das April 2, 2018, 10:45 a.m. UTC
From: Amit Nischal <anischal@codeaurora.org>

The default behavior of the GDSC enable/disable sequence is to
poll the status bits of either the actual GDSCR or the
corresponding HW_CTRL registers.

On targets which have support for a CFG_GDSCR register, the
status bits might not show the correct state of the GDSC,
especially in the disable sequence, where the status bit
will be cleared even before the core is completely power
collapsed. On targets with this issue, poll the power on/off
bits in the CFG_GDSCR register instead to correctly determine
the GDSC state.

Signed-off-by: Amit Nischal <anischal@codeaurora.org>
Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 drivers/clk/qcom/gdsc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/clk/qcom/gdsc.h |  1 +
 2 files changed, 47 insertions(+), 2 deletions(-)

--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Stephen Boyd April 6, 2018, 4:40 p.m. UTC | #1
Quoting Taniya Das (2018-04-02 03:45:45)
> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
> index e89584e..e0c83ba 100644
> --- a/drivers/clk/qcom/gdsc.c
> +++ b/drivers/clk/qcom/gdsc.c
> @@ -83,6 +88,38 @@ static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en)
>         return -ETIMEDOUT;
>  }
> 
> +static int gdsc_is_enabled_by_poll_cfg_reg(struct gdsc *sc, bool en)
> +{
> +       u32 val;
> +       int ret;
> +
> +       ret = regmap_read(sc->regmap, sc->gdscr + CFG_GDSCR_OFFSET, &val);
> +       if (ret)
> +               return ret;
> +
> +       if (en)
> +               return !!(val & GDSC_POWER_UP_COMPLETE);
> +       else
> +               return !(val & GDSC_POWER_DOWN_COMPLETE);

Make this into

	if (en)
		return ...

	return ...

> +}
> +
> +static int gdsc_poll_cfg_status(struct gdsc *sc, bool en)
> +{
> +       ktime_t start = ktime_get();
> +       ktime_t timeout =
> +               (sc->flags & GDS_TIMEOUT) ? TIMEOUT_US_GDS : TIMEOUT_US;
> +
> +       do {
> +               if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
> +                       return 0;
> +       } while (ktime_us_delta(ktime_get(), start) < timeout);
> +
> +       if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
> +               return 0;
> +
> +       return -ETIMEDOUT;
> +}
> +
>  static int gdsc_toggle_logic(struct gdsc *sc, bool en)
>  {
>         int ret;
> @@ -106,6 +143,9 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)
>                 return 0;
>         }
> 
> +       if (sc->flags & POLL_CFG_GDSCR)
> +               return gdsc_poll_cfg_status(sc, en);
> +
>         if (sc->gds_hw_ctrl) {
>                 status_reg = sc->gds_hw_ctrl;
>                 /*
> @@ -258,8 +298,12 @@ static int gdsc_disable(struct generic_pm_domain *domain)
>                  */
>                 udelay(1);
> 
> -               reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
> -               ret = gdsc_poll_status(sc, reg, true);
> +               if (sc->flags & POLL_CFG_GDSCR) {
> +                       ret = gdsc_poll_cfg_status(sc, true);
> +               } else {
> +                       reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
> +                       ret = gdsc_poll_status(sc, reg, true);
> +               }

Maybe this can be pushed into the gdsc_poll_status() function so that
we can keep the "how do we poll status bit" logic in one place.

>                 if (ret)
>                         return ret;
>         }
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Taniya Das April 9, 2018, 8:41 a.m. UTC | #2
Hello Stephen,

Thanks for the review comments.

On 4/6/2018 10:10 PM, Stephen Boyd wrote:
> Quoting Taniya Das (2018-04-02 03:45:45)
>> diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
>> index e89584e..e0c83ba 100644
>> --- a/drivers/clk/qcom/gdsc.c
>> +++ b/drivers/clk/qcom/gdsc.c
>> @@ -83,6 +88,38 @@ static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en)
>>          return -ETIMEDOUT;
>>   }
>>
>> +static int gdsc_is_enabled_by_poll_cfg_reg(struct gdsc *sc, bool en)
>> +{
>> +       u32 val;
>> +       int ret;
>> +
>> +       ret = regmap_read(sc->regmap, sc->gdscr + CFG_GDSCR_OFFSET, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (en)
>> +               return !!(val & GDSC_POWER_UP_COMPLETE);
>> +       else
>> +               return !(val & GDSC_POWER_DOWN_COMPLETE);
> 
> Make this into
> 
> 	if (en)
> 		return ...
> 
> 	return ...
> 

Will fix this in the next series.

>> +}
>> +
>> +static int gdsc_poll_cfg_status(struct gdsc *sc, bool en)
>> +{
>> +       ktime_t start = ktime_get();
>> +       ktime_t timeout =
>> +               (sc->flags & GDS_TIMEOUT) ? TIMEOUT_US_GDS : TIMEOUT_US;
>> +
>> +       do {
>> +               if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
>> +                       return 0;
>> +       } while (ktime_us_delta(ktime_get(), start) < timeout);
>> +
>> +       if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
>> +               return 0;
>> +
>> +       return -ETIMEDOUT;
>> +}
>> +
>>   static int gdsc_toggle_logic(struct gdsc *sc, bool en)
>>   {
>>          int ret;
>> @@ -106,6 +143,9 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)
>>                  return 0;
>>          }
>>
>> +       if (sc->flags & POLL_CFG_GDSCR)
>> +               return gdsc_poll_cfg_status(sc, en);
>> +
>>          if (sc->gds_hw_ctrl) {
>>                  status_reg = sc->gds_hw_ctrl;
>>                  /*
>> @@ -258,8 +298,12 @@ static int gdsc_disable(struct generic_pm_domain *domain)
>>                   */
>>                  udelay(1);
>>
>> -               reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
>> -               ret = gdsc_poll_status(sc, reg, true);
>> +               if (sc->flags & POLL_CFG_GDSCR) {
>> +                       ret = gdsc_poll_cfg_status(sc, true);
>> +               } else {
>> +                       reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
>> +                       ret = gdsc_poll_status(sc, reg, true);
>> +               }
> 
> Maybe this can be pushed into the gdsc_poll_status() function so that
> we can keep the "how do we poll status bit" logic in one place.
> 

Yes, I will push the logic in the gdsc_poll_status() in the next series.

>>                  if (ret)
>>                          return ret;
>>          }
diff mbox

Patch

diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index e89584e..e0c83ba 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -33,6 +33,11 @@ 
 #define GMEM_CLAMP_IO_MASK	BIT(0)
 #define GMEM_RESET_MASK		BIT(4)

+/* CFG_GDSCR */
+#define GDSC_POWER_UP_COMPLETE		BIT(16)
+#define GDSC_POWER_DOWN_COMPLETE	BIT(15)
+#define CFG_GDSCR_OFFSET		0x4
+
 /* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
 #define EN_REST_WAIT_VAL	(0x2 << 20)
 #define EN_FEW_WAIT_VAL		(0x8 << 16)
@@ -83,6 +88,38 @@  static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en)
 	return -ETIMEDOUT;
 }

+static int gdsc_is_enabled_by_poll_cfg_reg(struct gdsc *sc, bool en)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(sc->regmap, sc->gdscr + CFG_GDSCR_OFFSET, &val);
+	if (ret)
+		return ret;
+
+	if (en)
+		return !!(val & GDSC_POWER_UP_COMPLETE);
+	else
+		return !(val & GDSC_POWER_DOWN_COMPLETE);
+}
+
+static int gdsc_poll_cfg_status(struct gdsc *sc, bool en)
+{
+	ktime_t start = ktime_get();
+	ktime_t timeout =
+		(sc->flags & GDS_TIMEOUT) ? TIMEOUT_US_GDS : TIMEOUT_US;
+
+	do {
+		if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
+			return 0;
+	} while (ktime_us_delta(ktime_get(), start) < timeout);
+
+	if (gdsc_is_enabled_by_poll_cfg_reg(sc, en) == en)
+		return 0;
+
+	return -ETIMEDOUT;
+}
+
 static int gdsc_toggle_logic(struct gdsc *sc, bool en)
 {
 	int ret;
@@ -106,6 +143,9 @@  static int gdsc_toggle_logic(struct gdsc *sc, bool en)
 		return 0;
 	}

+	if (sc->flags & POLL_CFG_GDSCR)
+		return gdsc_poll_cfg_status(sc, en);
+
 	if (sc->gds_hw_ctrl) {
 		status_reg = sc->gds_hw_ctrl;
 		/*
@@ -258,8 +298,12 @@  static int gdsc_disable(struct generic_pm_domain *domain)
 		 */
 		udelay(1);

-		reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
-		ret = gdsc_poll_status(sc, reg, true);
+		if (sc->flags & POLL_CFG_GDSCR) {
+			ret = gdsc_poll_cfg_status(sc, true);
+		} else {
+			reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
+			ret = gdsc_poll_status(sc, reg, true);
+		}
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 2e3abbf..a74f715 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -56,6 +56,7 @@  struct gdsc {
 #define SW_RESET	BIT(3)
 #define AON_RESET	BIT(4)
 #define GDS_TIMEOUT	BIT(5)
+#define POLL_CFG_GDSCR  BIT(6)
 	struct reset_controller_dev	*rcdev;
 	unsigned int			*resets;
 	unsigned int			reset_count;