mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Uwe Kleine-König" <ukleinek@kernel.org>
To: Karl Mehltretter <kmehltretter@gmail.com>
Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev>,
	 Nicolas Ferre <nicolas.ferre@microchip.com>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	 linux-pwm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	 linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] pwm: atmel: Fix prescaler for periods of 2^32 clock cycles or more
Date: Thu, 22 Oct 2026 10:29:42 +0200	[thread overview]
Message-ID: <atnHHaOwTkI78_tc@monoceros> (raw)
In-Reply-To: <20261016161221.16553-1-kmehltretter@gmail.com>

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

Hello Karl,

On Fri, Oct 16, 2026 at 06:12:21PM +0200, Karl Mehltretter wrote:
> atmel_pwm_calculate_cprd_and_pres() keeps the period in clock cycles in
> an unsigned long long but derives the prescaler from fls(cycles). fls()
> takes an unsigned int, so the upper 32 bits are ignored.
> 
> On controllers that the driver configures with a 32-bit period register
> (SAM9X60, SAM9X7) a request of 2^32 clock cycles or more therefore gets
> prescaler 0 and a truncated CPRD. The request succeeds and sysfs
> reports the requested period.
> 
> On a SAM9X75 (PWM clock 266.67 MHz) this affects periods from about
> 16.1 s on. Requesting 20 s with a 10 s duty cycle
> 
>   # echo 2 > /sys/class/pwm/pwmchip0/export
>   # echo 20000000000 > /sys/class/pwm/pwmchip0/pwm2/period
>   # echo 10000000000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
>   # echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable
> 
> programs CPRE=0, CPRD=0x3de4355c and CDTY=0x9ef21aae. The channel runs
> with a period of 3.9 s, measured from the period end flag in PWM_ISR.
> With this duty cycle CDTY is above CPRD, so the output stays inactive
> and an LED on it stays dark.
> 
> Use fls64() so that the prescaler is derived from the full 64-bit
> value. The driver then programs CPRE=1, CPRD=0x9ef21aae and
> CDTY=0x4f790d57. The measured period is 20.0 s and the LED is on for
> 10 s of every 20 s.
> 
> Controllers configured with a 16-bit period register are unaffected
> for all representable periods. Some oversized requests of 2^32 clock
> cycles or more now fail with -EINVAL instead of being programmed with
> truncated values. Clamping such requests to the maximum possible
> configuration is a pre-existing issue best addressed when converting
> the driver to the waveform API.
> 
> The truncation was not reachable when it was introduced. The period
> was an unsigned int in nanoseconds then, which limits it to about
> 4.29 s. Commit a9d887dc1c60 ("pwm: Convert period and duty cycle to
> u64") made longer periods possible.
> 
> Fixes: a9d887dc1c60 ("pwm: Convert period and duty cycle to u64")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
> Changes in v2:
> - Keep only the Fixes tag for the commit that exposed the bug (Uwe).
> - Clarify the pre-existing handling of oversized requests (Uwe).
> - Add Microchip's confirmation of the data sheet error.
> - Rebase onto v7.3-rc7.
> - No code changes from v1.
> 
> v1: https://lore.kernel.org/r/20261003083036.21584-1-kmehltretter@gmail.com/
> 
> Notes:
>     Found while testing a Rust port of this driver on a SAM9X75 Curiosity
>     board. The port uses a 64-bit fls. Its registers differed from the C
>     driver for long periods.
> 
>     Tested on that board with a v7.3-rc5-based kernel built with clang
>     22.1.8. PC20 was routed to PWM2 (peripheral function C) in a local
>     device tree. The blue LED is on that pin.
> 
>     - Without the patch there are 23 period ends in 90 s, 3.88 s to 3.93 s
>       apart. The LED stays dark.
>     - With the patch the period ends are 19.98 s to 20.01 s apart. The LED
>       is on for 10 s of every 20 s.
> 
>     The SAM9X60 and SAM9X7 data sheets describe the channel counter as
>     16 bit. That does not match the hardware.
> 
>     - The 20.0 s result needs CPRD=0x9ef21aae. All 32 bits are used.
>     - Commit 0285827d546d ("pwm: atmel: Add support for controllers with
>       32 bit counters") says 32 bit.
>     - Microchip's Harmony PWM example for this board programs
>       CPRD=133333333.
> 
>     Microchip support has confirmed that the data sheets are incorrect and
>     will be corrected.
> 
>  drivers/pwm/pwm-atmel.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
> index 86918523d821..574020d90a3b 100644
> --- a/drivers/pwm/pwm-atmel.c
> +++ b/drivers/pwm/pwm-atmel.c
> @@ -195,7 +195,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
>  	 * So for each bit the number of clock cycles is wider divide the input
>  	 * clock frequency by two using pres and shift cprd accordingly.
>  	 */
> -	shift = fls(cycles) - atmel_pwm->data->cfg.period_bits;
> +	shift = fls64(cycles) - atmel_pwm->data->cfg.period_bits;
>  
>  	if (shift > PWM_MAX_PRES) {
>  		dev_err(pwmchip_parent(chip), "pres exceeds the maximum value\n");

just above this code snippet there is:

	unsigned long long cycles = state->period;

        /* Calculate the period cycles and prescale value */
        cycles *= clkrate;
        do_div(cycles, NSEC_PER_SEC);

there is another overflow happening here if state->period * clkrate
doesn't fit into an u64. A complete fix converts that to:

	cycles = mul_u64_u64_div_u64(state->period, clkrate, NSEC_PER_SEC);

which prevents an overflow if clkrate ≤ NSEC_PER_SEC.
The latter can be asserted by checking that at probe time after calling
clk_rate_exclusive_get().

The options now are I apply your patch anyhow and the above can be
addressed later, or you rework your patch to a complete fix. What is
your preference?

Best regards
Uwe

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

      reply	other threads:[~2026-10-22  8:29 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16 16:12 Karl Mehltretter
2026-10-22  8:29 ` Uwe Kleine-König [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atnHHaOwTkI78_tc@monoceros \
    --to=ukleinek@kernel.org \
    --cc=alexandre.belloni@bootlin.com \
    --cc=claudiu.beznea@tuxon.dev \
    --cc=kmehltretter@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=nicolas.ferre@microchip.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®