linux-sunxi.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling
@ 2021-05-31  4:46 Roman Beranek
  2021-05-31  4:46 ` [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate Roman Beranek
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

As Emil Lenngren has previously shown [1], actually only 1-2 cycles of
the prescaler-divided clock are necessary to pass before the PWM turns
off, not a full period.

To avoid having the PWM re-enabled from another thread while asleep,
ctrl_lock spinlock was converted to a mutex so that it can be released
only after the clock gate has finally been turned on.

[1] https://linux-sunxi.org/PWM_Controller_Register_Guide

Roman Beranek (6):
  pwm: sun4i: enable clk prior to getting its rate
  pwm: sun4i: disable EN bit prior to the delay
  pwm: sun4i: replace spinlock with a mutex
  pwm: sun4i: simplify calculation of the delay time
  pwm: sun4i: shorten the delay to 2 cycles
  pwm: sun4i: don't delay if the PWM is already off

 drivers/pwm/pwm-sun4i.c | 56 +++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 30 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-06-07  8:00   ` Uwe Kleine-König
  2021-05-31  4:46 ` [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay Roman Beranek
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

Ensure the PWM clock is enabled prior to retrieving its rate, as is
already being done in sun4i_pwm_apply.

Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index e01becd102c0..3721b9894cf6 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -117,8 +117,15 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
 	u64 clk_rate, tmp;
 	u32 val;
 	unsigned int prescaler;
+	int ret;
 
+	ret = clk_prepare_enable(sun4i_pwm->clk);
+	if (ret) {
+		dev_err(chip->dev, "failed to enable PWM clock\n");
+		return;
+	}
 	clk_rate = clk_get_rate(sun4i_pwm->clk);
+	clk_disable_unprepare(sun4i_pwm->clk);
 
 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
  2021-05-31  4:46 ` [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-06-07  8:07   ` Uwe Kleine-König
  2021-05-31  4:46 ` [PATCH 3/6] pwm: sun4i: replace spinlock with a mutex Roman Beranek
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

The reason why we wait before gating the clock is to allow for the PWM
to finish its cycle and stop. But it won't stop unless the EN bit is
disabled.

Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 3721b9894cf6..2777abe66f79 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -303,6 +303,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	if (state->enabled)
 		ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
+	else
+		ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
 
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
 
@@ -325,7 +327,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	spin_lock(&sun4i_pwm->ctrl_lock);
 	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
 	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
-	ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
 	spin_unlock(&sun4i_pwm->ctrl_lock);
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 3/6] pwm: sun4i: replace spinlock with a mutex
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
  2021-05-31  4:46 ` [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate Roman Beranek
  2021-05-31  4:46 ` [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-05-31  4:46 ` [PATCH 4/6] pwm: sun4i: simplify calculation of the delay time Roman Beranek
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

Releasing ctrl_lock for the duration of the delay is not desirable as it
allows re-enabling the PWM before the delay is over. Instead, substitute
the spinlock with a mutex so that we can sleep while holding it.

Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 2777abe66f79..b3ec59a83d00 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -16,13 +16,13 @@
 #include <linux/io.h>
 #include <linux/jiffies.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
 #include <linux/time.h>
 
 #define PWM_CTRL_REG		0x0
@@ -87,7 +87,7 @@ struct sun4i_pwm_chip {
 	struct clk *clk;
 	struct reset_control *rst;
 	void __iomem *base;
-	spinlock_t ctrl_lock;
+	struct mutex ctrl_lock;
 	const struct sun4i_pwm_data *data;
 	unsigned long next_period[2];
 };
@@ -265,7 +265,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		return ret;
 	}
 
-	spin_lock(&sun4i_pwm->ctrl_lock);
+	mutex_lock(&sun4i_pwm->ctrl_lock);
 	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
 
 	if (sun4i_pwm->data->has_direct_mod_clk_output) {
@@ -273,7 +273,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm);
 			/* We can skip other parameter */
 			sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
-			spin_unlock(&sun4i_pwm->ctrl_lock);
+			mutex_unlock(&sun4i_pwm->ctrl_lock);
 			return 0;
 		}
 
@@ -308,10 +308,10 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
 
-	spin_unlock(&sun4i_pwm->ctrl_lock);
-
-	if (state->enabled)
+	if (state->enabled) {
+		mutex_unlock(&sun4i_pwm->ctrl_lock);
 		return 0;
+	}
 
 	/* We need a full period to elapse before disabling the channel. */
 	now = jiffies;
@@ -324,11 +324,9 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			usleep_range(delay_us, delay_us * 2);
 	}
 
-	spin_lock(&sun4i_pwm->ctrl_lock);
-	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
 	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
-	spin_unlock(&sun4i_pwm->ctrl_lock);
+	mutex_unlock(&sun4i_pwm->ctrl_lock);
 
 	clk_disable_unprepare(sun4i_pwm->clk);
 
@@ -471,7 +469,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
 	pwm->chip.of_xlate = of_pwm_xlate_with_flags;
 	pwm->chip.of_pwm_n_cells = 3;
 
-	spin_lock_init(&pwm->ctrl_lock);
+	mutex_init(&pwm->ctrl_lock);
 
 	ret = pwmchip_add(&pwm->chip);
 	if (ret < 0) {
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 4/6] pwm: sun4i: simplify calculation of the delay time
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
                   ` (2 preceding siblings ...)
  2021-05-31  4:46 ` [PATCH 3/6] pwm: sun4i: replace spinlock with a mutex Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-05-31  4:46 ` [PATCH 5/6] pwm: sun4i: shorten the delay to 2 cycles Roman Beranek
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

There's no reason to expect a single jiffy has passed since writing
the CTRL register except if a preemption has occured in the meantime.
Avoid introducing unnecessary complexity and simply wait for the whole
period.

Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index b3ec59a83d00..8218173ce3f6 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -14,7 +14,6 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
@@ -89,7 +88,6 @@ struct sun4i_pwm_chip {
 	void __iomem *base;
 	struct mutex ctrl_lock;
 	const struct sun4i_pwm_data *data;
-	unsigned long next_period[2];
 };
 
 static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip)
@@ -242,8 +240,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	struct pwm_state cstate;
 	u32 ctrl, duty = 0, period = 0, val;
 	int ret;
-	unsigned int delay_us, prescaler = 0;
-	unsigned long now;
+	unsigned int prescaler = 0;
 	bool bypass;
 
 	pwm_get_state(pwm, &cstate);
@@ -291,8 +288,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
-	sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
-		nsecs_to_jiffies(cstate.period + 1000);
 
 	if (state->polarity != PWM_POLARITY_NORMAL)
 		ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
@@ -314,15 +309,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	}
 
 	/* We need a full period to elapse before disabling the channel. */
-	now = jiffies;
-	if (time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
-		delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] -
-					   now);
-		if ((delay_us / 500) > MAX_UDELAY_MS)
-			msleep(delay_us / 1000 + 1);
-		else
-			usleep_range(delay_us, delay_us * 2);
-	}
+	fsleep(cstate.period / NSEC_PER_USEC + 1);
 
 	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 5/6] pwm: sun4i: shorten the delay to 2 cycles
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
                   ` (3 preceding siblings ...)
  2021-05-31  4:46 ` [PATCH 4/6] pwm: sun4i: simplify calculation of the delay time Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-05-31  4:46 ` [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off Roman Beranek
  2021-05-31 19:07 ` [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Pascal Roeleven
  6 siblings, 0 replies; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

As Emil Lenngren has previously shown, actually only 1-2 cycles of
the prescaler-divided clock are necessary to pass before the PWM turns
off (instead of a full period). I was able to reproduce his observation
on a A64 using a logic analyzer.

Suggested-by: Emil Lenngren <emil.lenngren@gmail.com>
Suggested-by: Pascal Roeleven <dev@pascalroeleven.nl>
Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 8218173ce3f6..6ab06b9749d0 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -71,7 +71,7 @@ static const u32 prescaler_table[] = {
 	72000,
 	0,
 	0,
-	0, /* Actually 1 but tested separately */
+	1, /* Tested separately */
 };
 
 struct sun4i_pwm_data {
@@ -240,7 +240,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	struct pwm_state cstate;
 	u32 ctrl, duty = 0, period = 0, val;
 	int ret;
-	unsigned int prescaler = 0;
+	unsigned int cycle_ns, current_prescaler, prescaler = 0;
 	bool bypass;
 
 	pwm_get_state(pwm, &cstate);
@@ -277,7 +277,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm);
 	}
 
-	if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
+	current_prescaler = PWM_REG_PRESCAL(ctrl, pwm->hwpwm);
+	if (current_prescaler != prescaler) {
 		/* Prescaler changed, the clock has to be gated */
 		ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
 		sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
@@ -308,8 +309,10 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		return 0;
 	}
 
-	/* We need a full period to elapse before disabling the channel. */
-	fsleep(cstate.period / NSEC_PER_USEC + 1);
+	/* We need to wait 1-2 cycles before disabling the channel. */
+	cycle_ns = DIV_ROUND_UP(NSEC_PER_SEC, clk_get_rate(sun4i_pwm->clk))
+		   * prescaler_table[current_prescaler];
+	fsleep(DIV_ROUND_UP(cycle_ns * 2, NSEC_PER_USEC));
 
 	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
                   ` (4 preceding siblings ...)
  2021-05-31  4:46 ` [PATCH 5/6] pwm: sun4i: shorten the delay to 2 cycles Roman Beranek
@ 2021-05-31  4:46 ` Roman Beranek
  2021-06-10 13:41   ` Pascal Roeleven
  2021-05-31 19:07 ` [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Pascal Roeleven
  6 siblings, 1 reply; 14+ messages in thread
From: Roman Beranek @ 2021-05-31  4:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
---
 drivers/pwm/pwm-sun4i.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 6ab06b9749d0..88bd90498d1f 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -304,7 +304,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
 
-	if (state->enabled) {
+	if (state->enabled || !cstate.enabled) {
 		mutex_unlock(&sun4i_pwm->ctrl_lock);
 		return 0;
 	}
-- 
2.31.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling
  2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
                   ` (5 preceding siblings ...)
  2021-05-31  4:46 ` [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off Roman Beranek
@ 2021-05-31 19:07 ` Pascal Roeleven
  2021-05-31 20:01   ` Emil Lenngren
  2021-06-08 12:28   ` Pascal Roeleven
  6 siblings, 2 replies; 14+ messages in thread
From: Pascal Roeleven @ 2021-05-31 19:07 UTC (permalink / raw)
  To: Roman Beranek
  Cc: Uwe Kleine-König, Thierry Reding, Emil Lenngren, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

On 2021-05-31 06:46, Roman Beranek wrote:
> As Emil Lenngren has previously shown [1], actually only 1-2 cycles of
> the prescaler-divided clock are necessary to pass before the PWM turns
> off, not a full period.
> 
> To avoid having the PWM re-enabled from another thread while asleep,
> ctrl_lock spinlock was converted to a mutex so that it can be released
> only after the clock gate has finally been turned on.
> 
> [1] https://linux-sunxi.org/PWM_Controller_Register_Guide
> 
> Roman Beranek (6):
>   pwm: sun4i: enable clk prior to getting its rate
>   pwm: sun4i: disable EN bit prior to the delay
>   pwm: sun4i: replace spinlock with a mutex
>   pwm: sun4i: simplify calculation of the delay time
>   pwm: sun4i: shorten the delay to 2 cycles
>   pwm: sun4i: don't delay if the PWM is already off
> 
>  drivers/pwm/pwm-sun4i.c | 56 +++++++++++++++++++----------------------
>  1 file changed, 26 insertions(+), 30 deletions(-)

Hi Roman,

Thanks for your attempt to fix this.

Unfortunately on my A10 device (Topwise A721), the controller still gets
stuck in an unrecoverable state after disabling and re-enabling the PWM
when it was already on (set in U-Boot), or when enabling it when it was
off. In this state, any changes to the period register (using devmem)
don't seem to have any effect. It seems to be stuck in the state it was
before disabling. The only thing which still works is enabling and
disabling.

I can't reproduce this behavior manually so I'm not sure what is causing
this.

Regarding the amount of cycles of sleep; Using a prescaler of 72000 the
PWM clock is 3 ms. Although timing tests using devmem seem unreliable
(too much overhead?), in U-Boot I need to 'sleep' for at least 7 ms
between the commands to make sure the output doesn't sometimes get stuck
in the enabled state. So in my case it seems to be at least 3 cycles. I
am not sure how reliable this method is. However even if I can get it
stuck in the enabled state using a shorter time, it doesn't cause the
behavior I mentioned before. I was always able to recover it manually.
Increasing the number of cycles to sleep therefore also doesn't solve my
problem. Until we can solve that I cannot confirm nor deny if 2 cycles
is enough.

Regards,
Pascal


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling
  2021-05-31 19:07 ` [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Pascal Roeleven
@ 2021-05-31 20:01   ` Emil Lenngren
  2021-05-31 20:20     ` Pascal Roeleven
  2021-06-08 12:28   ` Pascal Roeleven
  1 sibling, 1 reply; 14+ messages in thread
From: Emil Lenngren @ 2021-05-31 20:01 UTC (permalink / raw)
  To: Pascal Roeleven
  Cc: Roman Beranek, Uwe Kleine-König, Thierry Reding, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

Den mån 31 maj 2021 kl 21:07 skrev Pascal Roeleven <dev@pascalroeleven.nl>:
>
> On 2021-05-31 06:46, Roman Beranek wrote:
> > As Emil Lenngren has previously shown [1], actually only 1-2 cycles of
> > the prescaler-divided clock are necessary to pass before the PWM turns
> > off, not a full period.
> >
> > To avoid having the PWM re-enabled from another thread while asleep,
> > ctrl_lock spinlock was converted to a mutex so that it can be released
> > only after the clock gate has finally been turned on.
> >
> > [1] https://linux-sunxi.org/PWM_Controller_Register_Guide
> >
> > Roman Beranek (6):
> >   pwm: sun4i: enable clk prior to getting its rate
> >   pwm: sun4i: disable EN bit prior to the delay
> >   pwm: sun4i: replace spinlock with a mutex
> >   pwm: sun4i: simplify calculation of the delay time
> >   pwm: sun4i: shorten the delay to 2 cycles
> >   pwm: sun4i: don't delay if the PWM is already off
> >
> >  drivers/pwm/pwm-sun4i.c | 56 +++++++++++++++++++----------------------
> >  1 file changed, 26 insertions(+), 30 deletions(-)
>
> Hi Roman,
>
> Thanks for your attempt to fix this.
>
> Unfortunately on my A10 device (Topwise A721), the controller still gets
> stuck in an unrecoverable state after disabling and re-enabling the PWM
> when it was already on (set in U-Boot), or when enabling it when it was
> off. In this state, any changes to the period register (using devmem)
> don't seem to have any effect. It seems to be stuck in the state it was
> before disabling. The only thing which still works is enabling and
> disabling.
>
> I can't reproduce this behavior manually so I'm not sure what is causing
> this.
>
> Regarding the amount of cycles of sleep; Using a prescaler of 72000 the
> PWM clock is 3 ms. Although timing tests using devmem seem unreliable
> (too much overhead?), in U-Boot I need to 'sleep' for at least 7 ms
> between the commands to make sure the output doesn't sometimes get stuck
> in the enabled state. So in my case it seems to be at least 3 cycles. I
> am not sure how reliable this method is. However even if I can get it
> stuck in the enabled state using a shorter time, it doesn't cause the
> behavior I mentioned before. I was always able to recover it manually.
> Increasing the number of cycles to sleep therefore also doesn't solve my
> problem. Until we can solve that I cannot confirm nor deny if 2 cycles
> is enough.
>
> Regards,
> Pascal

You could look at the devmem source code, and in C write a script that
writes to pwm register to disable the pwm, insert a usleep, then
disable the gating. This can be done for various sleep values, then
retrying with same sleep value multiple times. Assuming the overhead
is low (you can check the overhead by checking the current timestamp
at the beginning and at the end of the program, take the diff and then
subtract the sleep time), you will get one range where it never works,
one range where it works sometimes, and one range where it always
works. The uncertain range's condition for succeeding will depend on
when in the cycle you run the code.
Assuming we believe 3 cycles are enough on A10 and prescaler is 72000,
the thresholds for these ranges are 0-6 ms, 6-9 ms and 9+ ms.

About "being stuck", I'm not sure exactly what you mean but it's
expected that writes to the period register won't be visible (if you
read it after a write) when the clock gating is disabled. Three full
cycles (with the gating is on) must take place before the change is
visible (i.e. need to wait four cycles to be sure). At least on >=A13.
I documented that here:
https://linux-sunxi.org/PWM_Controller_Register_Guide.

/Emil

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling
  2021-05-31 20:01   ` Emil Lenngren
@ 2021-05-31 20:20     ` Pascal Roeleven
  0 siblings, 0 replies; 14+ messages in thread
From: Pascal Roeleven @ 2021-05-31 20:20 UTC (permalink / raw)
  To: Emil Lenngren
  Cc: Roman Beranek, Uwe Kleine-König, Thierry Reding, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

On 2021-05-31 22:01, Emil Lenngren wrote:
> You could look at the devmem source code, and in C write a script that
> writes to pwm register to disable the pwm, insert a usleep, then
> disable the gating. This can be done for various sleep values, then
> retrying with same sleep value multiple times. Assuming the overhead
> is low (you can check the overhead by checking the current timestamp
> at the beginning and at the end of the program, take the diff and then
> subtract the sleep time), you will get one range where it never works,
> one range where it works sometimes, and one range where it always
> works. The uncertain range's condition for succeeding will depend on
> when in the cycle you run the code.
> Assuming we believe 3 cycles are enough on A10 and prescaler is 72000,
> the thresholds for these ranges are 0-6 ms, 6-9 ms and 9+ ms.

Thank you I will give this a shot if there is still an uncertainty about
the cycles in the end. I performed my tests with a Busybox rootfs, so I
assumed the overhead was low as well.

> About "being stuck", I'm not sure exactly what you mean but it's
> expected that writes to the period register won't be visible (if you
> read it after a write) when the clock gating is disabled. Three full
> cycles (with the gating is on) must take place before the change is
> visible (i.e. need to wait four cycles to be sure). At least on >=A13.
> I documented that here:
> https://linux-sunxi.org/PWM_Controller_Register_Guide.

By being stuck, I mean being in an state from which it can't recover.
The controller will keep outputting seemingly the same signal regardless
what you write to the period register. You can read the values back, but
they aren't effecting the output anymore. No matter in what order or
with what delay I try to re-enable and disable the gate or enable bit,
it'll keep outputting the same signal until you reset the device.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate
  2021-05-31  4:46 ` [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate Roman Beranek
@ 2021-06-07  8:00   ` Uwe Kleine-König
  0 siblings, 0 replies; 14+ messages in thread
From: Uwe Kleine-König @ 2021-06-07  8:00 UTC (permalink / raw)
  To: Roman Beranek
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

[-- Attachment #1: Type: text/plain, Size: 1301 bytes --]

On Mon, May 31, 2021 at 06:46:03AM +0200, Roman Beranek wrote:
> Ensure the PWM clock is enabled prior to retrieving its rate, as is
> already being done in sun4i_pwm_apply.
> 
> Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
> ---
>  drivers/pwm/pwm-sun4i.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
> index e01becd102c0..3721b9894cf6 100644
> --- a/drivers/pwm/pwm-sun4i.c
> +++ b/drivers/pwm/pwm-sun4i.c
> @@ -117,8 +117,15 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
>  	u64 clk_rate, tmp;
>  	u32 val;
>  	unsigned int prescaler;
> +	int ret;
>  
> +	ret = clk_prepare_enable(sun4i_pwm->clk);
> +	if (ret) {
> +		dev_err(chip->dev, "failed to enable PWM clock\n");
> +		return;
> +	}
>  	clk_rate = clk_get_rate(sun4i_pwm->clk);
> +	clk_disable_unprepare(sun4i_pwm->clk);
>  
>  	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);

If the clock is off, does the PWM actually run? Assuming it doesn't the
right thing to do is to ensure the clock is enabled in .probe iff the
PWM is enabled.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay
  2021-05-31  4:46 ` [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay Roman Beranek
@ 2021-06-07  8:07   ` Uwe Kleine-König
  0 siblings, 0 replies; 14+ messages in thread
From: Uwe Kleine-König @ 2021-06-07  8:07 UTC (permalink / raw)
  To: Roman Beranek
  Cc: Thierry Reding, Emil Lenngren, Pascal Roeleven, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

[-- Attachment #1: Type: text/plain, Size: 1844 bytes --]

On Mon, May 31, 2021 at 06:46:04AM +0200, Roman Beranek wrote:
> The reason why we wait before gating the clock is to allow for the PWM
> to finish its cycle and stop. But it won't stop unless the EN bit is
> disabled.
> 
> Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
> ---
>  drivers/pwm/pwm-sun4i.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
> index 3721b9894cf6..2777abe66f79 100644
> --- a/drivers/pwm/pwm-sun4i.c
> +++ b/drivers/pwm/pwm-sun4i.c
> @@ -303,6 +303,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  
>  	if (state->enabled)
>  		ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
> +	else
> +		ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);

Catching the case !state->enabled even earlier would make sense.
Otherwise you might see a needless glitch after

	pwm_apply_state(mypwm, { .period = A, .duty_cycle = B, .enabled = true });
	pwm_apply_state(mypwm, { .period = C, .duty_cycle = D, .enabled = false });

which might make C+D visible on the PWM before disabling.

>  
>  	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
>  
> @@ -325,7 +327,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  	spin_lock(&sun4i_pwm->ctrl_lock);
>  	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
>  	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
> -	ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
>  	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
>  	spin_unlock(&sun4i_pwm->ctrl_lock);

So the comment

  /* We need a full period to elapse before disabling the channel. */

is wrong?

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling
  2021-05-31 19:07 ` [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Pascal Roeleven
  2021-05-31 20:01   ` Emil Lenngren
@ 2021-06-08 12:28   ` Pascal Roeleven
  1 sibling, 0 replies; 14+ messages in thread
From: Pascal Roeleven @ 2021-06-08 12:28 UTC (permalink / raw)
  To: Roman Beranek
  Cc: Uwe Kleine-König, Thierry Reding, Emil Lenngren, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

On 2021-05-31 21:07, Pascal Roeleven wrote:
> On 2021-05-31 06:46, Roman Beranek wrote:
>> As Emil Lenngren has previously shown [1], actually only 1-2 cycles of
>> the prescaler-divided clock are necessary to pass before the PWM turns
>> off, not a full period.
>>
>> To avoid having the PWM re-enabled from another thread while asleep,
>> ctrl_lock spinlock was converted to a mutex so that it can be released
>> only after the clock gate has finally been turned on.
>>
>> [1] https://linux-sunxi.org/PWM_Controller_Register_Guide
>>
>> Roman Beranek (6):
>>   pwm: sun4i: enable clk prior to getting its rate
>>   pwm: sun4i: disable EN bit prior to the delay
>>   pwm: sun4i: replace spinlock with a mutex
>>   pwm: sun4i: simplify calculation of the delay time
>>   pwm: sun4i: shorten the delay to 2 cycles
>>   pwm: sun4i: don't delay if the PWM is already off
>>
>>  drivers/pwm/pwm-sun4i.c | 56 +++++++++++++++++++----------------------
>>  1 file changed, 26 insertions(+), 30 deletions(-)
> 
> Hi Roman,
> 
> Thanks for your attempt to fix this.
> 
> Unfortunately on my A10 device (Topwise A721), the controller still gets
> stuck in an unrecoverable state after disabling and re-enabling the PWM
> when it was already on (set in U-Boot), or when enabling it when it was
> off. In this state, any changes to the period register (using devmem)
> don't seem to have any effect. It seems to be stuck in the state it was
> before disabling. The only thing which still works is enabling and
> disabling.
> 
> I can't reproduce this behavior manually so I'm not sure what is causing
> this.
> 
> Regarding the amount of cycles of sleep; Using a prescaler of 72000 the
> PWM clock is 3 ms. Although timing tests using devmem seem unreliable
> (too much overhead?), in U-Boot I need to 'sleep' for at least 7 ms
> between the commands to make sure the output doesn't sometimes get stuck
> in the enabled state. So in my case it seems to be at least 3 cycles. I
> am not sure how reliable this method is. However even if I can get it
> stuck in the enabled state using a shorter time, it doesn't cause the
> behavior I mentioned before. I was always able to recover it manually.
> Increasing the number of cycles to sleep therefore also doesn't solve my
> problem. Until we can solve that I cannot confirm nor deny if 2 cycles
> is enough.
> 
> Regards,
> Pascal

Turns out, what I'm referring to here is actually a different issue not
related to this patch series. A different series might be sent later to
address that. 

So no objections from my side for this one.

Regards,
Pascal


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off
  2021-05-31  4:46 ` [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off Roman Beranek
@ 2021-06-10 13:41   ` Pascal Roeleven
  0 siblings, 0 replies; 14+ messages in thread
From: Pascal Roeleven @ 2021-06-10 13:41 UTC (permalink / raw)
  To: Roman Beranek
  Cc: Uwe Kleine-König, Thierry Reding, Emil Lenngren, Lee Jones,
	Maxime Ripard, Chen-Yu Tsai, linux-pwm, linux-arm-kernel,
	linux-sunxi, linux-sunxi

On 2021-05-31 06:46, Roman Beranek wrote:
> Signed-off-by: Roman Beranek <roman.beranek@prusa3d.com>
> ---
>  drivers/pwm/pwm-sun4i.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
> index 6ab06b9749d0..88bd90498d1f 100644
> --- a/drivers/pwm/pwm-sun4i.c
> +++ b/drivers/pwm/pwm-sun4i.c
> @@ -304,7 +304,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip,
> struct pwm_device *pwm,
>  
>  	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
>  
> -	if (state->enabled) {
> +	if (state->enabled || !cstate.enabled) {
>  		mutex_unlock(&sun4i_pwm->ctrl_lock);
>  		return 0;
>  	}

Btw, this now leaves the gate open if the controller is currently
disabled and we are only changing the period register and staying
disabled. This becomes an issue because we always expect the gate to be
disabled when the controller is disabled.

Regards,
Pascal


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2021-06-10 13:48 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-31  4:46 [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Roman Beranek
2021-05-31  4:46 ` [PATCH 1/6] pwm: sun4i: enable clk prior to getting its rate Roman Beranek
2021-06-07  8:00   ` Uwe Kleine-König
2021-05-31  4:46 ` [PATCH 2/6] pwm: sun4i: disable EN bit prior to the delay Roman Beranek
2021-06-07  8:07   ` Uwe Kleine-König
2021-05-31  4:46 ` [PATCH 3/6] pwm: sun4i: replace spinlock with a mutex Roman Beranek
2021-05-31  4:46 ` [PATCH 4/6] pwm: sun4i: simplify calculation of the delay time Roman Beranek
2021-05-31  4:46 ` [PATCH 5/6] pwm: sun4i: shorten the delay to 2 cycles Roman Beranek
2021-05-31  4:46 ` [PATCH 6/6] pwm: sun4i: don't delay if the PWM is already off Roman Beranek
2021-06-10 13:41   ` Pascal Roeleven
2021-05-31 19:07 ` [PATCH 0/6] pwm: sun4i: only wait 2 cycles prior to disabling Pascal Roeleven
2021-05-31 20:01   ` Emil Lenngren
2021-05-31 20:20     ` Pascal Roeleven
2021-06-08 12:28   ` Pascal Roeleven

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).