diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-30 12:22:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-30 12:22:37 -0400 |
commit | 9ec97169e7d6afe2f8206d694d1411fb3bb49853 (patch) | |
tree | 9d24c8cd440a312f96b70db5cdaaef1136787003 /arch/arm/mach-vt8500 | |
parent | a410963ba4c0c768302f0298e258b1ee940e8316 (diff) | |
parent | 19891b20e7c275feb92d669f4b1879861f7e8c25 (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/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-vt8500/pwm.c | 265 |
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 | ||
6 | obj-$(CONFIG_MACH_BV07) += bv07.o | 6 | obj-$(CONFIG_MACH_BV07) += bv07.o |
7 | obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o | 7 | obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o |
8 | |||
9 | obj-$(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 | |||
29 | static DEFINE_MUTEX(pwm_lock); | ||
30 | static LIST_HEAD(pwm_list); | ||
31 | |||
32 | struct 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) | ||
45 | static 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 | |||
56 | int 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 | } | ||
94 | EXPORT_SYMBOL(pwm_config); | ||
95 | |||
96 | int 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 | } | ||
102 | EXPORT_SYMBOL(pwm_enable); | ||
103 | |||
104 | void 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 | } | ||
109 | EXPORT_SYMBOL(pwm_disable); | ||
110 | |||
111 | struct 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 | } | ||
139 | EXPORT_SYMBOL(pwm_request); | ||
140 | |||
141 | void 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 | } | ||
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 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 | |||
212 | err_free_mem: | ||
213 | release_mem_region(r->start, resource_size(r)); | ||
214 | err_free: | ||
215 | kfree(pwms); | ||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static 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 | |||
244 | static 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 | |||
253 | static int __init pwm_init(void) | ||
254 | { | ||
255 | return platform_driver_register(&pwm_driver); | ||
256 | } | ||
257 | arch_initcall(pwm_init); | ||
258 | |||
259 | static void __exit pwm_exit(void) | ||
260 | { | ||
261 | platform_driver_unregister(&pwm_driver); | ||
262 | } | ||
263 | module_exit(pwm_exit); | ||
264 | |||
265 | MODULE_LICENSE("GPL"); | ||