diff mbox series

[v3] pwm: bcm2835: Support apply function for atomic configuration

Message ID 1607464905-16630-1-git-send-email-LinoSanfilippo@gmx.de (mailing list archive)
State New, archived
Headers show
Series [v3] pwm: bcm2835: Support apply function for atomic configuration | expand

Commit Message

Lino Sanfilippo Dec. 8, 2020, 10:01 p.m. UTC
Use the newer .apply function of pwm_ops instead of .config, .enable,
.disable and .set_polarity. This guarantees atomic changes of the pwm
controller configuration. It also reduces the size of the driver.

Since now period is a 64 bit value, add an extra check to reject periods
that exceed the possible max value for the 32 bit register.

This has been tested on a Raspberry PI 4.

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
---

v3: Check against period truncation (based on a review by Uwe Kleine-König)
v2: Fix compiler error for 64 bit builds

 drivers/pwm/pwm-bcm2835.c | 72 +++++++++++++++++------------------------------
 1 file changed, 26 insertions(+), 46 deletions(-)

Comments

Uwe Kleine-König Dec. 9, 2020, 7:05 a.m. UTC | #1
Hello Lino,

On Tue, Dec 08, 2020 at 11:01:45PM +0100, Lino Sanfilippo wrote:
> Use the newer .apply function of pwm_ops instead of .config, .enable,
> .disable and .set_polarity. This guarantees atomic changes of the pwm
> controller configuration. It also reduces the size of the driver.
> 
> Since now period is a 64 bit value, add an extra check to reject periods
> that exceed the possible max value for the 32 bit register.
> 
> This has been tested on a Raspberry PI 4.

This looks right, just two small nitpicks below.

> Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
> ---
> 
> v3: Check against period truncation (based on a review by Uwe Kleine-König)
> v2: Fix compiler error for 64 bit builds
> 
>  drivers/pwm/pwm-bcm2835.c | 72 +++++++++++++++++------------------------------
>  1 file changed, 26 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c
> index 6841dcf..d339898 100644
> --- a/drivers/pwm/pwm-bcm2835.c
> +++ b/drivers/pwm/pwm-bcm2835.c
> @@ -58,13 +58,15 @@ static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
>  	writel(value, pc->base + PWM_CONTROL);
>  }
>  
> -static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> -			      int duty_ns, int period_ns)
> +static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> +			     const struct pwm_state *state)
>  {
> +
>  	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
>  	unsigned long rate = clk_get_rate(pc->clk);
> +	unsigned long long period;
>  	unsigned long scaler;
> -	u32 period;
> +	u32 val;
>  
>  	if (!rate) {
>  		dev_err(pc->dev, "failed to get clock rate\n");
> @@ -72,65 +74,43 @@ static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
>  	}
>  
>  	scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate);
> -	period = DIV_ROUND_CLOSEST(period_ns, scaler);
> +	/* set period */
> +	period = DIV_ROUND_CLOSEST_ULL(state->period, scaler);
>  
> -	if (period < PERIOD_MIN)
> +	/* dont accept a period that is too small or has been truncated */
> +	if ((period < PERIOD_MIN) || (period > U32_MAX))
>  		return -EINVAL;
>  
> -	writel(DIV_ROUND_CLOSEST(duty_ns, scaler),
> -	       pc->base + DUTY(pwm->hwpwm));
> -	writel(period, pc->base + PERIOD(pwm->hwpwm));
> -
> -	return 0;
> -}
> -
> -static int bcm2835_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
> -	u32 value;
> -
> -	value = readl(pc->base + PWM_CONTROL);
> -	value |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
> -	writel(value, pc->base + PWM_CONTROL);
> -
> -	return 0;
> -}
> -
> -static void bcm2835_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
> -	u32 value;
> +	writel((u32) period, pc->base + PERIOD(pwm->hwpwm));

This cast isn't necessary. (And if it was, I *think* the space between
"(u32)" and "period" is wrong. But my expectation that checkpatch warns
about this is wrong, so take this with a grain of salt.)

> -	value = readl(pc->base + PWM_CONTROL);
> -	value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
> -	writel(value, pc->base + PWM_CONTROL);
> -}
> +	/* set duty cycle */
> +	val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler);
> +	writel(val, pc->base + DUTY(pwm->hwpwm));
>  
> -static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
> -				enum pwm_polarity polarity)
> -{
> -	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
> -	u32 value;
> +	/* set polarity */
> +	val = readl(pc->base + PWM_CONTROL);
>  
> -	value = readl(pc->base + PWM_CONTROL);
> +	if (state->polarity == PWM_POLARITY_NORMAL)
> +		val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
> +	else
> +		val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
>  
> -	if (polarity == PWM_POLARITY_NORMAL)
> -		value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
> +	/* enable/disable */
> +	if (state->enabled)
> +		val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
>  	else
> -		value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
> +		val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
>  
> -	writel(value, pc->base + PWM_CONTROL);
> +	writel(val, pc->base + PWM_CONTROL);
>  
>  	return 0;
>  }
>  
> +

I wouldn't have added this empty line. But I guess that's subjective. Or
did you add this by mistake?

>  static const struct pwm_ops bcm2835_pwm_ops = {
>  	.request = bcm2835_pwm_request,
>  	.free = bcm2835_pwm_free,
> -	.config = bcm2835_pwm_config,
> -	.enable = bcm2835_pwm_enable,
> -	.disable = bcm2835_pwm_disable,
> -	.set_polarity = bcm2835_set_polarity,
> +	.apply = bcm2835_pwm_apply,
>  	.owner = THIS_MODULE,
>  };
Lino Sanfilippo Dec. 9, 2020, 1:20 p.m. UTC | #2
Hi Uwe

> Hello Lino,
>
> On Tue, Dec 08, 2020 at 11:01:45PM +0100, Lino Sanfilippo wrote:
> > Use the newer .apply function of pwm_ops instead of .config, .enable,
> > .disable and .set_polarity. This guarantees atomic changes of the pwm
> > controller configuration. It also reduces the size of the driver.
> >
> > Since now period is a 64 bit value, add an extra check to reject periods
> > that exceed the possible max value for the 32 bit register.
> >
> > This has been tested on a Raspberry PI 4.
>
> This looks right, just two small nitpicks below.
>

>
> This cast isn't necessary. (And if it was, I *think* the space between
> "(u32)" and "period" is wrong. But my expectation that checkpatch warns
> about this is wrong, so take this with a grain of salt.)

OK, I will omit the cast in the next patch version (it was primarily
meant for documentation purposes but now it seems to me rather
unusual for kernel code)

>
> > -	value = readl(pc->base + PWM_CONTROL);
> > -	value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
> > -	writel(value, pc->base + PWM_CONTROL);
> > -}
> > +	/* set duty cycle */
> > +	val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler);
> > +	writel(val, pc->base + DUTY(pwm->hwpwm));
> >
> > -static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
> > -				enum pwm_polarity polarity)
> > -{
> > -	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
> > -	u32 value;
> > +	/* set polarity */
> > +	val = readl(pc->base + PWM_CONTROL);
> >
> > -	value = readl(pc->base + PWM_CONTROL);
> > +	if (state->polarity == PWM_POLARITY_NORMAL)
> > +		val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
> > +	else
> > +		val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
> >
> > -	if (polarity == PWM_POLARITY_NORMAL)
> > -		value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
> > +	/* enable/disable */
> > +	if (state->enabled)
> > +		val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
> >  	else
> > -		value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
> > +		val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
> >
> > -	writel(value, pc->base + PWM_CONTROL);
> > +	writel(val, pc->base + PWM_CONTROL);
> >
> >  	return 0;
> >  }
> >
> > +
>
> I wouldn't have added this empty line. But I guess that's subjective. Or
> did you add this by mistake?

I cannot remember that the line was added by intention, so I am fine to remove it.

Thanks and regards,
Lino
Uwe Kleine-König Dec. 10, 2020, 11:43 a.m. UTC | #3
Hello,

On Tue, Dec 08, 2020 at 11:01:45PM +0100, Lino Sanfilippo wrote:
> Use the newer .apply function of pwm_ops instead of .config, .enable,
> .disable and .set_polarity. This guarantees atomic changes of the pwm
> controller configuration. It also reduces the size of the driver.
> 
> Since now period is a 64 bit value, add an extra check to reject periods
> that exceed the possible max value for the 32 bit register.
> 
> This has been tested on a Raspberry PI 4.
> 
> Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>

Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

Side note: I'm a bit surprised about the output of

	b4 diff 1607464905-16630-1-git-send-email-LinoSanfilippo@gmx.de

This is probably due to the fact that compared to v3 you also rebased.
Still the diff is quite big.

Best regards and thanks for your patch
Uwe
Lino Sanfilippo Dec. 11, 2020, 9:28 a.m. UTC | #4
Hi Uwe,

> Gesendet: Donnerstag, 10. Dezember 2020 um 12:43 Uhr
> Von: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
> An: "Lino Sanfilippo" <LinoSanfilippo@gmx.de>
> Cc: thierry.reding@gmail.com, lee.jones@linaro.org, nsaenzjulienne@suse.de, f.fainelli@gmail.com, rjui@broadcom.com, sean@mess.org, sbranden@broadcom.com, bcm-kernel-feedback-list@broadcom.com, linux-pwm@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org
> Betreff: Re: [PATCH v3] pwm: bcm2835: Support apply function for atomic configuration

> 
> Side note: I'm a bit surprised about the output of
> 
> 	b4 diff 1607464905-16630-1-git-send-email-LinoSanfilippo@gmx.de
> 
> This is probably due to the fact that compared to v3 you also rebased.
> Still the diff is quite big.

You are right, I made a rebase before I created the last patch, sorry for the confusion this caused.
Anyway, thanks for the review(s)!

Regards,
Lino
Uwe Kleine-König Dec. 11, 2020, 9:53 a.m. UTC | #5
On Fri, Dec 11, 2020 at 10:28:35AM +0100, Lino Sanfilippo wrote:
> Hi Uwe,
> 
> > Gesendet: Donnerstag, 10. Dezember 2020 um 12:43 Uhr
> > Von: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
> > An: "Lino Sanfilippo" <LinoSanfilippo@gmx.de>
> > Cc: thierry.reding@gmail.com, lee.jones@linaro.org, nsaenzjulienne@suse.de, f.fainelli@gmail.com, rjui@broadcom.com, sean@mess.org, sbranden@broadcom.com, bcm-kernel-feedback-list@broadcom.com, linux-pwm@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org
> > Betreff: Re: [PATCH v3] pwm: bcm2835: Support apply function for atomic configuration
> 
> > 
> > Side note: I'm a bit surprised about the output of
> > 
> > 	b4 diff 1607464905-16630-1-git-send-email-LinoSanfilippo@gmx.de
> > 
> > This is probably due to the fact that compared to v3 you also rebased.
> > Still the diff is quite big.
> 
> You are right, I made a rebase before I created the last patch, sorry for the confusion this caused.
> Anyway, thanks for the review(s)!

You did everything good enough. (To further improve, you could use
git-format-patch's --base option and mention a rebase in the series'
changelog; note this is quite high level critic.)

This was more me wondering the output is not easier to use. (And note I
also showed the wrong commandline, but that doesn't resolve the issue.
The right command is:

	b4 diff 1607546905-20549-1-git-send-email-LinoSanfilippo@gmx.de

.)

Best regards
Uwe
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c
index 6841dcf..d339898 100644
--- a/drivers/pwm/pwm-bcm2835.c
+++ b/drivers/pwm/pwm-bcm2835.c
@@ -58,13 +58,15 @@  static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 	writel(value, pc->base + PWM_CONTROL);
 }
 
-static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-			      int duty_ns, int period_ns)
+static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
 {
+
 	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
 	unsigned long rate = clk_get_rate(pc->clk);
+	unsigned long long period;
 	unsigned long scaler;
-	u32 period;
+	u32 val;
 
 	if (!rate) {
 		dev_err(pc->dev, "failed to get clock rate\n");
@@ -72,65 +74,43 @@  static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	}
 
 	scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate);
-	period = DIV_ROUND_CLOSEST(period_ns, scaler);
+	/* set period */
+	period = DIV_ROUND_CLOSEST_ULL(state->period, scaler);
 
-	if (period < PERIOD_MIN)
+	/* dont accept a period that is too small or has been truncated */
+	if ((period < PERIOD_MIN) || (period > U32_MAX))
 		return -EINVAL;
 
-	writel(DIV_ROUND_CLOSEST(duty_ns, scaler),
-	       pc->base + DUTY(pwm->hwpwm));
-	writel(period, pc->base + PERIOD(pwm->hwpwm));
-
-	return 0;
-}
-
-static int bcm2835_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
-	u32 value;
-
-	value = readl(pc->base + PWM_CONTROL);
-	value |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
-	writel(value, pc->base + PWM_CONTROL);
-
-	return 0;
-}
-
-static void bcm2835_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
-	u32 value;
+	writel((u32) period, pc->base + PERIOD(pwm->hwpwm));
 
-	value = readl(pc->base + PWM_CONTROL);
-	value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
-	writel(value, pc->base + PWM_CONTROL);
-}
+	/* set duty cycle */
+	val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler);
+	writel(val, pc->base + DUTY(pwm->hwpwm));
 
-static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
-				enum pwm_polarity polarity)
-{
-	struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
-	u32 value;
+	/* set polarity */
+	val = readl(pc->base + PWM_CONTROL);
 
-	value = readl(pc->base + PWM_CONTROL);
+	if (state->polarity == PWM_POLARITY_NORMAL)
+		val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
+	else
+		val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
 
-	if (polarity == PWM_POLARITY_NORMAL)
-		value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
+	/* enable/disable */
+	if (state->enabled)
+		val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
 	else
-		value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
+		val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
 
-	writel(value, pc->base + PWM_CONTROL);
+	writel(val, pc->base + PWM_CONTROL);
 
 	return 0;
 }
 
+
 static const struct pwm_ops bcm2835_pwm_ops = {
 	.request = bcm2835_pwm_request,
 	.free = bcm2835_pwm_free,
-	.config = bcm2835_pwm_config,
-	.enable = bcm2835_pwm_enable,
-	.disable = bcm2835_pwm_disable,
-	.set_polarity = bcm2835_set_polarity,
+	.apply = bcm2835_pwm_apply,
 	.owner = THIS_MODULE,
 };