]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/arm/mach-pxa/pwm.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm
[linux-2.6.git] / arch / arm / mach-pxa / pwm.c
1 /*
2  * linux/arch/arm/mach-pxa/pwm.c
3  *
4  * simple driver for PWM (Pulse Width Modulator) controller
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * 2008-02-13   initial version
11  *              eric miao <eric.miao@marvell.com>
12  */
13
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/platform_device.h>
17 #include <linux/err.h>
18 #include <linux/clk.h>
19 #include <linux/io.h>
20 #include <linux/pwm.h>
21
22 #include <asm/div64.h>
23 #include <mach/pxa-regs.h>
24
25 /* PWM registers and bits definitions */
26 #define PWMCR           (0x00)
27 #define PWMDCR          (0x04)
28 #define PWMPCR          (0x08)
29
30 #define PWMCR_SD        (1 << 6)
31 #define PWMDCR_FD       (1 << 10)
32
33 struct pwm_device {
34         struct list_head        node;
35         struct platform_device *pdev;
36
37         const char      *label;
38         struct clk      *clk;
39         int             clk_enabled;
40         void __iomem    *mmio_base;
41
42         unsigned int    use_count;
43         unsigned int    pwm_id;
44 };
45
46 /*
47  * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
48  * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
49  */
50 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
51 {
52         unsigned long long c;
53         unsigned long period_cycles, prescale, pv, dc;
54
55         if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
56                 return -EINVAL;
57
58         c = clk_get_rate(pwm->clk);
59         c = c * period_ns;
60         do_div(c, 1000000000);
61         period_cycles = c;
62
63         if (period_cycles < 1)
64                 period_cycles = 1;
65         prescale = (period_cycles - 1) / 1024;
66         pv = period_cycles / (prescale + 1) - 1;
67
68         if (prescale > 63)
69                 return -EINVAL;
70
71         if (duty_ns == period_ns)
72                 dc = PWMDCR_FD;
73         else
74                 dc = (pv + 1) * duty_ns / period_ns;
75
76         /* NOTE: the clock to PWM has to be enabled first
77          * before writing to the registers
78          */
79         clk_enable(pwm->clk);
80         __raw_writel(prescale, pwm->mmio_base + PWMCR);
81         __raw_writel(dc, pwm->mmio_base + PWMDCR);
82         __raw_writel(pv, pwm->mmio_base + PWMPCR);
83         clk_disable(pwm->clk);
84
85         return 0;
86 }
87 EXPORT_SYMBOL(pwm_config);
88
89 int pwm_enable(struct pwm_device *pwm)
90 {
91         int rc = 0;
92
93         if (!pwm->clk_enabled) {
94                 rc = clk_enable(pwm->clk);
95                 if (!rc)
96                         pwm->clk_enabled = 1;
97         }
98         return rc;
99 }
100 EXPORT_SYMBOL(pwm_enable);
101
102 void pwm_disable(struct pwm_device *pwm)
103 {
104         if (pwm->clk_enabled) {
105                 clk_disable(pwm->clk);
106                 pwm->clk_enabled = 0;
107         }
108 }
109 EXPORT_SYMBOL(pwm_disable);
110
111 static DEFINE_MUTEX(pwm_lock);
112 static LIST_HEAD(pwm_list);
113
114 struct pwm_device *pwm_request(int pwm_id, const char *label)
115 {
116         struct pwm_device *pwm;
117         int found = 0;
118
119         mutex_lock(&pwm_lock);
120
121         list_for_each_entry(pwm, &pwm_list, node) {
122                 if (pwm->pwm_id == pwm_id) {
123                         found = 1;
124                         break;
125                 }
126         }
127
128         if (found) {
129                 if (pwm->use_count == 0) {
130                         pwm->use_count++;
131                         pwm->label = label;
132                 } else
133                         pwm = ERR_PTR(-EBUSY);
134         } else
135                 pwm = ERR_PTR(-ENOENT);
136
137         mutex_unlock(&pwm_lock);
138         return pwm;
139 }
140 EXPORT_SYMBOL(pwm_request);
141
142 void pwm_free(struct pwm_device *pwm)
143 {
144         mutex_lock(&pwm_lock);
145
146         if (pwm->use_count) {
147                 pwm->use_count--;
148                 pwm->label = NULL;
149         } else
150                 pr_warning("PWM device already freed\n");
151
152         mutex_unlock(&pwm_lock);
153 }
154 EXPORT_SYMBOL(pwm_free);
155
156 static inline void __add_pwm(struct pwm_device *pwm)
157 {
158         mutex_lock(&pwm_lock);
159         list_add_tail(&pwm->node, &pwm_list);
160         mutex_unlock(&pwm_lock);
161 }
162
163 static struct pwm_device *pwm_probe(struct platform_device *pdev,
164                 unsigned int pwm_id, struct pwm_device *parent_pwm)
165 {
166         struct pwm_device *pwm;
167         struct resource *r;
168         int ret = 0;
169
170         pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
171         if (pwm == NULL) {
172                 dev_err(&pdev->dev, "failed to allocate memory\n");
173                 return ERR_PTR(-ENOMEM);
174         }
175
176         pwm->clk = clk_get(&pdev->dev, NULL);
177         if (IS_ERR(pwm->clk)) {
178                 ret = PTR_ERR(pwm->clk);
179                 goto err_free;
180         }
181         pwm->clk_enabled = 0;
182
183         pwm->use_count = 0;
184         pwm->pwm_id = pwm_id;
185         pwm->pdev = pdev;
186
187         if (parent_pwm != NULL) {
188                 /* registers for the second PWM has offset of 0x10 */
189                 pwm->mmio_base = parent_pwm->mmio_base + 0x10;
190                 __add_pwm(pwm);
191                 return pwm;
192         }
193
194         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
195         if (r == NULL) {
196                 dev_err(&pdev->dev, "no memory resource defined\n");
197                 ret = -ENODEV;
198                 goto err_free_clk;
199         }
200
201         r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
202         if (r == NULL) {
203                 dev_err(&pdev->dev, "failed to request memory resource\n");
204                 ret = -EBUSY;
205                 goto err_free_clk;
206         }
207
208         pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
209         if (pwm->mmio_base == NULL) {
210                 dev_err(&pdev->dev, "failed to ioremap() registers\n");
211                 ret = -ENODEV;
212                 goto err_free_mem;
213         }
214
215         __add_pwm(pwm);
216         platform_set_drvdata(pdev, pwm);
217         return pwm;
218
219 err_free_mem:
220         release_mem_region(r->start, r->end - r->start + 1);
221 err_free_clk:
222         clk_put(pwm->clk);
223 err_free:
224         kfree(pwm);
225         return ERR_PTR(ret);
226 }
227
228 static int __devinit pxa25x_pwm_probe(struct platform_device *pdev)
229 {
230         struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL);
231
232         if (IS_ERR(pwm))
233                 return PTR_ERR(pwm);
234
235         return 0;
236 }
237
238 static int __devinit pxa27x_pwm_probe(struct platform_device *pdev)
239 {
240         struct pwm_device *pwm;
241
242         pwm = pwm_probe(pdev, pdev->id, NULL);
243         if (IS_ERR(pwm))
244                 return PTR_ERR(pwm);
245
246         pwm = pwm_probe(pdev, pdev->id + 2, pwm);
247         if (IS_ERR(pwm))
248                 return PTR_ERR(pwm);
249
250         return 0;
251 }
252
253 static int __devexit pwm_remove(struct platform_device *pdev)
254 {
255         struct pwm_device *pwm;
256         struct resource *r;
257
258         pwm = platform_get_drvdata(pdev);
259         if (pwm == NULL)
260                 return -ENODEV;
261
262         mutex_lock(&pwm_lock);
263         list_del(&pwm->node);
264         mutex_unlock(&pwm_lock);
265
266         iounmap(pwm->mmio_base);
267
268         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
269         release_mem_region(r->start, r->end - r->start + 1);
270
271         clk_put(pwm->clk);
272         kfree(pwm);
273         return 0;
274 }
275
276 static struct platform_driver pxa25x_pwm_driver = {
277         .driver         = {
278                 .name   = "pxa25x-pwm",
279         },
280         .probe          = pxa25x_pwm_probe,
281         .remove         = __devexit_p(pwm_remove),
282 };
283
284 static struct platform_driver pxa27x_pwm_driver = {
285         .driver         = {
286                 .name   = "pxa27x-pwm",
287         },
288         .probe          = pxa27x_pwm_probe,
289         .remove         = __devexit_p(pwm_remove),
290 };
291
292 static int __init pwm_init(void)
293 {
294         int ret = 0;
295
296         ret = platform_driver_register(&pxa25x_pwm_driver);
297         if (ret) {
298                 printk(KERN_ERR "failed to register pxa25x_pwm_driver\n");
299                 return ret;
300         }
301
302         ret = platform_driver_register(&pxa27x_pwm_driver);
303         if (ret) {
304                 printk(KERN_ERR "failed to register pxa27x_pwm_driver\n");
305                 return ret;
306         }
307
308         return ret;
309 }
310 arch_initcall(pwm_init);
311
312 static void __exit pwm_exit(void)
313 {
314         platform_driver_unregister(&pxa25x_pwm_driver);
315         platform_driver_unregister(&pxa27x_pwm_driver);
316 }
317 module_exit(pwm_exit);
318
319 MODULE_LICENSE("GPL v2");