[PATCH v2] pwm: mediatek: Fix duty and period setting

AngeloGioacchino Del Regno angelogioacchino.delregno at collabora.com
Mon Jul 28 03:31:20 PDT 2025


Il 24/07/25 23:00, Uwe Kleine-König ha scritto:
> The period generated by the hardware is
> 
> 	(PWMDWIDTH + 1) << CLKDIV) / freq
> 
> according to my tests with a signal analyser and also the documentation.
> 
> The current algorithm doesn't consider the `+ 1` part and so configures
> slightly too high periods. The same issue exists for the duty cycle
> setting. So subtract 1 from both the register values for period and
> duty cycle. If period is 0, bail out, if duty_cycle is 0, just disable
> the PWM which results in a constant low output.
> 
> Note that clk handling is dropped from pwm_mediatek_{en,dis}able to
> handle duty_cycle = 0, so the calls in pwm_mediatek_apply() have to be
> modified to do clk handling, too.

The changes LGTM, but please split those in two commits, one that fixes the +1
and one that changes the clock handling.

After which:

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>

Cheers,
Angelo

> 
> Fixes: caf065f8fd58 ("pwm: Add MediaTek PWM support")
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig at baylibre.com>
> ---
> Hello,
> 
> changes since (implicit) v1,
> https://lore.kernel.org/linux-pwm/20250710163705.2095418-2-u.kleine-koenig@baylibre.com/:
> 
>   - Drop superflous parenthesis from commit log
>   - To implement drop the enable register instead of bit 15 in the CON
>     register, adapt commit log accordingly.
> 
> Best regards
> Uwe
> 
>   drivers/pwm/pwm-mediatek.c | 76 ++++++++++++++++++++------------------
>   1 file changed, 41 insertions(+), 35 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c
> index 6777c511622a..4460bbca2582 100644
> --- a/drivers/pwm/pwm-mediatek.c
> +++ b/drivers/pwm/pwm-mediatek.c
> @@ -121,6 +121,26 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
>   	writel(value, chip->regs + chip->soc->reg_offset[num] + offset);
>   }
>   
> +static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
> +	u32 value;
> +
> +	value = readl(pc->regs);
> +	value |= BIT(pwm->hwpwm);
> +	writel(value, pc->regs);
> +}
> +
> +static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
> +	u32 value;
> +
> +	value = readl(pc->regs);
> +	value &= ~BIT(pwm->hwpwm);
> +	writel(value, pc->regs);
> +}
> +
>   static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
>   			       int duty_ns, int period_ns)
>   {
> @@ -150,7 +170,10 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
>   	do_div(resolution, clk_rate);
>   
>   	cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
> -	while (cnt_period > 8191) {
> +	if (!cnt_period)
> +		return -EINVAL;
> +
> +	while (cnt_period > 8192) {
>   		resolution *= 2;
>   		clkdiv++;
>   		cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
> @@ -173,9 +196,16 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
>   	}
>   
>   	cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
> +
>   	pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
> -	pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
> -	pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
> +	pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1);
> +
> +	if (cnt_duty) {
> +		pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty - 1);
> +		pwm_mediatek_enable(chip, pwm);
> +	} else {
> +		pwm_mediatek_disable(chip, pwm);
> +	}
>   
>   out:
>   	pwm_mediatek_clk_disable(chip, pwm);
> @@ -183,35 +213,6 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
>   	return ret;
>   }
>   
> -static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
> -	u32 value;
> -	int ret;
> -
> -	ret = pwm_mediatek_clk_enable(chip, pwm);
> -	if (ret < 0)
> -		return ret;
> -
> -	value = readl(pc->regs);
> -	value |= BIT(pwm->hwpwm);
> -	writel(value, pc->regs);
> -
> -	return 0;
> -}
> -
> -static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> -{
> -	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
> -	u32 value;
> -
> -	value = readl(pc->regs);
> -	value &= ~BIT(pwm->hwpwm);
> -	writel(value, pc->regs);
> -
> -	pwm_mediatek_clk_disable(chip, pwm);
> -}
> -
>   static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   			      const struct pwm_state *state)
>   {
> @@ -221,8 +222,10 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   		return -EINVAL;
>   
>   	if (!state->enabled) {
> -		if (pwm->state.enabled)
> +		if (pwm->state.enabled) {
>   			pwm_mediatek_disable(chip, pwm);
> +			pwm_mediatek_clk_disable(chip, pwm);
> +		}
>   
>   		return 0;
>   	}
> @@ -231,8 +234,11 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>   	if (err)
>   		return err;
>   
> -	if (!pwm->state.enabled)
> -		err = pwm_mediatek_enable(chip, pwm);
> +	if (!pwm->state.enabled) {
> +		err = pwm_mediatek_clk_enable(chip, pwm);
> +		if (err < 0)
> +			return err;
> +	}
>   
>   	return err;
>   }
> 
> base-commit: a02b105fe9f2b82cbd13b13a98c2b9ffae4a7c27





More information about the linux-arm-kernel mailing list