diff mbox series

[v2,4/5] pwm: pxa: Wait for final PWM period to finish

Message ID 20221003015546.202308-5-doug@schmorgal.com
State Changes Requested
Headers show
Series pwm: pxa: Fixes for enable/disable transitions | expand

Commit Message

Doug Brown Oct. 3, 2022, 1:55 a.m. UTC
If the clock is turned on too quickly after being turned off, it won't
actually turn back on. Work around this problem by waiting for the final
period to complete when disabling the PWM. The delay logic is borrowed
from the pwm-sun4i driver.

To avoid unnecessary delays, skip the whole config process if the PWM is
already disabled and staying disabled.

Signed-off-by: Doug Brown <doug@schmorgal.com>
---
 drivers/pwm/pwm-pxa.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

Comments

Uwe Kleine-König Oct. 19, 2022, 7:39 a.m. UTC | #1
On Sun, Oct 02, 2022 at 06:55:45PM -0700, Doug Brown wrote:
> If the clock is turned on too quickly after being turned off, it won't
> actually turn back on. Work around this problem by waiting for the final
> period to complete when disabling the PWM. The delay logic is borrowed
> from the pwm-sun4i driver.
> 
> To avoid unnecessary delays, skip the whole config process if the PWM is
> already disabled and staying disabled.

I wonder if there is some documentation available about this issue. This
feels like a workaround without knowledge about the details and so might
break at the next opportunity.

> [...]
> @@ -122,6 +127,18 @@ static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  	if (!state->enabled && pwm->state.enabled)
>  		clk_disable_unprepare(pc->clk);
>  
> +	if (state->enabled)
> +		return 0;
> +
> +	/* Wait for the final PWM period to finish. This prevents it from
> +	 * being re-enabled too quickly (which can fail silently).
> +	 */

Please stick to the usual comment style. i.e. put the /* on a line for
itself.

Best regards
Uwe
Doug Brown Oct. 22, 2022, 7:35 p.m. UTC | #2
Hi Uwe,

On 10/19/2022 12:39 AM, Uwe Kleine-König wrote:
> On Sun, Oct 02, 2022 at 06:55:45PM -0700, Doug Brown wrote:
>> If the clock is turned on too quickly after being turned off, it won't
>> actually turn back on. Work around this problem by waiting for the final
>> period to complete when disabling the PWM. The delay logic is borrowed
>> from the pwm-sun4i driver.
>>
>> To avoid unnecessary delays, skip the whole config process if the PWM is
>> already disabled and staying disabled.
> 
> I wonder if there is some documentation available about this issue. This
> feels like a workaround without knowledge about the details and so might
> break at the next opportunity.

Thanks for reviewing! Yes, it does feel like a crazy workaround. I'm not
super proud of it. The best documentation I've been able to look at is
the PXA168 software manual [1]. Page 502 of the PDF talks about a
"graceful shutdown" where turning off the clock enable bit doesn't
immediately stop the clock, and instead it waits for the current PWM
period to complete first. This driver is currently configuring it for
graceful shutdown mode, because the PWMCR_SD bit is not set (page 1257).

I've experimentally determined that if you try to turn the clock back on
when a graceful shutdown is still scheduled, it doesn't cancel the
graceful shutdown, so the clock ends up off afterward, even though the
common clock framework thinks it's still on. The hacky delay in this
commit works around that problem. This almost seems like a problem that
should be solved on the common clock framework side instead, but it
doesn't know what the PWM frequency is so it wouldn't know how long to
delay.

Do all the other similar drivers in the kernel do a graceful shutdown
like this when they are turned off? If not, a simpler solution would be
to start turning on the PWMCR_SD bit instead, so the clock stops
immediately (potentially resulting in the final duty cycle being short).
I tested that change in place of this commit and it seems to work pretty
well, although I can still cause it to fail if I turn my PWM backlight
off and back on quickly without a "sleep 0.000001" in between. It feels
to me like there are some weird interdependencies between the clock
enable bits and the actual PWM controller block, at least in the PXA168,
likely due to "graceful shutdown" mode's existence.

What do you think? Turning on the PWMCR_SD bit would be very simple, but
it doesn't fully fix the issue in my testing. I'd still be okay with it
though, because the only failure case I can reproduce is a minor edge
case (plus, I don't love the delay solution).

> 
>> [...]
>> @@ -122,6 +127,18 @@ static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>>   	if (!state->enabled && pwm->state.enabled)
>>   		clk_disable_unprepare(pc->clk);
>>   
>> +	if (state->enabled)
>> +		return 0;
>> +
>> +	/* Wait for the final PWM period to finish. This prevents it from
>> +	 * being re-enabled too quickly (which can fail silently).
>> +	 */
> 
> Please stick to the usual comment style. i.e. put the /* on a line for
> itself.

My bad, thanks for pointing this out. If this comment still exists in
the next version of the patch, I'll fix it.

Thanks,
Doug

[1] 
https://web.archive.org/web/20160428154454/http://www.marvell.com/application-processors/armada-100/assets/armada_16x_software_manual.pdf
Uwe Kleine-König Nov. 9, 2022, 9:26 a.m. UTC | #3
Hello Doug,

On Sat, Oct 22, 2022 at 12:35:23PM -0700, Doug Brown wrote:
> On 10/19/2022 12:39 AM, Uwe Kleine-König wrote:
> > On Sun, Oct 02, 2022 at 06:55:45PM -0700, Doug Brown wrote:
> > > If the clock is turned on too quickly after being turned off, it won't
> > > actually turn back on. Work around this problem by waiting for the final
> > > period to complete when disabling the PWM. The delay logic is borrowed
> > > from the pwm-sun4i driver.
> > > 
> > > To avoid unnecessary delays, skip the whole config process if the PWM is
> > > already disabled and staying disabled.
> > 
> > I wonder if there is some documentation available about this issue. This
> > feels like a workaround without knowledge about the details and so might
> > break at the next opportunity.
> 
> Thanks for reviewing! Yes, it does feel like a crazy workaround. I'm not
> super proud of it. The best documentation I've been able to look at is
> the PXA168 software manual [1]. Page 502 of the PDF talks about a
> "graceful shutdown" where turning off the clock enable bit doesn't
> immediately stop the clock, and instead it waits for the current PWM
> period to complete first. This driver is currently configuring it for
> graceful shutdown mode, because the PWMCR_SD bit is not set (page 1257).
> 
> I've experimentally determined that if you try to turn the clock back on
> when a graceful shutdown is still scheduled, it doesn't cancel the
> graceful shutdown, so the clock ends up off afterward, even though the
> common clock framework thinks it's still on. The hacky delay in this
> commit works around that problem. This almost seems like a problem that
> should be solved on the common clock framework side instead, but it
> doesn't know what the PWM frequency is so it wouldn't know how long to
> delay.
> 
> Do all the other similar drivers in the kernel do a graceful shutdown
> like this when they are turned off? If not, a simpler solution would be
> to start turning on the PWMCR_SD bit instead, so the clock stops
> immediately (potentially resulting in the final duty cycle being short).

There are supported hardwares that (only) support immediate shutdown, so
consumers cannot rely on a graceful stop anyhow. So I'd say using
PWMCR_SD=1 is fine. The documentation suggests that in this case "PWM_OUTx stops
after at most one PSCLK_PWM" which might explain that the problem can
still be triggered. (i.e. if you reenable before the next PSCLK_PWM
cycle.)

BTW, the driver lacks information about how the hardware behaves on
disable. The most common cases are

 - emits the inactive output
 - freezes where it just happens to be (assuming PWMCR_SD=1)

Would you mind testing that and adding a patch to your series? (Look at
e.g. drivers/pwm/pwm-sl28cpld.c for the desired format to ease
grepping.)

> I tested that change in place of this commit and it seems to work pretty
> well, although I can still cause it to fail if I turn my PWM backlight
> off and back on quickly without a "sleep 0.000001" in between. It feels
> to me like there are some weird interdependencies between the clock
> enable bits and the actual PWM controller block, at least in the PXA168,
> likely due to "graceful shutdown" mode's existence.
> 
> What do you think? Turning on the PWMCR_SD bit would be very simple, but
> it doesn't fully fix the issue in my testing. I'd still be okay with it
> though, because the only failure case I can reproduce is a minor edge
> case (plus, I don't love the delay solution).

+1 for no love for the delay solution.
 
Best regards
Uwe
Doug Brown Nov. 13, 2022, 10:29 p.m. UTC | #4
Hi Uwe,

On 11/9/2022 1:26 AM, Uwe Kleine-König wrote:

> There are supported hardwares that (only) support immediate shutdown, so
> consumers cannot rely on a graceful stop anyhow. So I'd say using
> PWMCR_SD=1 is fine. The documentation suggests that in this case "PWM_OUTx stops
> after at most one PSCLK_PWM" which might explain that the problem can
> still be triggered. (i.e. if you reenable before the next PSCLK_PWM
> cycle.)

Thanks for taking a look! Your point about PSCLK_PWM does make sense and
likely explains how I'm still able to trigger the problem.

> BTW, the driver lacks information about how the hardware behaves on
> disable. The most common cases are
> 
>   - emits the inactive output
>   - freezes where it just happens to be (assuming PWMCR_SD=1)
> 
> Would you mind testing that and adding a patch to your series? (Look at
> e.g. drivers/pwm/pwm-sl28cpld.c for the desired format to ease
> grepping.)

Will do. I assume you're referring to the "Limitations" section at the
top of a lot of the drivers. It appears that this hardware always emits
the inactive input when the clock disables -- almost immediately if
PWMCR_SD=1, or at the end of the current duty cycle if PWMCR_SD=0.

Doug
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index cf4d22c91929..edcef67f7ffe 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -17,6 +17,7 @@ 
 #include <linux/io.h>
 #include <linux/pwm.h>
 #include <linux/of_device.h>
+#include <linux/delay.h>
 
 #include <asm/div64.h>
 
@@ -96,12 +97,16 @@  static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			 const struct pwm_state *state)
 {
 	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+	unsigned int delay_us;
 	u64 duty_cycle;
 	int err;
 
 	if (state->polarity != PWM_POLARITY_NORMAL)
 		return -EINVAL;
 
+	if (!state->enabled && !pwm->state.enabled)
+		return 0;
+
 	err = clk_prepare_enable(pc->clk);
 	if (err)
 		return err;
@@ -122,6 +127,18 @@  static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	if (!state->enabled && pwm->state.enabled)
 		clk_disable_unprepare(pc->clk);
 
+	if (state->enabled)
+		return 0;
+
+	/* Wait for the final PWM period to finish. This prevents it from
+	 * being re-enabled too quickly (which can fail silently).
+	 */
+	delay_us = DIV_ROUND_UP_ULL(state->period, NSEC_PER_USEC);
+	if ((delay_us / 500) > MAX_UDELAY_MS)
+		msleep(delay_us / 1000 + 1);
+	else
+		usleep_range(delay_us, delay_us * 2);
+
 	return 0;
 }