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.c63
1 files changed, 46 insertions, 17 deletions
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index d3c22de9ee47..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;
@@ -155,24 +165,25 @@ static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm,
155 struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); 165 struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
156 unsigned int val; 166 unsigned int val;
157 167
158 if (test_bit(PWMF_ENABLED, &pwm->flags)) {
159 /*
160 * If the PWM channel is enabled, using the update register,
161 * it needs to set bit 10 of CMR to 0
162 */
163 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
164 168
165 val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); 169 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
166 val &= ~PWM_CMR_UPD_CDTY; 170
167 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); 171 val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
168 } else { 172 val &= ~PWM_CMR_UPD_CDTY;
169 /* 173 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
170 * If the PWM channel is disabled, write value to duty and 174
171 * period registers directly. 175 /*
172 */ 176 * If the PWM channel is enabled, only update CDTY by using the update
173 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty); 177 * register, it needs to set bit 10 of CMR to 0
174 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd); 178 */
175 } 179 if (test_bit(PWMF_ENABLED, &pwm->flags))
180 return;
181 /*
182 * If the PWM channel is disabled, write value to duty and period
183 * registers directly.
184 */
185 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
186 atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
176} 187}
177 188
178static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm, 189static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -242,7 +253,22 @@ static int atmel_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
242static 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)
243{ 254{
244 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 }
245 270
271 mutex_unlock(&atmel_pwm->isr_lock);
246 atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm); 272 atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
247 273
248 clk_disable(atmel_pwm->clk); 274 clk_disable(atmel_pwm->clk);
@@ -357,6 +383,8 @@ static int atmel_pwm_probe(struct platform_device *pdev)
357 atmel_pwm->chip.npwm = 4; 383 atmel_pwm->chip.npwm = 4;
358 atmel_pwm->chip.can_sleep = true; 384 atmel_pwm->chip.can_sleep = true;
359 atmel_pwm->config = data->config; 385 atmel_pwm->config = data->config;
386 atmel_pwm->updated_pwms = 0;
387 mutex_init(&atmel_pwm->isr_lock);
360 388
361 ret = pwmchip_add(&atmel_pwm->chip); 389 ret = pwmchip_add(&atmel_pwm->chip);
362 if (ret < 0) { 390 if (ret < 0) {
@@ -378,6 +406,7 @@ static int atmel_pwm_remove(struct platform_device *pdev)
378 struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev); 406 struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev);
379 407
380 clk_unprepare(atmel_pwm->clk); 408 clk_unprepare(atmel_pwm->clk);
409 mutex_destroy(&atmel_pwm->isr_lock);
381 410
382 return pwmchip_remove(&atmel_pwm->chip); 411 return pwmchip_remove(&atmel_pwm->chip);
383} 412}