]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - arch/arm/mach-pxa/pwm.c
dec6f2c8dbf989742ae124bbffe924121f3eb4f4
[mirror_ubuntu-zesty-kernel.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
24 #define HAS_SECONDARY_PWM 0x10
25
26 static const struct platform_device_id pwm_id_table[] = {
27 /* PWM has_secondary_pwm? */
28 { "pxa25x-pwm", 0 },
29 { "pxa27x-pwm", HAS_SECONDARY_PWM },
30 { },
31 };
32 MODULE_DEVICE_TABLE(platform, pwm_id_table);
33
34 /* PWM registers and bits definitions */
35 #define PWMCR (0x00)
36 #define PWMDCR (0x04)
37 #define PWMPCR (0x08)
38
39 #define PWMCR_SD (1 << 6)
40 #define PWMDCR_FD (1 << 10)
41
42 struct pwm_device {
43 struct list_head node;
44 struct pwm_device *secondary;
45 struct platform_device *pdev;
46
47 const char *label;
48 struct clk *clk;
49 int clk_enabled;
50 void __iomem *mmio_base;
51
52 unsigned int use_count;
53 unsigned int pwm_id;
54 };
55
56 /*
57 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
58 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
59 */
60 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
61 {
62 unsigned long long c;
63 unsigned long period_cycles, prescale, pv, dc;
64
65 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
66 return -EINVAL;
67
68 c = clk_get_rate(pwm->clk);
69 c = c * period_ns;
70 do_div(c, 1000000000);
71 period_cycles = c;
72
73 if (period_cycles < 1)
74 period_cycles = 1;
75 prescale = (period_cycles - 1) / 1024;
76 pv = period_cycles / (prescale + 1) - 1;
77
78 if (prescale > 63)
79 return -EINVAL;
80
81 if (duty_ns == period_ns)
82 dc = PWMDCR_FD;
83 else
84 dc = (pv + 1) * duty_ns / period_ns;
85
86 /* NOTE: the clock to PWM has to be enabled first
87 * before writing to the registers
88 */
89 clk_enable(pwm->clk);
90 __raw_writel(prescale, pwm->mmio_base + PWMCR);
91 __raw_writel(dc, pwm->mmio_base + PWMDCR);
92 __raw_writel(pv, pwm->mmio_base + PWMPCR);
93 clk_disable(pwm->clk);
94
95 return 0;
96 }
97 EXPORT_SYMBOL(pwm_config);
98
99 int pwm_enable(struct pwm_device *pwm)
100 {
101 int rc = 0;
102
103 if (!pwm->clk_enabled) {
104 rc = clk_enable(pwm->clk);
105 if (!rc)
106 pwm->clk_enabled = 1;
107 }
108 return rc;
109 }
110 EXPORT_SYMBOL(pwm_enable);
111
112 void pwm_disable(struct pwm_device *pwm)
113 {
114 if (pwm->clk_enabled) {
115 clk_disable(pwm->clk);
116 pwm->clk_enabled = 0;
117 }
118 }
119 EXPORT_SYMBOL(pwm_disable);
120
121 static DEFINE_MUTEX(pwm_lock);
122 static LIST_HEAD(pwm_list);
123
124 struct pwm_device *pwm_request(int pwm_id, const char *label)
125 {
126 struct pwm_device *pwm;
127 int found = 0;
128
129 mutex_lock(&pwm_lock);
130
131 list_for_each_entry(pwm, &pwm_list, node) {
132 if (pwm->pwm_id == pwm_id) {
133 found = 1;
134 break;
135 }
136 }
137
138 if (found) {
139 if (pwm->use_count == 0) {
140 pwm->use_count++;
141 pwm->label = label;
142 } else
143 pwm = ERR_PTR(-EBUSY);
144 } else
145 pwm = ERR_PTR(-ENOENT);
146
147 mutex_unlock(&pwm_lock);
148 return pwm;
149 }
150 EXPORT_SYMBOL(pwm_request);
151
152 void pwm_free(struct pwm_device *pwm)
153 {
154 mutex_lock(&pwm_lock);
155
156 if (pwm->use_count) {
157 pwm->use_count--;
158 pwm->label = NULL;
159 } else
160 pr_warning("PWM device already freed\n");
161
162 mutex_unlock(&pwm_lock);
163 }
164 EXPORT_SYMBOL(pwm_free);
165
166 static inline void __add_pwm(struct pwm_device *pwm)
167 {
168 mutex_lock(&pwm_lock);
169 list_add_tail(&pwm->node, &pwm_list);
170 mutex_unlock(&pwm_lock);
171 }
172
173 static int __devinit pwm_probe(struct platform_device *pdev)
174 {
175 struct platform_device_id *id = platform_get_device_id(pdev);
176 struct pwm_device *pwm, *secondary = NULL;
177 struct resource *r;
178 int ret = 0;
179
180 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
181 if (pwm == NULL) {
182 dev_err(&pdev->dev, "failed to allocate memory\n");
183 return -ENOMEM;
184 }
185
186 pwm->clk = clk_get(&pdev->dev, NULL);
187 if (IS_ERR(pwm->clk)) {
188 ret = PTR_ERR(pwm->clk);
189 goto err_free;
190 }
191 pwm->clk_enabled = 0;
192
193 pwm->use_count = 0;
194 pwm->pwm_id = pdev->id;
195 pwm->pdev = pdev;
196
197 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
198 if (r == NULL) {
199 dev_err(&pdev->dev, "no memory resource defined\n");
200 ret = -ENODEV;
201 goto err_free_clk;
202 }
203
204 r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
205 if (r == NULL) {
206 dev_err(&pdev->dev, "failed to request memory resource\n");
207 ret = -EBUSY;
208 goto err_free_clk;
209 }
210
211 pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
212 if (pwm->mmio_base == NULL) {
213 dev_err(&pdev->dev, "failed to ioremap() registers\n");
214 ret = -ENODEV;
215 goto err_free_mem;
216 }
217
218 if (id->driver_data & HAS_SECONDARY_PWM) {
219 secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
220 if (secondary == NULL) {
221 ret = -ENOMEM;
222 goto err_free_mem;
223 }
224
225 *secondary = *pwm;
226 pwm->secondary = secondary;
227
228 /* registers for the second PWM has offset of 0x10 */
229 secondary->mmio_base = pwm->mmio_base + 0x10;
230 secondary->pwm_id = pdev->id + 2;
231 }
232
233 __add_pwm(pwm);
234 if (secondary)
235 __add_pwm(secondary);
236
237 platform_set_drvdata(pdev, pwm);
238 return 0;
239
240 err_free_mem:
241 release_mem_region(r->start, r->end - r->start + 1);
242 err_free_clk:
243 clk_put(pwm->clk);
244 err_free:
245 kfree(pwm);
246 return ret;
247 }
248
249 static int __devexit pwm_remove(struct platform_device *pdev)
250 {
251 struct pwm_device *pwm;
252 struct resource *r;
253
254 pwm = platform_get_drvdata(pdev);
255 if (pwm == NULL)
256 return -ENODEV;
257
258 mutex_lock(&pwm_lock);
259
260 if (pwm->secondary) {
261 list_del(&pwm->secondary->node);
262 kfree(pwm->secondary);
263 }
264
265 list_del(&pwm->node);
266 mutex_unlock(&pwm_lock);
267
268 iounmap(pwm->mmio_base);
269
270 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
271 release_mem_region(r->start, r->end - r->start + 1);
272
273 clk_put(pwm->clk);
274 kfree(pwm);
275 return 0;
276 }
277
278 static struct platform_driver pwm_driver = {
279 .driver = {
280 .name = "pxa25x-pwm",
281 .owner = THIS_MODULE,
282 },
283 .probe = pwm_probe,
284 .remove = __devexit_p(pwm_remove),
285 .id_table = pwm_id_table,
286 };
287
288 static int __init pwm_init(void)
289 {
290 return platform_driver_register(&pwm_driver);
291 }
292 arch_initcall(pwm_init);
293
294 static void __exit pwm_exit(void)
295 {
296 platform_driver_unregister(&pwm_driver);
297 }
298 module_exit(pwm_exit);
299
300 MODULE_LICENSE("GPL v2");