Instead of just clearing the START bit to stop the PWM, disable the autoreload bit and let it finish the current period. This ensures that TOUT stays at a known output level after the PWM is stopped, and is the recommended procedure as per Samsung's technical docs.
This is tested on 3.7 but should be applicable up to 3.11. A similar fix was implemented in mainline in 3.12. Signed-off-by: Guillermo Rodriguez <[email protected]> --- Index: linux-3.7/drivers/pwm/pwm-samsung.c =================================================================== --- linux-3.7.orig/drivers/pwm/pwm-samsung.c 2014-06-10 13:48:04.393351182 +0200 +++ linux-3.7/drivers/pwm/pwm-samsung.c 2014-06-10 13:48:11.212203000 +0200 @@ -66,7 +66,13 @@ local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_start(s3c); + tcon &= ~pwm_tcon_start(s3c); + tcon |= pwm_tcon_manulupdate(s3c); + tcon |= pwm_tcon_autoreload(s3c); + __raw_writel(tcon, S3C2410_TCON); + + tcon &= ~pwm_tcon_manulupdate(s3c); + tcon |= pwm_tcon_start(s3c); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); @@ -82,8 +88,11 @@ local_irq_save(flags); + /* Disable autoreload instead of clearing the 'start' bit to ensure + * that TOUT goes to a known level after the timer is stopped. */ + tcon = __raw_readl(S3C2410_TCON); - tcon &= ~pwm_tcon_start(s3c); + tcon &= ~pwm_tcon_autoreload(s3c); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); @@ -115,7 +124,6 @@ unsigned long tin_ns; unsigned long period; unsigned long flags; - unsigned long tcon; unsigned long tcnt; long tcmp; @@ -176,21 +184,14 @@ if (tcmp < 0) tcmp = 0; - /* Update the PWM register block. */ + /* Update the TCMP/TCNT registers. The rest of the work is done in + * s3c_pwm_enable */ local_irq_save(flags); __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id)); __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id)); - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_manulupdate(s3c); - tcon |= pwm_tcon_autoreload(s3c); - __raw_writel(tcon, S3C2410_TCON); - - tcon &= ~pwm_tcon_manulupdate(s3c); - __raw_writel(tcon, S3C2410_TCON); - local_irq_restore(flags); return 0;
