diff mbox

[RESEND] pwm: ftm: fix clock enable/disable when using PM

Message ID 1445142552-3530-1-git-send-email-stefan@agner.ch (mailing list archive)
State New, archived
Headers show

Commit Message

Stefan Agner Oct. 18, 2015, 4:29 a.m. UTC
The driver has multiple issues when enabling/disabling clocks. A given
instance enables/disables three clocks: the bus clock, the counter
clock and the PWM clock. The bus clock gets enabled on pwm_request,
whereas the counter and PWM clocks will be enabled upon pwm_enable.

When entering suspend, the current behavior relies on the FTM_OUTMASK
register: If a PWM output is unmasked, the driver assumes the clocks
are enabled. However, some PWM instances, e.g. Vybrid's FTM1, only
have 2 channels connected. In that case, the FTM_OUTMASK reads only
0x3, even when it was initialized with 0xff. Hence for those PWM
instances, the current approach does not work at all.

A second issue is that the three clocks are not treated differently
regarding PWM's enabled/requested state. This can lead to clocks
getting disabled which have not been enabled in the first place (a PWM
channel which only has been requested).

A third issue applies to the bus clock only, which can get enabled
multiple times (once for each PWM channel of a PWM chip). This is
fine, however when entering suspend mode, the clock only gets disabled
once.

To fix the issues this change introduces a different approach by
relying on the enable/prepared counters of the clock framework. Clocks
get disabled during suspend and back enabled on resume regarding to
the PWM channels individual state. However, since we do not count the
clocks locally, this change no longer clears the Status and Control
registers Clock Source Selection (FTM_SC[CLKS]). There have not
observed issues with that approach so far.

Signed-off-by: Stefan Agner <stefan@agner.ch>
---
 drivers/pwm/pwm-fsl-ftm.c | 58 ++++++++++++++++++++---------------------------
 1 file changed, 25 insertions(+), 33 deletions(-)

Comments

Stefan Agner Nov. 19, 2015, 2:04 a.m. UTC | #1
Thierry,

I realized that this patch did not make it into 4.4-rc1, while others,
IMHO less important patches which have been posted later (e.g. sunxi
whitespace fixes) have made it! :-(

Anything wrong with that? Or am I on your spam list? Note that this is
already a RESEND :-)

--
Stefan

On 2015-10-17 21:29, Stefan Agner wrote:
> The driver has multiple issues when enabling/disabling clocks. A given
> instance enables/disables three clocks: the bus clock, the counter
> clock and the PWM clock. The bus clock gets enabled on pwm_request,
> whereas the counter and PWM clocks will be enabled upon pwm_enable.
> 
> When entering suspend, the current behavior relies on the FTM_OUTMASK
> register: If a PWM output is unmasked, the driver assumes the clocks
> are enabled. However, some PWM instances, e.g. Vybrid's FTM1, only
> have 2 channels connected. In that case, the FTM_OUTMASK reads only
> 0x3, even when it was initialized with 0xff. Hence for those PWM
> instances, the current approach does not work at all.
> 
> A second issue is that the three clocks are not treated differently
> regarding PWM's enabled/requested state. This can lead to clocks
> getting disabled which have not been enabled in the first place (a PWM
> channel which only has been requested).
> 
> A third issue applies to the bus clock only, which can get enabled
> multiple times (once for each PWM channel of a PWM chip). This is
> fine, however when entering suspend mode, the clock only gets disabled
> once.
> 
> To fix the issues this change introduces a different approach by
> relying on the enable/prepared counters of the clock framework. Clocks
> get disabled during suspend and back enabled on resume regarding to
> the PWM channels individual state. However, since we do not count the
> clocks locally, this change no longer clears the Status and Control
> registers Clock Source Selection (FTM_SC[CLKS]). There have not
> observed issues with that approach so far.
> 
> Signed-off-by: Stefan Agner <stefan@agner.ch>
> ---
>  drivers/pwm/pwm-fsl-ftm.c | 58 ++++++++++++++++++++---------------------------
>  1 file changed, 25 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
> index f9dfc8b..5e1dd96 100644
> --- a/drivers/pwm/pwm-fsl-ftm.c
> +++ b/drivers/pwm/pwm-fsl-ftm.c
> @@ -80,7 +80,6 @@ struct fsl_pwm_chip {
>  
>  	struct mutex lock;
>  
> -	unsigned int use_count;
>  	unsigned int cnt_select;
>  	unsigned int clk_ps;
>  
> @@ -300,9 +299,6 @@ static int fsl_counter_clock_enable(struct
> fsl_pwm_chip *fpc)
>  {
>  	int ret;
>  
> -	if (fpc->use_count++ != 0)
> -		return 0;
> -
>  	/* select counter clock source */
>  	regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK,
>  			   FTM_SC_CLK(fpc->cnt_select));
> @@ -334,25 +330,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip,
> struct pwm_device *pwm)
>  	return ret;
>  }
>  
> -static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
> -{
> -	/*
> -	 * already disabled, do nothing
> -	 */
> -	if (fpc->use_count == 0)
> -		return;
> -
> -	/* there are still users, so can't disable yet */
> -	if (--fpc->use_count > 0)
> -		return;
> -
> -	/* no users left, disable PWM counter clock */
> -	regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0);
> -
> -	clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
> -	clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
> -}
> -
>  static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>  {
>  	struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
> @@ -362,7 +339,8 @@ static void fsl_pwm_disable(struct pwm_chip *chip,
> struct pwm_device *pwm)
>  	regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm),
>  			   BIT(pwm->hwpwm));
>  
> -	fsl_counter_clock_disable(fpc);
> +	clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
> +	clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
>  
>  	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
>  	if ((val & 0xFF) == 0xFF)
> @@ -492,17 +470,24 @@ static int fsl_pwm_remove(struct platform_device *pdev)
>  static int fsl_pwm_suspend(struct device *dev)
>  {
>  	struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
> -	u32 val;
> +	int i;
>  
>  	regcache_cache_only(fpc->regmap, true);
>  	regcache_mark_dirty(fpc->regmap);
>  
> -	/* read from cache */
> -	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
> -	if ((val & 0xFF) != 0xFF) {
> +	for (i = 0; i < fpc->chip.npwm; i++) {
> +		struct pwm_device *pwm = &fpc->chip.pwms[i];
> +
> +		if (!test_bit(PWMF_REQUESTED, &pwm->flags))
> +			continue;
> +
> +		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
> +
> +		if (!test_bit(PWMF_ENABLED, &pwm->flags))
> +			continue;
> +
>  		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
>  		clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
> -		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
>  	}
>  
>  	return 0;
> @@ -511,12 +496,19 @@ static int fsl_pwm_suspend(struct device *dev)
>  static int fsl_pwm_resume(struct device *dev)
>  {
>  	struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
> -	u32 val;
> +	int i;
> +
> +	for (i = 0; i < fpc->chip.npwm; i++) {
> +		struct pwm_device *pwm = &fpc->chip.pwms[i];
> +
> +		if (!test_bit(PWMF_REQUESTED, &pwm->flags))
> +			continue;
>  
> -	/* read from cache */
> -	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
> -	if ((val & 0xFF) != 0xFF) {
>  		clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
> +
> +		if (!test_bit(PWMF_ENABLED, &pwm->flags))
> +			continue;
> +
>  		clk_prepare_enable(fpc->clk[fpc->cnt_select]);
>  		clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
>  	}
Vladimir Zapolskiy Nov. 20, 2015, 12:38 a.m. UTC | #2
On 19.11.2015 04:04, Stefan Agner wrote:
> Thierry,
> 
> I realized that this patch did not make it into 4.4-rc1, while others,
> IMHO less important patches which have been posted later (e.g. sunxi
> whitespace fixes) have made it! :-(
> 
> Anything wrong with that? Or am I on your spam list? Note that this is
> already a RESEND :-)

You are not alone with this problem, I have unreviewed changes in
linux-pwm mailing list sent in 2014 :)

--
With best wishes,
Vladimir
Thierry Reding Nov. 23, 2015, 9:45 a.m. UTC | #3
On Wed, Nov 18, 2015 at 06:04:12PM -0800, Stefan Agner wrote:
> Thierry,
> 
> I realized that this patch did not make it into 4.4-rc1, while others,
> IMHO less important patches which have been posted later (e.g. sunxi
> whitespace fixes) have made it! :-(

The reason why I merged them is because they are low-risk, whereas this
patch of yours changes existing behaviour, and hasn't received any
feedback from anyone. So the choice that I need to make is to either
trust the original author to have tested the driver and it was working,
or you to have verified that it is still working after the patch on all
setups that it used to work on. The first option obviously carries the
least risk, and that's the reason why the patch hasn't been merged.

> Anything wrong with that? Or am I on your spam list? Note that this is
> already a RESEND :-)

If you want to get this merged, you should try to get some feedback from
at least the original author. Xiubo Li and I extensively discussed this
back at the time when he submitted the driver and we came up with the
current code as the best approach to making sure that clocks are on and
off when they should be. So if it's not working for you, I'm fine taking
the patch if Xiubo or somebody else has had the chance to look at it and
review or test it. So a Reviewed-by or Tested-by tag will go a long way
to convince me that it's safe to apply.

Also you enumerate all the various bits that are broken, and it would
seem to me that they could each be fixed individually rather than go and
implement something completely different which might have undesirable
side-effects. Such an approach would also make it more likely for me to
merge the patch because it would hopefully be more obvious what is being
fixed and that it's a correct fix.

It's not that I mind the rework, but I'd at least like for someone to
verify that it's all still working on existing setups. Now, I understand
that people can go missing, so if nobody were to give you a Reviewed-by
or Tested-by for a couple of weeks I'd even consider applying without,
but as it is, you didn't even Cc Xiubo on the patch, so he's likely
missed it entirely.

Thierry
Thierry Reding Nov. 23, 2015, 9:49 a.m. UTC | #4
On Fri, Nov 20, 2015 at 02:38:05AM +0200, Vladimir Zapolskiy wrote:
> On 19.11.2015 04:04, Stefan Agner wrote:
> > Thierry,
> > 
> > I realized that this patch did not make it into 4.4-rc1, while others,
> > IMHO less important patches which have been posted later (e.g. sunxi
> > whitespace fixes) have made it! :-(
> > 
> > Anything wrong with that? Or am I on your spam list? Note that this is
> > already a RESEND :-)
> 
> You are not alone with this problem, I have unreviewed changes in
> linux-pwm mailing list sent in 2014 :)

Please either ping me or resend patches if I don't reply within a week
or so. Chances are I've either not seen them or meant to reply but never
got around to.

Also, the Linux kernel is a community-driven project, so I fully expect
contributors to review each other's patches. Review isn't something that
only maintainers need to do.

Thierry
Stefan Agner Nov. 23, 2015, 9:40 p.m. UTC | #5
Thanks Thierry for looking into that!

Added Li as recipient to start discussion.

Some comments below:

On 2015-11-23 01:45, Thierry Reding wrote:
> On Wed, Nov 18, 2015 at 06:04:12PM -0800, Stefan Agner wrote:
>> Thierry,
>>
>> I realized that this patch did not make it into 4.4-rc1, while others,
>> IMHO less important patches which have been posted later (e.g. sunxi
>> whitespace fixes) have made it! :-(
> 
> The reason why I merged them is because they are low-risk, whereas this
> patch of yours changes existing behaviour, and hasn't received any
> feedback from anyone. So the choice that I need to make is to either
> trust the original author to have tested the driver and it was working,
> or you to have verified that it is still working after the patch on all
> setups that it used to work on. The first option obviously carries the
> least risk, and that's the reason why the patch hasn't been merged.
> 
>> Anything wrong with that? Or am I on your spam list? Note that this is
>> already a RESEND :-)
> 
> If you want to get this merged, you should try to get some feedback from
> at least the original author. Xiubo Li and I extensively discussed this
> back at the time when he submitted the driver and we came up with the
> current code as the best approach to making sure that clocks are on and
> off when they should be. So if it's not working for you, I'm fine taking
> the patch if Xiubo or somebody else has had the chance to look at it and
> review or test it. So a Reviewed-by or Tested-by tag will go a long way
> to convince me that it's safe to apply.

In mainline, the driver is only used in Freescale Vybrid device trees. I
think most issues have been introduced with the patches adding suspend
support:
http://thread.gmane.org/gmane.linux.kernel/1806940

Not sure against what suspend/resume implementation Li created the
patches, so far there is no suspend/resume implementation for Vybrid
upstream. While working on Vybrid's low power mode LPSTOP3, I noticed
the issues....
 
> Also you enumerate all the various bits that are broken, and it would
> seem to me that they could each be fixed individually rather than go and
> implement something completely different which might have undesirable
> side-effects. Such an approach would also make it more likely for me to
> merge the patch because it would hopefully be more obvious what is being
> fixed and that it's a correct fix.

There are different issues addressed by one solution: Using the clock
frameworks enable/disable counts... I looked through the code again, it
is really quite hard to split it up. I could create one patch which only
switches the PWM enable/disable part to clock framework counts, and
leave the suspend/resume part broken. Than, in a second patch fix the
suspend/resume part. But that sounds like the wrong approach to me
too...

I took inspiration from other PWM drivers, I think the suspend/resume
idea was from TI EHRPWM PWM driver. So this shouldn't be far off what
others are doing.

> 
> It's not that I mind the rework, but I'd at least like for someone to
> verify that it's all still working on existing setups. Now, I understand
> that people can go missing, so if nobody were to give you a Reviewed-by
> or Tested-by for a couple of weeks I'd even consider applying without,
> but as it is, you didn't even Cc Xiubo on the patch, so he's likely
> missed it entirely.

I have had Li in the initial email, don't know/remember why I removed
him from the list in RESEND...  Will keep him in the loop.

Just realized that there is now a helper function for
test_bit(PWMF_ENABLED,... Will send a v2 anyway then.

--
Stefan
Stefan Agner Nov. 23, 2015, 9:47 p.m. UTC | #6
On 2015-11-23 13:40, Stefan Agner wrote:
> Thanks Thierry for looking into that!
> 
> Added Li as recipient to start discussion.

Yeah, kind of remember now, Li is not with Freescale anymore, at least
his address bounces:

The original message was received at Mon, 23 Nov 2015 14:50:20 -0700
from az84f-il1-il2-vlan904.am.freescale.net [192.88.158.118]

   ----- The following addresses had permanent fatal errors -----
<Li.Xiubo@freescale.com>
    (reason: 550 5.1.1 <Li.Xiubo@freescale.com>... User unknown)


I did not found a newer/updated email address.

I will send a v2 with the helper.

--
Stefan

> 
> Some comments below:
> 
> On 2015-11-23 01:45, Thierry Reding wrote:
>> On Wed, Nov 18, 2015 at 06:04:12PM -0800, Stefan Agner wrote:
>>> Thierry,
>>>
>>> I realized that this patch did not make it into 4.4-rc1, while others,
>>> IMHO less important patches which have been posted later (e.g. sunxi
>>> whitespace fixes) have made it! :-(
>>
>> The reason why I merged them is because they are low-risk, whereas this
>> patch of yours changes existing behaviour, and hasn't received any
>> feedback from anyone. So the choice that I need to make is to either
>> trust the original author to have tested the driver and it was working,
>> or you to have verified that it is still working after the patch on all
>> setups that it used to work on. The first option obviously carries the
>> least risk, and that's the reason why the patch hasn't been merged.
>>
>>> Anything wrong with that? Or am I on your spam list? Note that this is
>>> already a RESEND :-)
>>
>> If you want to get this merged, you should try to get some feedback from
>> at least the original author. Xiubo Li and I extensively discussed this
>> back at the time when he submitted the driver and we came up with the
>> current code as the best approach to making sure that clocks are on and
>> off when they should be. So if it's not working for you, I'm fine taking
>> the patch if Xiubo or somebody else has had the chance to look at it and
>> review or test it. So a Reviewed-by or Tested-by tag will go a long way
>> to convince me that it's safe to apply.
> 
> In mainline, the driver is only used in Freescale Vybrid device trees. I
> think most issues have been introduced with the patches adding suspend
> support:
> http://thread.gmane.org/gmane.linux.kernel/1806940
> 
> Not sure against what suspend/resume implementation Li created the
> patches, so far there is no suspend/resume implementation for Vybrid
> upstream. While working on Vybrid's low power mode LPSTOP3, I noticed
> the issues....
>  
>> Also you enumerate all the various bits that are broken, and it would
>> seem to me that they could each be fixed individually rather than go and
>> implement something completely different which might have undesirable
>> side-effects. Such an approach would also make it more likely for me to
>> merge the patch because it would hopefully be more obvious what is being
>> fixed and that it's a correct fix.
> 
> There are different issues addressed by one solution: Using the clock
> frameworks enable/disable counts... I looked through the code again, it
> is really quite hard to split it up. I could create one patch which only
> switches the PWM enable/disable part to clock framework counts, and
> leave the suspend/resume part broken. Than, in a second patch fix the
> suspend/resume part. But that sounds like the wrong approach to me
> too...
> 
> I took inspiration from other PWM drivers, I think the suspend/resume
> idea was from TI EHRPWM PWM driver. So this shouldn't be far off what
> others are doing.
> 
>>
>> It's not that I mind the rework, but I'd at least like for someone to
>> verify that it's all still working on existing setups. Now, I understand
>> that people can go missing, so if nobody were to give you a Reviewed-by
>> or Tested-by for a couple of weeks I'd even consider applying without,
>> but as it is, you didn't even Cc Xiubo on the patch, so he's likely
>> missed it entirely.
> 
> I have had Li in the initial email, don't know/remember why I removed
> him from the list in RESEND...  Will keep him in the loop.
> 
> Just realized that there is now a helper function for
> test_bit(PWMF_ENABLED,... Will send a v2 anyway then.
> 
> --
> Stefan
diff mbox

Patch

diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
index f9dfc8b..5e1dd96 100644
--- a/drivers/pwm/pwm-fsl-ftm.c
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -80,7 +80,6 @@  struct fsl_pwm_chip {
 
 	struct mutex lock;
 
-	unsigned int use_count;
 	unsigned int cnt_select;
 	unsigned int clk_ps;
 
@@ -300,9 +299,6 @@  static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
 {
 	int ret;
 
-	if (fpc->use_count++ != 0)
-		return 0;
-
 	/* select counter clock source */
 	regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK,
 			   FTM_SC_CLK(fpc->cnt_select));
@@ -334,25 +330,6 @@  static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 	return ret;
 }
 
-static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
-{
-	/*
-	 * already disabled, do nothing
-	 */
-	if (fpc->use_count == 0)
-		return;
-
-	/* there are still users, so can't disable yet */
-	if (--fpc->use_count > 0)
-		return;
-
-	/* no users left, disable PWM counter clock */
-	regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0);
-
-	clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
-	clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
-}
-
 static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
@@ -362,7 +339,8 @@  static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 	regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm),
 			   BIT(pwm->hwpwm));
 
-	fsl_counter_clock_disable(fpc);
+	clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+	clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
 
 	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
 	if ((val & 0xFF) == 0xFF)
@@ -492,17 +470,24 @@  static int fsl_pwm_remove(struct platform_device *pdev)
 static int fsl_pwm_suspend(struct device *dev)
 {
 	struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
-	u32 val;
+	int i;
 
 	regcache_cache_only(fpc->regmap, true);
 	regcache_mark_dirty(fpc->regmap);
 
-	/* read from cache */
-	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
-	if ((val & 0xFF) != 0xFF) {
+	for (i = 0; i < fpc->chip.npwm; i++) {
+		struct pwm_device *pwm = &fpc->chip.pwms[i];
+
+		if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+			continue;
+
+		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+
+		if (!test_bit(PWMF_ENABLED, &pwm->flags))
+			continue;
+
 		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
 		clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
-		clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
 	}
 
 	return 0;
@@ -511,12 +496,19 @@  static int fsl_pwm_suspend(struct device *dev)
 static int fsl_pwm_resume(struct device *dev)
 {
 	struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
-	u32 val;
+	int i;
+
+	for (i = 0; i < fpc->chip.npwm; i++) {
+		struct pwm_device *pwm = &fpc->chip.pwms[i];
+
+		if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+			continue;
 
-	/* read from cache */
-	regmap_read(fpc->regmap, FTM_OUTMASK, &val);
-	if ((val & 0xFF) != 0xFF) {
 		clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+
+		if (!test_bit(PWMF_ENABLED, &pwm->flags))
+			continue;
+
 		clk_prepare_enable(fpc->clk[fpc->cnt_select]);
 		clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
 	}