drivers/pwm/pwm-imx27.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
This patch fixes a variable truncation when calculating period in
microseconds as part of the solution for the ERR051198 in .apply()
callback.
The problem was identified when reducing the duty cycle through sysfs,
with enable set to 1. The condition to fix errata ERR051198 for period
smaller than 2us is always being met, due to a truncation on tmp,
variable from .apply() callback, caused by the multiplication of
NSEC_PER_SEC, PWMPR (period register) and the prescaler which can easily
overflow u32. Declaring tmp as u64 makes it large enough to accommodate
larger multiplication results.
Testing:
- Hardware: Udoo Neo Extended with iMX6SoloX SoC
- Tools: Verified with a logic analyzer
Signed-off-by: Ronaldo Nunez <rnunez@baylibre.com>
---
drivers/pwm/pwm-imx27.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
index 3d34cdc4a3a5..c8b801fcb525 100644
--- a/drivers/pwm/pwm-imx27.c
+++ b/drivers/pwm/pwm-imx27.c
@@ -200,7 +200,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
- unsigned long period_cycles, duty_cycles, prescale, period_us, tmp;
+ unsigned long period_cycles, duty_cycles, prescale, period_us;
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
unsigned long long c;
unsigned long long clkrate;
@@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
int val;
int ret;
u32 cr;
+ u64 tmp;
clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk);
c = clkrate * state->period;
@@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
val = readl(imx->mmio_base + MX3_PWMPR);
val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
cr = readl(imx->mmio_base + MX3_PWMCR);
+
+ /*
+ * tmp stores period in nanoseconds. Result fits in u64 since
+ * val <= 0xfffe and prescaler in [1, 0x1000].
+ */
tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr);
tmp = DIV_ROUND_UP_ULL(tmp, clkrate);
period_us = DIV_ROUND_UP_ULL(tmp, 1000);
--
2.53.0
On Wed, May 20, 2026 at 09:00:54PM -0300, Ronaldo Nunez wrote:
> This patch fixes a variable truncation when calculating period in
Remove "This Patch" just
Fix a variable 'tmp' truncation when calculating period in
> microseconds as part of the solution for the ERR051198 in .apply()
> callback.
>
> The problem was identified when reducing the duty cycle through sysfs,
> with enable set to 1. The condition to fix errata ERR051198 for period
> smaller than 2us is always being met, due to a truncation on tmp,
> variable from .apply() callback, caused by the multiplication of
> NSEC_PER_SEC, PWMPR (period register) and the prescaler which can easily
> overflow u32.
It'd better provide actual example value for PWMPR and prescaler when
overflow happen.
> Declaring tmp as u64 makes it large enough to accommodate
> larger multiplication results.
>
> Testing:
> - Hardware: Udoo Neo Extended with iMX6SoloX SoC
> - Tools: Verified with a logic analyzer
needn't this part.
Frank
>
> Signed-off-by: Ronaldo Nunez <rnunez@baylibre.com>
> ---
> drivers/pwm/pwm-imx27.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
> index 3d34cdc4a3a5..c8b801fcb525 100644
> --- a/drivers/pwm/pwm-imx27.c
> +++ b/drivers/pwm/pwm-imx27.c
> @@ -200,7 +200,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
> static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> const struct pwm_state *state)
> {
> - unsigned long period_cycles, duty_cycles, prescale, period_us, tmp;
> + unsigned long period_cycles, duty_cycles, prescale, period_us;
> struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
> unsigned long long c;
> unsigned long long clkrate;
> @@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> int val;
> int ret;
> u32 cr;
> + u64 tmp;
>
> clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk);
> c = clkrate * state->period;
> @@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> val = readl(imx->mmio_base + MX3_PWMPR);
> val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
> cr = readl(imx->mmio_base + MX3_PWMCR);
> +
> + /*
> + * tmp stores period in nanoseconds. Result fits in u64 since
> + * val <= 0xfffe and prescaler in [1, 0x1000].
> + */
> tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr);
> tmp = DIV_ROUND_UP_ULL(tmp, clkrate);
> period_us = DIV_ROUND_UP_ULL(tmp, 1000);
> --
> 2.53.0
>
© 2016 - 2026 Red Hat, Inc.