asm-generic: merge branch 'master' of torvalds/linux-2.6
[linux-2.6] / arch / arm / plat-mxc / pwm.c
1 /*
2  * simple driver for PWM (Pulse Width Modulator) controller
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
9  */
10
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/platform_device.h>
14 #include <linux/err.h>
15 #include <linux/clk.h>
16 #include <linux/io.h>
17 #include <linux/pwm.h>
18
19 #if defined CONFIG_ARCH_MX1 || defined CONFIG_ARCH_MX21
20 #define PWM_VER_1
21
22 #define PWMCR   0x00    /* PWM Control Register         */
23 #define PWMSR   0x04    /* PWM Sample Register          */
24 #define PWMPR   0x08    /* PWM Period Register          */
25 #define PWMCNR  0x0C    /* PWM Counter Register         */
26
27 #define PWMCR_HCTR              (1 << 18)               /* Halfword FIFO Data Swapping  */
28 #define PWMCR_BCTR              (1 << 17)               /* Byte FIFO Data Swapping      */
29 #define PWMCR_SWR               (1 << 16)               /* Software Reset               */
30 #define PWMCR_CLKSRC_PERCLK     (0 << 15)               /* PERCLK Clock Source          */
31 #define PWMCR_CLKSRC_CLK32      (1 << 15)               /* 32KHz Clock Source           */
32 #define PWMCR_PRESCALER(x)      (((x - 1) & 0x7F) << 8) /* PRESCALER                    */
33 #define PWMCR_IRQ               (1 << 7)                /* Interrupt Request            */
34 #define PWMCR_IRQEN             (1 << 6)                /* Interrupt Request Enable     */
35 #define PWMCR_FIFOAV            (1 << 5)                /* FIFO Available               */
36 #define PWMCR_EN                (1 << 4)                /* Enables/Disables the PWM     */
37 #define PWMCR_REPEAT(x)         (((x) & 0x03) << 2)     /* Sample Repeats               */
38 #define PWMCR_DIV(x)            (((x) & 0x03) << 0)     /* Clock divider 2/4/8/16       */
39
40 #define MAX_DIV                 (128 * 16)
41 #endif
42
43 #if defined CONFIG_MACH_MX27 || defined CONFIG_ARCH_MX31
44 #define PWM_VER_2
45
46 #define PWMCR   0x00    /* PWM Control Register         */
47 #define PWMSR   0x04    /* PWM Status Register          */
48 #define PWMIR   0x08    /* PWM Interrupt Register       */
49 #define PWMSAR  0x0C    /* PWM Sample Register          */
50 #define PWMPR   0x10    /* PWM Period Register          */
51 #define PWMCNR  0x14    /* PWM Counter Register         */
52
53 #define PWMCR_EN                (1 << 0)                /* Enables/Disables the PWM     */
54 #define PWMCR_REPEAT(x)         (((x) & 0x03) << 1)     /* Sample Repeats               */
55 #define PWMCR_SWR               (1 << 3)                /* Software Reset               */
56 #define PWMCR_PRESCALER(x)      (((x - 1) & 0xFFF) << 4)/* PRESCALER                    */
57 #define PWMCR_CLKSRC(x)         (((x) & 0x3) << 16)
58 #define PWMCR_CLKSRC_OFF        (0 << 16)
59 #define PWMCR_CLKSRC_IPG        (1 << 16)
60 #define PWMCR_CLKSRC_IPG_HIGH   (2 << 16)
61 #define PWMCR_CLKSRC_CLK32      (3 << 16)
62 #define PWMCR_POUTC
63 #define PWMCR_HCTR              (1 << 20)               /* Halfword FIFO Data Swapping  */
64 #define PWMCR_BCTR              (1 << 21)               /* Byte FIFO Data Swapping      */
65 #define PWMCR_DBGEN             (1 << 22)               /* Debug Mode                   */
66 #define PWMCR_WAITEN            (1 << 23)               /* Wait Mode                    */
67 #define PWMCR_DOZEN             (1 << 24)               /* Doze Mode                    */
68 #define PWMCR_STOPEN            (1 << 25)               /* Stop Mode                    */
69 #define PWMCR_FWM(x)            (((x) & 0x3) << 26)     /* FIFO Water Mark              */
70
71 #define MAX_DIV 4096
72 #endif
73
74 #define PWMS_SAMPLE(x)          ((x) & 0xFFFF)          /* Contains a two-sample word   */
75 #define PWMP_PERIOD(x)          ((x) & 0xFFFF)          /* Represents the PWM's period  */
76 #define PWMC_COUNTER(x)         ((x) & 0xFFFF)          /* Represents the current count value   */
77
78 struct pwm_device {
79         struct list_head        node;
80         struct platform_device *pdev;
81
82         const char      *label;
83         struct clk      *clk;
84
85         int             clk_enabled;
86         void __iomem    *mmio_base;
87
88         unsigned int    use_count;
89         unsigned int    pwm_id;
90 };
91
92 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
93 {
94         unsigned long long c;
95         unsigned long period_cycles, duty_cycles, prescale;
96
97         if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
98                 return -EINVAL;
99
100         c = clk_get_rate(pwm->clk);
101         c = c * period_ns;
102         do_div(c, 1000000000);
103         period_cycles = c;
104
105         prescale = period_cycles / 0x10000 + 1;
106
107         period_cycles /= prescale;
108         c = (unsigned long long)period_cycles * duty_ns;
109         do_div(c, period_ns);
110         duty_cycles = c;
111
112 #ifdef PWM_VER_2
113         writel(duty_cycles, pwm->mmio_base + PWMSAR);
114         writel(period_cycles, pwm->mmio_base + PWMPR);
115         writel(PWMCR_PRESCALER(prescale - 1) | PWMCR_CLKSRC_IPG_HIGH | PWMCR_EN,
116                         pwm->mmio_base + PWMCR);
117 #elif defined PWM_VER_1
118 #error PWM not yet working on MX1 / MX21
119 #endif
120
121         return 0;
122 }
123 EXPORT_SYMBOL(pwm_config);
124
125 int pwm_enable(struct pwm_device *pwm)
126 {
127         int rc = 0;
128
129         if (!pwm->clk_enabled) {
130                 rc = clk_enable(pwm->clk);
131                 if (!rc)
132                         pwm->clk_enabled = 1;
133         }
134         return rc;
135 }
136 EXPORT_SYMBOL(pwm_enable);
137
138 void pwm_disable(struct pwm_device *pwm)
139 {
140         if (pwm->clk_enabled) {
141                 clk_disable(pwm->clk);
142                 pwm->clk_enabled = 0;
143         }
144 }
145 EXPORT_SYMBOL(pwm_disable);
146
147 static DEFINE_MUTEX(pwm_lock);
148 static LIST_HEAD(pwm_list);
149
150 struct pwm_device *pwm_request(int pwm_id, const char *label)
151 {
152         struct pwm_device *pwm;
153         int found = 0;
154
155         mutex_lock(&pwm_lock);
156
157         list_for_each_entry(pwm, &pwm_list, node) {
158                 if (pwm->pwm_id == pwm_id) {
159                         found = 1;
160                         break;
161                 }
162         }
163
164         if (found) {
165                 if (pwm->use_count == 0) {
166                         pwm->use_count++;
167                         pwm->label = label;
168                 } else
169                         pwm = ERR_PTR(-EBUSY);
170         } else
171                 pwm = ERR_PTR(-ENOENT);
172
173         mutex_unlock(&pwm_lock);
174         return pwm;
175 }
176 EXPORT_SYMBOL(pwm_request);
177
178 void pwm_free(struct pwm_device *pwm)
179 {
180         mutex_lock(&pwm_lock);
181
182         if (pwm->use_count) {
183                 pwm->use_count--;
184                 pwm->label = NULL;
185         } else
186                 pr_warning("PWM device already freed\n");
187
188         mutex_unlock(&pwm_lock);
189 }
190 EXPORT_SYMBOL(pwm_free);
191
192 static int __devinit mxc_pwm_probe(struct platform_device *pdev)
193 {
194         struct pwm_device *pwm;
195         struct resource *r;
196         int ret = 0;
197
198         pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
199         if (pwm == NULL) {
200                 dev_err(&pdev->dev, "failed to allocate memory\n");
201                 return -ENOMEM;
202         }
203
204         pwm->clk = clk_get(&pdev->dev, "pwm");
205
206         if (IS_ERR(pwm->clk)) {
207                 ret = PTR_ERR(pwm->clk);
208                 goto err_free;
209         }
210
211         pwm->clk_enabled = 0;
212
213         pwm->use_count = 0;
214         pwm->pwm_id = pdev->id;
215         pwm->pdev = pdev;
216
217         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
218         if (r == NULL) {
219                 dev_err(&pdev->dev, "no memory resource defined\n");
220                 ret = -ENODEV;
221                 goto err_free_clk;
222         }
223
224         r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
225         if (r == NULL) {
226                 dev_err(&pdev->dev, "failed to request memory resource\n");
227                 ret = -EBUSY;
228                 goto err_free_clk;
229         }
230
231         pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
232         if (pwm->mmio_base == NULL) {
233                 dev_err(&pdev->dev, "failed to ioremap() registers\n");
234                 ret = -ENODEV;
235                 goto err_free_mem;
236         }
237
238         mutex_lock(&pwm_lock);
239         list_add_tail(&pwm->node, &pwm_list);
240         mutex_unlock(&pwm_lock);
241
242         platform_set_drvdata(pdev, pwm);
243         return 0;
244
245 err_free_mem:
246         release_mem_region(r->start, r->end - r->start + 1);
247 err_free_clk:
248         clk_put(pwm->clk);
249 err_free:
250         kfree(pwm);
251         return ret;
252 }
253
254 static int __devexit mxc_pwm_remove(struct platform_device *pdev)
255 {
256         struct pwm_device *pwm;
257         struct resource *r;
258
259         pwm = platform_get_drvdata(pdev);
260         if (pwm == NULL)
261                 return -ENODEV;
262
263         mutex_lock(&pwm_lock);
264         list_del(&pwm->node);
265         mutex_unlock(&pwm_lock);
266
267         iounmap(pwm->mmio_base);
268
269         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
270         release_mem_region(r->start, r->end - r->start + 1);
271
272         clk_put(pwm->clk);
273
274         kfree(pwm);
275         return 0;
276 }
277
278 static struct platform_driver mxc_pwm_driver = {
279         .driver         = {
280                 .name   = "mxc_pwm",
281         },
282         .probe          = mxc_pwm_probe,
283         .remove         = __devexit_p(mxc_pwm_remove),
284 };
285
286 static int __init mxc_pwm_init(void)
287 {
288         return platform_driver_register(&mxc_pwm_driver);
289 }
290 arch_initcall(mxc_pwm_init);
291
292 static void __exit mxc_pwm_exit(void)
293 {
294         platform_driver_unregister(&mxc_pwm_driver);
295 }
296 module_exit(mxc_pwm_exit);
297
298 MODULE_LICENSE("GPL v2");
299 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
300