Merge tag 'timer' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[deliverable/linux.git] / arch / arm / plat-samsung / pwm.c
1 /* arch/arm/plat-s3c/pwm.c
2 *
3 * Copyright (c) 2007 Ben Dooks
4 * Copyright (c) 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
6 *
7 * S3C series PWM device core
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License.
12 */
13
14 #include <linux/export.h>
15 #include <linux/kernel.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <linux/err.h>
19 #include <linux/clk.h>
20 #include <linux/io.h>
21 #include <linux/pwm.h>
22
23 #include <mach/map.h>
24
25 #include <plat/regs-timer.h>
26
27 struct pwm_device {
28 struct list_head list;
29 struct platform_device *pdev;
30
31 struct clk *clk_div;
32 struct clk *clk;
33 const char *label;
34
35 unsigned int period_ns;
36 unsigned int duty_ns;
37
38 unsigned char tcon_base;
39 unsigned char use_count;
40 unsigned char pwm_id;
41 };
42
43 #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
44
45 static struct clk *clk_scaler[2];
46
47 static inline int pwm_is_tdiv(struct pwm_device *pwm)
48 {
49 return clk_get_parent(pwm->clk) == pwm->clk_div;
50 }
51
52 static DEFINE_MUTEX(pwm_lock);
53 static LIST_HEAD(pwm_list);
54
55 struct pwm_device *pwm_request(int pwm_id, const char *label)
56 {
57 struct pwm_device *pwm;
58 int found = 0;
59
60 mutex_lock(&pwm_lock);
61
62 list_for_each_entry(pwm, &pwm_list, list) {
63 if (pwm->pwm_id == pwm_id) {
64 found = 1;
65 break;
66 }
67 }
68
69 if (found) {
70 if (pwm->use_count == 0) {
71 pwm->use_count = 1;
72 pwm->label = label;
73 } else
74 pwm = ERR_PTR(-EBUSY);
75 } else
76 pwm = ERR_PTR(-ENOENT);
77
78 mutex_unlock(&pwm_lock);
79 return pwm;
80 }
81
82 EXPORT_SYMBOL(pwm_request);
83
84
85 void pwm_free(struct pwm_device *pwm)
86 {
87 mutex_lock(&pwm_lock);
88
89 if (pwm->use_count) {
90 pwm->use_count--;
91 pwm->label = NULL;
92 } else
93 printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id);
94
95 mutex_unlock(&pwm_lock);
96 }
97
98 EXPORT_SYMBOL(pwm_free);
99
100 #define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
101 #define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
102 #define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
103 #define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
104
105 int pwm_enable(struct pwm_device *pwm)
106 {
107 unsigned long flags;
108 unsigned long tcon;
109
110 local_irq_save(flags);
111
112 tcon = __raw_readl(S3C2410_TCON);
113 tcon |= pwm_tcon_start(pwm);
114 __raw_writel(tcon, S3C2410_TCON);
115
116 local_irq_restore(flags);
117
118 return 0;
119 }
120
121 EXPORT_SYMBOL(pwm_enable);
122
123 void pwm_disable(struct pwm_device *pwm)
124 {
125 unsigned long flags;
126 unsigned long tcon;
127
128 local_irq_save(flags);
129
130 tcon = __raw_readl(S3C2410_TCON);
131 tcon &= ~pwm_tcon_start(pwm);
132 __raw_writel(tcon, S3C2410_TCON);
133
134 local_irq_restore(flags);
135 }
136
137 EXPORT_SYMBOL(pwm_disable);
138
139 static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
140 {
141 unsigned long tin_parent_rate;
142 unsigned int div;
143
144 tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div));
145 pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate);
146
147 for (div = 2; div <= 16; div *= 2) {
148 if ((tin_parent_rate / (div << 16)) < freq)
149 return tin_parent_rate / div;
150 }
151
152 return tin_parent_rate / 16;
153 }
154
155 #define NS_IN_HZ (1000000000UL)
156
157 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
158 {
159 unsigned long tin_rate;
160 unsigned long tin_ns;
161 unsigned long period;
162 unsigned long flags;
163 unsigned long tcon;
164 unsigned long tcnt;
165 long tcmp;
166
167 /* We currently avoid using 64bit arithmetic by using the
168 * fact that anything faster than 1Hz is easily representable
169 * by 32bits. */
170
171 if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
172 return -ERANGE;
173
174 if (duty_ns > period_ns)
175 return -EINVAL;
176
177 if (period_ns == pwm->period_ns &&
178 duty_ns == pwm->duty_ns)
179 return 0;
180
181 /* The TCMP and TCNT can be read without a lock, they're not
182 * shared between the timers. */
183
184 tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));
185 tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
186
187 period = NS_IN_HZ / period_ns;
188
189 pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
190 duty_ns, period_ns, period);
191
192 /* Check to see if we are changing the clock rate of the PWM */
193
194 if (pwm->period_ns != period_ns) {
195 if (pwm_is_tdiv(pwm)) {
196 tin_rate = pwm_calc_tin(pwm, period);
197 clk_set_rate(pwm->clk_div, tin_rate);
198 } else
199 tin_rate = clk_get_rate(pwm->clk);
200
201 pwm->period_ns = period_ns;
202
203 pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
204
205 tin_ns = NS_IN_HZ / tin_rate;
206 tcnt = period_ns / tin_ns;
207 } else
208 tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
209
210 /* Note, counters count down */
211
212 tcmp = duty_ns / tin_ns;
213 tcmp = tcnt - tcmp;
214 /* the pwm hw only checks the compare register after a decrement,
215 so the pin never toggles if tcmp = tcnt */
216 if (tcmp == tcnt)
217 tcmp--;
218
219 pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
220
221 if (tcmp < 0)
222 tcmp = 0;
223
224 /* Update the PWM register block. */
225
226 local_irq_save(flags);
227
228 __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id));
229 __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
230
231 tcon = __raw_readl(S3C2410_TCON);
232 tcon |= pwm_tcon_manulupdate(pwm);
233 tcon |= pwm_tcon_autoreload(pwm);
234 __raw_writel(tcon, S3C2410_TCON);
235
236 tcon &= ~pwm_tcon_manulupdate(pwm);
237 __raw_writel(tcon, S3C2410_TCON);
238
239 local_irq_restore(flags);
240
241 return 0;
242 }
243
244 EXPORT_SYMBOL(pwm_config);
245
246 static int pwm_register(struct pwm_device *pwm)
247 {
248 pwm->duty_ns = -1;
249 pwm->period_ns = -1;
250
251 mutex_lock(&pwm_lock);
252 list_add_tail(&pwm->list, &pwm_list);
253 mutex_unlock(&pwm_lock);
254
255 return 0;
256 }
257
258 static int s3c_pwm_probe(struct platform_device *pdev)
259 {
260 struct device *dev = &pdev->dev;
261 struct pwm_device *pwm;
262 unsigned long flags;
263 unsigned long tcon;
264 unsigned int id = pdev->id;
265 int ret;
266
267 if (id == 4) {
268 dev_err(dev, "TIMER4 is currently not supported\n");
269 return -ENXIO;
270 }
271
272 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
273 if (pwm == NULL) {
274 dev_err(dev, "failed to allocate pwm_device\n");
275 return -ENOMEM;
276 }
277
278 pwm->pdev = pdev;
279 pwm->pwm_id = id;
280
281 /* calculate base of control bits in TCON */
282 pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
283
284 pwm->clk = clk_get(dev, "pwm-tin");
285 if (IS_ERR(pwm->clk)) {
286 dev_err(dev, "failed to get pwm tin clk\n");
287 ret = PTR_ERR(pwm->clk);
288 goto err_alloc;
289 }
290
291 pwm->clk_div = clk_get(dev, "pwm-tdiv");
292 if (IS_ERR(pwm->clk_div)) {
293 dev_err(dev, "failed to get pwm tdiv clk\n");
294 ret = PTR_ERR(pwm->clk_div);
295 goto err_clk_tin;
296 }
297
298 clk_enable(pwm->clk);
299 clk_enable(pwm->clk_div);
300
301 local_irq_save(flags);
302
303 tcon = __raw_readl(S3C2410_TCON);
304 tcon |= pwm_tcon_invert(pwm);
305 __raw_writel(tcon, S3C2410_TCON);
306
307 local_irq_restore(flags);
308
309
310 ret = pwm_register(pwm);
311 if (ret) {
312 dev_err(dev, "failed to register pwm\n");
313 goto err_clk_tdiv;
314 }
315
316 pwm_dbg(pwm, "config bits %02x\n",
317 (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);
318
319 dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
320 clk_get_rate(pwm->clk),
321 clk_get_rate(pwm->clk_div),
322 pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);
323
324 platform_set_drvdata(pdev, pwm);
325 return 0;
326
327 err_clk_tdiv:
328 clk_disable(pwm->clk_div);
329 clk_disable(pwm->clk);
330 clk_put(pwm->clk_div);
331
332 err_clk_tin:
333 clk_put(pwm->clk);
334
335 err_alloc:
336 kfree(pwm);
337 return ret;
338 }
339
340 static int __devexit s3c_pwm_remove(struct platform_device *pdev)
341 {
342 struct pwm_device *pwm = platform_get_drvdata(pdev);
343
344 clk_disable(pwm->clk_div);
345 clk_disable(pwm->clk);
346 clk_put(pwm->clk_div);
347 clk_put(pwm->clk);
348 kfree(pwm);
349
350 return 0;
351 }
352
353 #ifdef CONFIG_PM
354 static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
355 {
356 struct pwm_device *pwm = platform_get_drvdata(pdev);
357
358 /* No one preserve these values during suspend so reset them
359 * Otherwise driver leaves PWM unconfigured if same values
360 * passed to pwm_config
361 */
362 pwm->period_ns = 0;
363 pwm->duty_ns = 0;
364
365 return 0;
366 }
367
368 static int s3c_pwm_resume(struct platform_device *pdev)
369 {
370 struct pwm_device *pwm = platform_get_drvdata(pdev);
371 unsigned long tcon;
372
373 /* Restore invertion */
374 tcon = __raw_readl(S3C2410_TCON);
375 tcon |= pwm_tcon_invert(pwm);
376 __raw_writel(tcon, S3C2410_TCON);
377
378 return 0;
379 }
380
381 #else
382 #define s3c_pwm_suspend NULL
383 #define s3c_pwm_resume NULL
384 #endif
385
386 static struct platform_driver s3c_pwm_driver = {
387 .driver = {
388 .name = "s3c24xx-pwm",
389 .owner = THIS_MODULE,
390 },
391 .probe = s3c_pwm_probe,
392 .remove = __devexit_p(s3c_pwm_remove),
393 .suspend = s3c_pwm_suspend,
394 .resume = s3c_pwm_resume,
395 };
396
397 static int __init pwm_init(void)
398 {
399 int ret;
400
401 clk_scaler[0] = clk_get(NULL, "pwm-scaler0");
402 clk_scaler[1] = clk_get(NULL, "pwm-scaler1");
403
404 if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {
405 printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__);
406 return -EINVAL;
407 }
408
409 ret = platform_driver_register(&s3c_pwm_driver);
410 if (ret)
411 printk(KERN_ERR "%s: failed to add pwm driver\n", __func__);
412
413 return ret;
414 }
415
416 arch_initcall(pwm_init);
This page took 0.038665 seconds and 5 git commands to generate.