Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pwm: pwm-samsung: Trigger manual update when disabling PWM

When duty-cycle is at full level (100%), the TCNTn and TCMPn registers
needs to be flushed in order to disable the signal. The PWM manual does
not say anything about this, but states that only clearing the TCON
auto-reload bit should be needed, and this seems to be true when the PWM
duty-cycle is not at full level. This can be observed on an Axis
ARTPEC-8, by running:

echo <period> > pwm/period
echo <period> > pwm/duty_cycle
echo 1 > pwm/enable
echo 0 > pwm/enable

Since the TCNTn and TCMPn registers are activated when enabling the PWM
(setting TCON auto-reload bit), and are not touched when disabling the
PWM, the double buffered auto-reload function seems to be still active.
Lowering duty-cycle, and restoring it again in between the enabling and
disabling, makes the disable work since it triggers a reload of the
TCNTn and TCMPn registers.

Fix this by securing a reload of the TCNTn and TCMPn registers when
disabling the PWM and having a full duty-cycle.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Mårten Lindahl and committed by
Thierry Reding
5d82e661 6facd840

+22 -8
+22 -8
drivers/pwm/pwm-samsung.c
··· 117 117 return (channel == 0) ? 0 : (channel + 1); 118 118 } 119 119 120 + static void __pwm_samsung_manual_update(struct samsung_pwm_chip *chip, 121 + struct pwm_device *pwm) 122 + { 123 + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); 124 + u32 tcon; 125 + 126 + tcon = readl(chip->base + REG_TCON); 127 + tcon |= TCON_MANUALUPDATE(tcon_chan); 128 + writel(tcon, chip->base + REG_TCON); 129 + 130 + tcon &= ~TCON_MANUALUPDATE(tcon_chan); 131 + writel(tcon, chip->base + REG_TCON); 132 + } 133 + 120 134 static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, 121 135 unsigned int channel, u8 divisor) 122 136 { ··· 290 276 tcon &= ~TCON_AUTORELOAD(tcon_chan); 291 277 writel(tcon, our_chip->base + REG_TCON); 292 278 279 + /* 280 + * In case the PWM is at 100% duty cycle, force a manual 281 + * update to prevent the signal from staying high. 282 + */ 283 + if (readl(our_chip->base + REG_TCMPB(pwm->hwpwm)) == (u32)-1U) 284 + __pwm_samsung_manual_update(our_chip, pwm); 285 + 293 286 our_chip->disabled_mask |= BIT(pwm->hwpwm); 294 287 295 288 spin_unlock_irqrestore(&samsung_pwm_lock, flags); ··· 305 284 static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, 306 285 struct pwm_device *pwm) 307 286 { 308 - unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); 309 - u32 tcon; 310 287 unsigned long flags; 311 288 312 289 spin_lock_irqsave(&samsung_pwm_lock, flags); 313 290 314 - tcon = readl(chip->base + REG_TCON); 315 - tcon |= TCON_MANUALUPDATE(tcon_chan); 316 - writel(tcon, chip->base + REG_TCON); 317 - 318 - tcon &= ~TCON_MANUALUPDATE(tcon_chan); 319 - writel(tcon, chip->base + REG_TCON); 291 + __pwm_samsung_manual_update(chip, pwm); 320 292 321 293 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 322 294 }