2 * linux/arch/arm/mach-pxa/pwm.c
4 * simple driver for PWM (Pulse Width Modulator) controller
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.
10 * 2008-02-13 initial version
11 * eric miao <eric.miao@marvell.com>
14 #include <linux/module.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>
21 #include <linux/pwm.h>
23 #include <asm/div64.h>
25 #define HAS_SECONDARY_PWM 0x10
26 #define PWM_ID_BASE(d) ((d) & 0xf)
28 static const struct platform_device_id pwm_id_table
[] = {
29 /* PWM has_secondary_pwm? */
31 { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM
},
36 MODULE_DEVICE_TABLE(platform
, pwm_id_table
);
38 /* PWM registers and bits definitions */
43 #define PWMCR_SD (1 << 6)
44 #define PWMDCR_FD (1 << 10)
47 struct list_head node
;
48 struct pwm_device
*secondary
;
49 struct platform_device
*pdev
;
54 void __iomem
*mmio_base
;
56 unsigned int use_count
;
61 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
62 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
64 int pwm_config(struct pwm_device
*pwm
, int duty_ns
, int period_ns
)
67 unsigned long period_cycles
, prescale
, pv
, dc
;
69 if (pwm
== NULL
|| period_ns
== 0 || duty_ns
> period_ns
)
72 c
= clk_get_rate(pwm
->clk
);
74 do_div(c
, 1000000000);
77 if (period_cycles
< 1)
79 prescale
= (period_cycles
- 1) / 1024;
80 pv
= period_cycles
/ (prescale
+ 1) - 1;
85 if (duty_ns
== period_ns
)
88 dc
= (pv
+ 1) * duty_ns
/ period_ns
;
90 /* NOTE: the clock to PWM has to be enabled first
91 * before writing to the registers
94 __raw_writel(prescale
, pwm
->mmio_base
+ PWMCR
);
95 __raw_writel(dc
, pwm
->mmio_base
+ PWMDCR
);
96 __raw_writel(pv
, pwm
->mmio_base
+ PWMPCR
);
97 clk_disable(pwm
->clk
);
101 EXPORT_SYMBOL(pwm_config
);
103 int pwm_enable(struct pwm_device
*pwm
)
107 if (!pwm
->clk_enabled
) {
108 rc
= clk_enable(pwm
->clk
);
110 pwm
->clk_enabled
= 1;
114 EXPORT_SYMBOL(pwm_enable
);
116 void pwm_disable(struct pwm_device
*pwm
)
118 if (pwm
->clk_enabled
) {
119 clk_disable(pwm
->clk
);
120 pwm
->clk_enabled
= 0;
123 EXPORT_SYMBOL(pwm_disable
);
125 static DEFINE_MUTEX(pwm_lock
);
126 static LIST_HEAD(pwm_list
);
128 struct pwm_device
*pwm_request(int pwm_id
, const char *label
)
130 struct pwm_device
*pwm
;
133 mutex_lock(&pwm_lock
);
135 list_for_each_entry(pwm
, &pwm_list
, node
) {
136 if (pwm
->pwm_id
== pwm_id
) {
143 if (pwm
->use_count
== 0) {
147 pwm
= ERR_PTR(-EBUSY
);
149 pwm
= ERR_PTR(-ENOENT
);
151 mutex_unlock(&pwm_lock
);
154 EXPORT_SYMBOL(pwm_request
);
156 void pwm_free(struct pwm_device
*pwm
)
158 mutex_lock(&pwm_lock
);
160 if (pwm
->use_count
) {
164 pr_warning("PWM device already freed\n");
166 mutex_unlock(&pwm_lock
);
168 EXPORT_SYMBOL(pwm_free
);
170 static inline void __add_pwm(struct pwm_device
*pwm
)
172 mutex_lock(&pwm_lock
);
173 list_add_tail(&pwm
->node
, &pwm_list
);
174 mutex_unlock(&pwm_lock
);
177 static int __devinit
pwm_probe(struct platform_device
*pdev
)
179 const struct platform_device_id
*id
= platform_get_device_id(pdev
);
180 struct pwm_device
*pwm
, *secondary
= NULL
;
184 pwm
= kzalloc(sizeof(struct pwm_device
), GFP_KERNEL
);
186 dev_err(&pdev
->dev
, "failed to allocate memory\n");
190 pwm
->clk
= clk_get(&pdev
->dev
, NULL
);
191 if (IS_ERR(pwm
->clk
)) {
192 ret
= PTR_ERR(pwm
->clk
);
195 pwm
->clk_enabled
= 0;
198 pwm
->pwm_id
= PWM_ID_BASE(id
->driver_data
) + pdev
->id
;
201 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
203 dev_err(&pdev
->dev
, "no memory resource defined\n");
208 r
= request_mem_region(r
->start
, resource_size(r
), pdev
->name
);
210 dev_err(&pdev
->dev
, "failed to request memory resource\n");
215 pwm
->mmio_base
= ioremap(r
->start
, resource_size(r
));
216 if (pwm
->mmio_base
== NULL
) {
217 dev_err(&pdev
->dev
, "failed to ioremap() registers\n");
222 if (id
->driver_data
& HAS_SECONDARY_PWM
) {
223 secondary
= kzalloc(sizeof(struct pwm_device
), GFP_KERNEL
);
224 if (secondary
== NULL
) {
230 pwm
->secondary
= secondary
;
232 /* registers for the second PWM has offset of 0x10 */
233 secondary
->mmio_base
= pwm
->mmio_base
+ 0x10;
234 secondary
->pwm_id
= pdev
->id
+ 2;
239 __add_pwm(secondary
);
241 platform_set_drvdata(pdev
, pwm
);
245 release_mem_region(r
->start
, resource_size(r
));
253 static int __devexit
pwm_remove(struct platform_device
*pdev
)
255 struct pwm_device
*pwm
;
258 pwm
= platform_get_drvdata(pdev
);
262 mutex_lock(&pwm_lock
);
264 if (pwm
->secondary
) {
265 list_del(&pwm
->secondary
->node
);
266 kfree(pwm
->secondary
);
269 list_del(&pwm
->node
);
270 mutex_unlock(&pwm_lock
);
272 iounmap(pwm
->mmio_base
);
274 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
275 release_mem_region(r
->start
, resource_size(r
));
282 static struct platform_driver pwm_driver
= {
284 .name
= "pxa25x-pwm",
285 .owner
= THIS_MODULE
,
288 .remove
= __devexit_p(pwm_remove
),
289 .id_table
= pwm_id_table
,
292 static int __init
pwm_init(void)
294 return platform_driver_register(&pwm_driver
);
296 arch_initcall(pwm_init
);
298 static void __exit
pwm_exit(void)
300 platform_driver_unregister(&pwm_driver
);
302 module_exit(pwm_exit
);
304 MODULE_LICENSE("GPL v2");