aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authoreric miao <eric.miao@marvell.com>2008-04-13 16:44:04 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-05-19 11:25:39 -0400
commit75540c1ac3c7bd72ac8e092058f9714875239995 (patch)
tree9d6dc7cb389f879be36a1583ce8949023a55dd2d /arch/arm
parent1a189b97190d3f0f8cf0379a799d3555b2d648bb (diff)
[ARM] pxa: Add PXA support for PWM API
Patch mainly from Eric Miao, with minor edits by rmk. Note: PWM0 and PWM2 share the same register I/O space and clock gating on pxa{27x, 3xx}, thus PWM2 is treated in the driver as a child PWM of PWM0. And this is also true for PWM1/3. Signed-off-by: eric miao <eric.miao@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-pxa/Kconfig5
-rw-r--r--arch/arm/mach-pxa/Makefile1
-rw-r--r--arch/arm/mach-pxa/devices.c60
-rw-r--r--arch/arm/mach-pxa/devices.h5
-rw-r--r--arch/arm/mach-pxa/pwm.c299
-rw-r--r--arch/arm/mach-pxa/pxa25x.c6
-rw-r--r--arch/arm/mach-pxa/pxa27x.c5
-rw-r--r--arch/arm/mach-pxa/pxa3xx.c4
8 files changed, 382 insertions, 3 deletions
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 5da7a6820492..9c01505b1825 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -273,4 +273,9 @@ config PXA_SSP
273 tristate 273 tristate
274 help 274 help
275 Enable support for PXA2xx SSP ports 275 Enable support for PXA2xx SSP ports
276
277config PXA_PWM
278 tristate
279 help
280 Enable support for PXA2xx/PXA3xx PWM controllers
276endif 281endif
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 0e6d05bb81aa..02cbc3cfbe01 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o
10 10
11# Generic drivers that other drivers may depend upon 11# Generic drivers that other drivers may depend upon
12obj-$(CONFIG_PXA_SSP) += ssp.o 12obj-$(CONFIG_PXA_SSP) += ssp.o
13obj-$(CONFIG_PXA_PWM) += pwm.o
13 14
14# SoC-specific code 15# SoC-specific code
15obj-$(CONFIG_PXA25x) += mfp-pxa2xx.o pxa25x.o 16obj-$(CONFIG_PXA25x) += mfp-pxa2xx.o pxa25x.o
diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
index d6c05b6eab35..794a1076db73 100644
--- a/arch/arm/mach-pxa/devices.c
+++ b/arch/arm/mach-pxa/devices.c
@@ -280,6 +280,36 @@ struct platform_device pxa_device_rtc = {
280 280
281#ifdef CONFIG_PXA25x 281#ifdef CONFIG_PXA25x
282 282
283static struct resource pxa25x_resource_pwm0[] = {
284 [0] = {
285 .start = 0x40b00000,
286 .end = 0x40b0000f,
287 .flags = IORESOURCE_MEM,
288 },
289};
290
291struct platform_device pxa25x_device_pwm0 = {
292 .name = "pxa25x-pwm",
293 .id = 0,
294 .resource = pxa25x_resource_pwm0,
295 .num_resources = ARRAY_SIZE(pxa25x_resource_pwm0),
296};
297
298static struct resource pxa25x_resource_pwm1[] = {
299 [0] = {
300 .start = 0x40c00000,
301 .end = 0x40c0000f,
302 .flags = IORESOURCE_MEM,
303 },
304};
305
306struct platform_device pxa25x_device_pwm1 = {
307 .name = "pxa25x-pwm",
308 .id = 1,
309 .resource = pxa25x_resource_pwm1,
310 .num_resources = ARRAY_SIZE(pxa25x_resource_pwm1),
311};
312
283static u64 pxa25x_ssp_dma_mask = DMA_BIT_MASK(32); 313static u64 pxa25x_ssp_dma_mask = DMA_BIT_MASK(32);
284 314
285static struct resource pxa25x_resource_ssp[] = { 315static struct resource pxa25x_resource_ssp[] = {
@@ -568,6 +598,36 @@ struct platform_device pxa27x_device_ssp3 = {
568 .num_resources = ARRAY_SIZE(pxa27x_resource_ssp3), 598 .num_resources = ARRAY_SIZE(pxa27x_resource_ssp3),
569}; 599};
570 600
601static struct resource pxa27x_resource_pwm0[] = {
602 [0] = {
603 .start = 0x40b00000,
604 .end = 0x40b0001f,
605 .flags = IORESOURCE_MEM,
606 },
607};
608
609struct platform_device pxa27x_device_pwm0 = {
610 .name = "pxa27x-pwm",
611 .id = 0,
612 .resource = pxa27x_resource_pwm0,
613 .num_resources = ARRAY_SIZE(pxa27x_resource_pwm0),
614};
615
616static struct resource pxa27x_resource_pwm1[] = {
617 [0] = {
618 .start = 0x40c00000,
619 .end = 0x40c0001f,
620 .flags = IORESOURCE_MEM,
621 },
622};
623
624struct platform_device pxa27x_device_pwm1 = {
625 .name = "pxa27x-pwm",
626 .id = 1,
627 .resource = pxa27x_resource_pwm1,
628 .num_resources = ARRAY_SIZE(pxa27x_resource_pwm1),
629};
630
571static struct resource pxa27x_resource_camera[] = { 631static struct resource pxa27x_resource_camera[] = {
572 [0] = { 632 [0] = {
573 .start = 0x50000000, 633 .start = 0x50000000,
diff --git a/arch/arm/mach-pxa/devices.h b/arch/arm/mach-pxa/devices.h
index fcab017f27ee..e620a3373d42 100644
--- a/arch/arm/mach-pxa/devices.h
+++ b/arch/arm/mach-pxa/devices.h
@@ -24,4 +24,9 @@ extern struct platform_device pxa27x_device_ssp2;
24extern struct platform_device pxa27x_device_ssp3; 24extern struct platform_device pxa27x_device_ssp3;
25extern struct platform_device pxa3xx_device_ssp4; 25extern struct platform_device pxa3xx_device_ssp4;
26 26
27extern struct platform_device pxa25x_device_pwm0;
28extern struct platform_device pxa25x_device_pwm1;
29extern struct platform_device pxa27x_device_pwm0;
30extern struct platform_device pxa27x_device_pwm1;
31
27void __init pxa_register_device(struct platform_device *dev, void *data); 32void __init pxa_register_device(struct platform_device *dev, void *data);
diff --git a/arch/arm/mach-pxa/pwm.c b/arch/arm/mach-pxa/pwm.c
new file mode 100644
index 000000000000..70fb3ca6a058
--- /dev/null
+++ b/arch/arm/mach-pxa/pwm.c
@@ -0,0 +1,299 @@
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#include <asm/arch/pxa-regs.h>
24
25/* PWM registers and bits definitions */
26#define PWMCR (0x00)
27#define PWMDCR (0x04)
28#define PWMPCR (0x08)
29
30#define PWMCR_SD (1 << 6)
31#define PWMDCR_FD (1 << 10)
32
33struct pwm_device {
34 struct list_head node;
35 struct platform_device *pdev;
36
37 const char *label;
38 struct clk *clk;
39 void __iomem *mmio_base;
40
41 unsigned int use_count;
42 unsigned int pwm_id;
43};
44
45/*
46 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
47 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
48 */
49int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
50{
51 unsigned long long c;
52 unsigned long period_cycles, prescale, pv, dc;
53
54 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
55 return -EINVAL;
56
57 c = clk_get_rate(pwm->clk);
58 c = c * period_ns;
59 do_div(c, 1000000000);
60 period_cycles = c;
61
62 if (period_cycles < 0)
63 period_cycles = 1;
64 prescale = (period_cycles - 1) / 1024;
65 pv = period_cycles / (prescale + 1) - 1;
66
67 if (prescale > 63)
68 return -EINVAL;
69
70 if (duty_ns == period_ns)
71 dc = PWMDCR_FD;
72 else
73 dc = (pv + 1) * duty_ns / period_ns;
74
75 /* NOTE: the clock to PWM has to be enabled first
76 * before writing to the registers
77 */
78 clk_enable(pwm->clk);
79 __raw_writel(prescale, pwm->mmio_base + PWMCR);
80 __raw_writel(dc, pwm->mmio_base + PWMDCR);
81 __raw_writel(pv, pwm->mmio_base + PWMPCR);
82 clk_disable(pwm->clk);
83
84 return 0;
85}
86EXPORT_SYMBOL(pwm_config);
87
88int pwm_enable(struct pwm_device *pwm)
89{
90 return clk_enable(pwm->clk);
91}
92EXPORT_SYMBOL(pwm_enable);
93
94void pwm_disable(struct pwm_device *pwm)
95{
96 clk_disable(pwm->clk);
97}
98EXPORT_SYMBOL(pwm_disable);
99
100static DEFINE_MUTEX(pwm_lock);
101static LIST_HEAD(pwm_list);
102
103struct pwm_device *pwm_request(int pwm_id, const char *label)
104{
105 struct pwm_device *pwm;
106 int found = 0;
107
108 mutex_lock(&pwm_lock);
109
110 list_for_each_entry(pwm, &pwm_list, node) {
111 if (pwm->pwm_id == pwm_id && pwm->use_count == 0) {
112 pwm->use_count++;
113 pwm->label = label;
114 found = 1;
115 break;
116 }
117 }
118
119 mutex_unlock(&pwm_lock);
120
121 return (found) ? pwm : NULL;
122}
123EXPORT_SYMBOL(pwm_request);
124
125void pwm_free(struct pwm_device *pwm)
126{
127 mutex_lock(&pwm_lock);
128
129 if (pwm->use_count) {
130 pwm->use_count--;
131 pwm->label = NULL;
132 } else
133 pr_warning("PWM device already freed\n");
134
135 mutex_unlock(&pwm_lock);
136}
137EXPORT_SYMBOL(pwm_free);
138
139static inline void __add_pwm(struct pwm_device *pwm)
140{
141 mutex_lock(&pwm_lock);
142 list_add_tail(&pwm->node, &pwm_list);
143 mutex_unlock(&pwm_lock);
144}
145
146static struct pwm_device *pwm_probe(struct platform_device *pdev,
147 unsigned int pwm_id, struct pwm_device *parent_pwm)
148{
149 struct pwm_device *pwm;
150 struct resource *r;
151 int ret = 0;
152
153 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
154 if (pwm == NULL) {
155 dev_err(&pdev->dev, "failed to allocate memory\n");
156 return ERR_PTR(-ENOMEM);
157 }
158
159 pwm->clk = clk_get(&pdev->dev, "PWMCLK");
160 if (IS_ERR(pwm->clk)) {
161 ret = PTR_ERR(pwm->clk);
162 goto err_free;
163 }
164
165 pwm->use_count = 0;
166 pwm->pwm_id = pwm_id;
167 pwm->pdev = pdev;
168
169 if (parent_pwm != NULL) {
170 /* registers for the second PWM has offset of 0x10 */
171 pwm->mmio_base = parent_pwm->mmio_base + 0x10;
172 __add_pwm(pwm);
173 return pwm;
174 }
175
176 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
177 if (r == NULL) {
178 dev_err(&pdev->dev, "no memory resource defined\n");
179 ret = -ENODEV;
180 goto err_free_clk;
181 }
182
183 r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
184 if (r == NULL) {
185 dev_err(&pdev->dev, "failed to request memory resource\n");
186 ret = -EBUSY;
187 goto err_free_clk;
188 }
189
190 pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
191 if (pwm->mmio_base == NULL) {
192 dev_err(&pdev->dev, "failed to ioremap() registers\n");
193 ret = -ENODEV;
194 goto err_free_mem;
195 }
196
197 __add_pwm(pwm);
198 platform_set_drvdata(pdev, pwm);
199 return pwm;
200
201err_free_mem:
202 release_mem_region(r->start, r->end - r->start + 1);
203err_free_clk:
204 clk_put(pwm->clk);
205err_free:
206 kfree(pwm);
207 return ERR_PTR(ret);
208}
209
210static int __devinit pxa25x_pwm_probe(struct platform_device *pdev)
211{
212 struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL);
213
214 if (IS_ERR(pwm))
215 return PTR_ERR(pwm);
216
217 return 0;
218}
219
220static int __devinit pxa27x_pwm_probe(struct platform_device *pdev)
221{
222 struct pwm_device *pwm;
223
224 pwm = pwm_probe(pdev, pdev->id * 2, NULL);
225 if (IS_ERR(pwm))
226 return PTR_ERR(pwm);
227
228 pwm = pwm_probe(pdev, pdev->id * 2 + 1, pwm);
229 if (IS_ERR(pwm))
230 return PTR_ERR(pwm);
231
232 return 0;
233}
234
235static int __devexit pwm_remove(struct platform_device *pdev)
236{
237 struct pwm_device *pwm;
238 struct resource *r;
239
240 pwm = platform_get_drvdata(pdev);
241 if (pwm == NULL)
242 return -ENODEV;
243
244 mutex_lock(&pwm_lock);
245 list_del(&pwm->node);
246 mutex_unlock(&pwm_lock);
247
248 iounmap(pwm->mmio_base);
249
250 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251 release_mem_region(r->start, r->end - r->start + 1);
252
253 clk_put(pwm->clk);
254 kfree(pwm);
255 return 0;
256}
257
258static struct platform_driver pxa25x_pwm_driver = {
259 .driver = {
260 .name = "pxa25x-pwm",
261 },
262 .probe = pxa25x_pwm_probe,
263 .remove = __devexit_p(pwm_remove),
264};
265
266static struct platform_driver pxa27x_pwm_driver = {
267 .driver = {
268 .name = "pxa27x-pwm",
269 },
270 .probe = pxa27x_pwm_probe,
271 .remove = __devexit_p(pwm_remove),
272};
273
274static int __init pwm_init(void)
275{
276 int ret = 0;
277
278 ret = platform_driver_register(&pxa25x_pwm_driver);
279 if (ret) {
280 printk(KERN_ERR "failed to register pxa25x_pwm_driver\n");
281 return ret;
282 }
283
284 ret = platform_driver_register(&pxa27x_pwm_driver);
285 if (ret) {
286 printk(KERN_ERR "failed to register pxa27x_pwm_driver\n");
287 return ret;
288 }
289
290 return ret;
291}
292arch_initcall(pwm_init);
293
294static void __exit pwm_exit(void)
295{
296 platform_driver_unregister(&pxa25x_pwm_driver);
297 platform_driver_unregister(&pxa27x_pwm_driver);
298}
299module_exit(pwm_exit);
diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
index e5b417d14bb0..2bed3f98d41c 100644
--- a/arch/arm/mach-pxa/pxa25x.c
+++ b/arch/arm/mach-pxa/pxa25x.c
@@ -129,12 +129,12 @@ static struct clk pxa25x_clks[] = {
129 INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev), 129 INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev),
130 INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev), 130 INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev),
131 INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev), 131 INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev),
132 INIT_CKEN("PWMCLK", PWM0, 3686400, 0, &pxa25x_device_pwm0.dev),
133 INIT_CKEN("PWMCLK", PWM1, 3686400, 0, &pxa25x_device_pwm1.dev),
132 134
133 INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL), 135 INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
134 136
135 /* 137 /*
136 INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
137 INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
138 INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL), 138 INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
139 */ 139 */
140 INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL), 140 INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
@@ -269,6 +269,8 @@ static struct platform_device *pxa25x_devices[] __initdata = {
269 &pxa25x_device_ssp, 269 &pxa25x_device_ssp,
270 &pxa25x_device_nssp, 270 &pxa25x_device_nssp,
271 &pxa25x_device_assp, 271 &pxa25x_device_assp,
272 &pxa25x_device_pwm0,
273 &pxa25x_device_pwm1,
272}; 274};
273 275
274static struct sys_device pxa25x_sysdev[] = { 276static struct sys_device pxa25x_sysdev[] = {
diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index 7e945836e129..bc2e80f69673 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -157,12 +157,13 @@ static struct clk pxa27x_clks[] = {
157 INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), 157 INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
158 INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), 158 INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
159 INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), 159 INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
160 INIT_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
161 INIT_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
160 162
161 INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL), 163 INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
162 INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0, NULL), 164 INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0, NULL),
163 165
164 /* 166 /*
165 INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL),
166 INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL), 167 INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL),
167 INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL), 168 INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL),
168 INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL), 169 INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL),
@@ -363,6 +364,8 @@ static struct platform_device *devices[] __initdata = {
363 &pxa27x_device_ssp1, 364 &pxa27x_device_ssp1,
364 &pxa27x_device_ssp2, 365 &pxa27x_device_ssp2,
365 &pxa27x_device_ssp3, 366 &pxa27x_device_ssp3,
367 &pxa27x_device_pwm0,
368 &pxa27x_device_pwm1,
366}; 369};
367 370
368static struct sys_device pxa27x_sysdev[] = { 371static struct sys_device pxa27x_sysdev[] = {
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index 644550bfa330..0f717df1fdb2 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -239,6 +239,8 @@ static struct clk pxa3xx_clks[] = {
239 PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), 239 PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
240 PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), 240 PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
241 PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev), 241 PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev),
242 PXA3xx_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
243 PXA3xx_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
242 244
243 PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev), 245 PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev),
244 PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev), 246 PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev),
@@ -530,6 +532,8 @@ static struct platform_device *devices[] __initdata = {
530 &pxa27x_device_ssp2, 532 &pxa27x_device_ssp2,
531 &pxa27x_device_ssp3, 533 &pxa27x_device_ssp3,
532 &pxa3xx_device_ssp4, 534 &pxa3xx_device_ssp4,
535 &pxa27x_device_pwm0,
536 &pxa27x_device_pwm1,
533}; 537};
534 538
535static struct sys_device pxa3xx_sysdev[] = { 539static struct sys_device pxa3xx_sysdev[] = {