aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-vt8500
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 12:22:37 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 12:22:37 -0400
commit9ec97169e7d6afe2f8206d694d1411fb3bb49853 (patch)
tree9d24c8cd440a312f96b70db5cdaaef1136787003 /arch/arm/mach-vt8500
parenta410963ba4c0c768302f0298e258b1ee940e8316 (diff)
parent19891b20e7c275feb92d669f4b1879861f7e8c25 (diff)
Merge branch 'for-3.6' of git://gitorious.org/linux-pwm/linux-pwm
Pull PWM subsystem from Thierry Reding: "The new PWM subsystem aims at collecting all implementations of the legacy PWM API and to eventually replace it completely. The subsystem has been in development for over half a year now and many drivers have already been converted. It has been in linux-next for a couple of weeks and there have been no major issues so I think it is ready for inclusion in your tree." Arnd Bergmann <arnd@arndb.de>: "Very much Ack on the new subsystem. It uses the interface declarations as the previously separate pwm drivers, so nothing changes for now in the drivers using it, although it enables us to change those more easily in the future if we want to. This work is also one of the missing pieces that are required to eventually build ARM kernels for multiple platforms, which is currently prohibited (amongs other things) by the fact that you cannot have more than one driver exporting the pwm functions." Tested-and-acked-by: Alexandre Courbot <acourbot@nvidia.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Philip, Avinash <avinashphilip@ti.com> # TI's AM33xx platforms Acked-By: Alexandre Pereira da Silva <aletes.xgr@gmail.com> # LPC32XX Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Sachin Kamat <sachin.kamat@linaro.org> Fix up trivial conflicts with other cleanups and DT updates. * 'for-3.6' of git://gitorious.org/linux-pwm/linux-pwm: (36 commits) pwm: pwm-tiehrpwm: PWM driver support for EHRPWM pwm: pwm-tiecap: PWM driver support for ECAP APWM pwm: fix used-uninitialized warning in pwm_get() pwm: add lpc32xx PWM support pwm_backlight: pass correct brightness to callback pwm: Use pr_* functions in pwm-samsung.c file pwm: Convert pwm-samsung to use devm_* APIs pwm: Convert pwm-tegra to use devm_clk_get() pwm: pwm-mxs: Return proper error if pwmchip_remove() fails pwm: pwm-bfin: Return proper error if pwmchip_remove() fails pwm: pxa: Propagate pwmchip_remove() error pwm: Convert pwm-pxa to use devm_* APIs pwm: Convert pwm-vt8500 to use devm_* APIs pwm: Convert pwm-imx to use devm_* APIs pwm: Conflict with legacy PWM API pwm: pwm-mxs: add pinctrl support pwm: pwm-mxs: use devm_* managed functions pwm: pwm-mxs: use global reset function stmp_reset_block pwm: pwm-mxs: encode soc name in compatible string pwm: Take over maintainership of the PWM subsystem ...
Diffstat (limited to 'arch/arm/mach-vt8500')
-rw-r--r--arch/arm/mach-vt8500/Makefile2
-rw-r--r--arch/arm/mach-vt8500/pwm.c265
2 files changed, 0 insertions, 267 deletions
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 54e69973f39b..7ce51767c99c 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -5,5 +5,3 @@ obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
5 5
6obj-$(CONFIG_MACH_BV07) += bv07.o 6obj-$(CONFIG_MACH_BV07) += bv07.o
7obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o 7obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
8
9obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
deleted file mode 100644
index 8ad825e93592..000000000000
--- a/arch/arm/mach-vt8500/pwm.c
+++ /dev/null
@@ -1,265 +0,0 @@
1/*
2 * arch/arm/mach-vt8500/pwm.c
3 *
4 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/err.h>
21#include <linux/io.h>
22#include <linux/pwm.h>
23#include <linux/delay.h>
24
25#include <asm/div64.h>
26
27#define VT8500_NR_PWMS 4
28
29static DEFINE_MUTEX(pwm_lock);
30static LIST_HEAD(pwm_list);
31
32struct pwm_device {
33 struct list_head node;
34 struct platform_device *pdev;
35
36 const char *label;
37
38 void __iomem *regbase;
39
40 unsigned int use_count;
41 unsigned int pwm_id;
42};
43
44#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
45static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
46{
47 int loops = msecs_to_loops(10);
48 while ((readb(reg) & bitmask) && --loops)
49 cpu_relax();
50
51 if (unlikely(!loops))
52 pr_warning("Waiting for status bits 0x%x to clear timed out\n",
53 bitmask);
54}
55
56int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
57{
58 unsigned long long c;
59 unsigned long period_cycles, prescale, pv, dc;
60
61 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
62 return -EINVAL;
63
64 c = 25000000/2; /* wild guess --- need to implement clocks */
65 c = c * period_ns;
66 do_div(c, 1000000000);
67 period_cycles = c;
68
69 if (period_cycles < 1)
70 period_cycles = 1;
71 prescale = (period_cycles - 1) / 4096;
72 pv = period_cycles / (prescale + 1) - 1;
73 if (pv > 4095)
74 pv = 4095;
75
76 if (prescale > 1023)
77 return -EINVAL;
78
79 c = (unsigned long long)pv * duty_ns;
80 do_div(c, period_ns);
81 dc = c;
82
83 pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
84 writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
85
86 pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
87 writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
88
89 pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
90 writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
91
92 return 0;
93}
94EXPORT_SYMBOL(pwm_config);
95
96int pwm_enable(struct pwm_device *pwm)
97{
98 pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
99 writel(5, pwm->regbase + (pwm->pwm_id << 4));
100 return 0;
101}
102EXPORT_SYMBOL(pwm_enable);
103
104void pwm_disable(struct pwm_device *pwm)
105{
106 pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
107 writel(0, pwm->regbase + (pwm->pwm_id << 4));
108}
109EXPORT_SYMBOL(pwm_disable);
110
111struct pwm_device *pwm_request(int pwm_id, const char *label)
112{
113 struct pwm_device *pwm;
114 int found = 0;
115
116 mutex_lock(&pwm_lock);
117
118 list_for_each_entry(pwm, &pwm_list, node) {
119 if (pwm->pwm_id == pwm_id) {
120 found = 1;
121 break;
122 }
123 }
124
125 if (found) {
126 if (pwm->use_count == 0) {
127 pwm->use_count++;
128 pwm->label = label;
129 } else {
130 pwm = ERR_PTR(-EBUSY);
131 }
132 } else {
133 pwm = ERR_PTR(-ENOENT);
134 }
135
136 mutex_unlock(&pwm_lock);
137 return pwm;
138}
139EXPORT_SYMBOL(pwm_request);
140
141void pwm_free(struct pwm_device *pwm)
142{
143 mutex_lock(&pwm_lock);
144
145 if (pwm->use_count) {
146 pwm->use_count--;
147 pwm->label = NULL;
148 } else {
149 pr_warning("PWM device already freed\n");
150 }
151
152 mutex_unlock(&pwm_lock);
153}
154EXPORT_SYMBOL(pwm_free);
155
156static 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
163static int __devinit pwm_probe(struct platform_device *pdev)
164{
165 struct pwm_device *pwms;
166 struct resource *r;
167 int ret = 0;
168 int i;
169
170 pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
171 if (pwms == NULL) {
172 dev_err(&pdev->dev, "failed to allocate memory\n");
173 return -ENOMEM;
174 }
175
176 for (i = 0; i < VT8500_NR_PWMS; i++) {
177 pwms[i].use_count = 0;
178 pwms[i].pwm_id = i;
179 pwms[i].pdev = pdev;
180 }
181
182 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
183 if (r == NULL) {
184 dev_err(&pdev->dev, "no memory resource defined\n");
185 ret = -ENODEV;
186 goto err_free;
187 }
188
189 r = request_mem_region(r->start, resource_size(r), pdev->name);
190 if (r == NULL) {
191 dev_err(&pdev->dev, "failed to request memory resource\n");
192 ret = -EBUSY;
193 goto err_free;
194 }
195
196 pwms[0].regbase = ioremap(r->start, resource_size(r));
197 if (pwms[0].regbase == NULL) {
198 dev_err(&pdev->dev, "failed to ioremap() registers\n");
199 ret = -ENODEV;
200 goto err_free_mem;
201 }
202
203 for (i = 1; i < VT8500_NR_PWMS; i++)
204 pwms[i].regbase = pwms[0].regbase;
205
206 for (i = 0; i < VT8500_NR_PWMS; i++)
207 __add_pwm(&pwms[i]);
208
209 platform_set_drvdata(pdev, pwms);
210 return 0;
211
212err_free_mem:
213 release_mem_region(r->start, resource_size(r));
214err_free:
215 kfree(pwms);
216 return ret;
217}
218
219static int __devexit pwm_remove(struct platform_device *pdev)
220{
221 struct pwm_device *pwms;
222 struct resource *r;
223 int i;
224
225 pwms = platform_get_drvdata(pdev);
226 if (pwms == NULL)
227 return -ENODEV;
228
229 mutex_lock(&pwm_lock);
230
231 for (i = 0; i < VT8500_NR_PWMS; i++)
232 list_del(&pwms[i].node);
233 mutex_unlock(&pwm_lock);
234
235 iounmap(pwms[0].regbase);
236
237 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
238 release_mem_region(r->start, resource_size(r));
239
240 kfree(pwms);
241 return 0;
242}
243
244static struct platform_driver pwm_driver = {
245 .driver = {
246 .name = "vt8500-pwm",
247 .owner = THIS_MODULE,
248 },
249 .probe = pwm_probe,
250 .remove = __devexit_p(pwm_remove),
251};
252
253static int __init pwm_init(void)
254{
255 return platform_driver_register(&pwm_driver);
256}
257arch_initcall(pwm_init);
258
259static void __exit pwm_exit(void)
260{
261 platform_driver_unregister(&pwm_driver);
262}
263module_exit(pwm_exit);
264
265MODULE_LICENSE("GPL");