aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-atmel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-atmel.c')
-rw-r--r--drivers/pwm/pwm-atmel.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 89f9ca41d9af..a947c9095d9d 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -8,9 +8,11 @@
8 */ 8 */
9 9
10#include <linux/clk.h> 10#include <linux/clk.h>
11#include <linux/delay.h>
11#include <linux/err.h> 12#include <linux/err.h>
12#include <linux/io.h> 13#include <linux/io.h>
13#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/mutex.h>
14#include <linux/of.h> 16#include <linux/of.h>
15#include <linux/of_device.h> 17#include <linux/of_device.h>
16#include <linux/platform_device.h> 18#include <linux/platform_device.h>
@@ -21,6 +23,7 @@
21#define PWM_ENA 0x04 23#define PWM_ENA 0x04
22#define PWM_DIS 0x08 24#define PWM_DIS 0x08
23#define PWM_SR 0x0C 25#define PWM_SR 0x0C
26#define PWM_ISR 0x1C
24/* Bit field in SR */ 27/* Bit field in SR */
25#define PWM_SR_ALL_CH_ON 0x0F 28#define PWM_SR_ALL_CH_ON 0x0F
26 29
@@ -60,6 +63,9 @@ struct atmel_pwm_chip {
60 struct clk *clk; 63 struct clk *clk;
61 void __iomem *base; 64 void __iomem *base;
62 65
66 unsigned int updated_pwms;
67 struct mutex isr_lock; /* ISR is cleared when read, ensure only one thread does that */
68
63 void (*config)(struct pwm_chip *chip, struct pwm_device *pwm, 69 void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
64 unsigned long dty, unsigned long prd); 70 unsigned long dty, unsigned long prd);
65}; 71};
@@ -144,6 +150,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
144 val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK); 150 val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
145 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); 151 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
146 atmel_pwm->config(chip, pwm, dty, prd); 152 atmel_pwm->config(chip, pwm, dty, prd);
153 mutex_lock(&atmel_pwm->isr_lock);
154 atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
155 atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm);
156 mutex_unlock(&atmel_pwm->isr_lock);
147 157
148 clk_disable(atmel_pwm->clk); 158 clk_disable(atmel_pwm->clk);
149 return ret; 159 return ret;
@@ -243,7 +253,22 @@ static int atmel_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
243static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 253static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
244{ 254{
245 struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); 255 struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
256 unsigned long timeout = jiffies + 2 * HZ;
257
258 /*
259 * Wait for at least a complete period to have passed before disabling a
260 * channel to be sure that CDTY has been updated
261 */
262 mutex_lock(&atmel_pwm->isr_lock);
263 atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
264
265 while (!(atmel_pwm->updated_pwms & (1 << pwm->hwpwm)) &&
266 time_before(jiffies, timeout)) {
267 usleep_range(10, 100);
268 atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
269 }
246 270
271 mutex_unlock(&atmel_pwm->isr_lock);
247 atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm); 272 atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
248 273
249 clk_disable(atmel_pwm->clk); 274 clk_disable(atmel_pwm->clk);
@@ -358,6 +383,8 @@ static int atmel_pwm_probe(struct platform_device *pdev)
358 atmel_pwm->chip.npwm = 4; 383 atmel_pwm->chip.npwm = 4;
359 atmel_pwm->chip.can_sleep = true; 384 atmel_pwm->chip.can_sleep = true;
360 atmel_pwm->config = data->config; 385 atmel_pwm->config = data->config;
386 atmel_pwm->updated_pwms = 0;
387 mutex_init(&atmel_pwm->isr_lock);
361 388
362 ret = pwmchip_add(&atmel_pwm->chip); 389 ret = pwmchip_add(&atmel_pwm->chip);
363 if (ret < 0) { 390 if (ret < 0) {
@@ -379,6 +406,7 @@ static int atmel_pwm_remove(struct platform_device *pdev)
379 struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev); 406 struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev);
380 407
381 clk_unprepare(atmel_pwm->clk); 408 clk_unprepare(atmel_pwm->clk);
409 mutex_destroy(&atmel_pwm->isr_lock);
382 410
383 return pwmchip_remove(&atmel_pwm->chip); 411 return pwmchip_remove(&atmel_pwm->chip);
384} 412}