diff options
author | Xiubo Li <Li.Xiubo@freescale.com> | 2014-02-27 04:39:49 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2014-03-18 13:01:56 -0400 |
commit | b505183b5117ce149c65ae62f8c00e889acafa69 (patch) | |
tree | ffd76e3d6cbab118f9cac96843afac1ec8d4e196 /drivers/pwm | |
parent | 8468949cddcdbb1b1b1bc552aefceb252078ceb1 (diff) |
pwm: Add Freescale FTM PWM driver support
The FTM PWM device can be found on Vybrid VF610 Tower and
Layerscape LS-1 SoCs.
Signed-off-by: Xiubo Li <Li.Xiubo@freescale.com>
Signed-off-by: Alison Wang <b18965@freescale.com>
Signed-off-by: Jingchang Lu <b35083@freescale.com>
Reviewed-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Yuan Yao <yao.yuan@freescale.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-fsl-ftm.c | 495 |
3 files changed, 506 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 0b7a3c96f639..17935628383a 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
@@ -80,6 +80,16 @@ config PWM_EP93XX | |||
80 | To compile this driver as a module, choose M here: the module | 80 | To compile this driver as a module, choose M here: the module |
81 | will be called pwm-ep93xx. | 81 | will be called pwm-ep93xx. |
82 | 82 | ||
83 | config PWM_FSL_FTM | ||
84 | tristate "Freescale FlexTimer Module (FTM) PWM support" | ||
85 | depends on OF | ||
86 | help | ||
87 | Generic FTM PWM framework driver for Freescale VF610 and | ||
88 | Layerscape LS-1 SoCs. | ||
89 | |||
90 | To compile this driver as a module, choose M here: the module | ||
91 | will be called pwm-fsl-ftm. | ||
92 | |||
83 | config PWM_IMX | 93 | config PWM_IMX |
84 | tristate "i.MX PWM support" | 94 | tristate "i.MX PWM support" |
85 | depends on ARCH_MXC | 95 | depends on ARCH_MXC |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index d8906ec69976..7a10e05acb48 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o | |||
5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o | 5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o |
6 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o | 6 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
7 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o | 7 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o |
8 | obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o | ||
8 | obj-$(CONFIG_PWM_IMX) += pwm-imx.o | 9 | obj-$(CONFIG_PWM_IMX) += pwm-imx.o |
9 | obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o | 10 | obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o |
10 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o | 11 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o |
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c new file mode 100644 index 000000000000..420169e96b5f --- /dev/null +++ b/drivers/pwm/pwm-fsl-ftm.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * Freescale FlexTimer Module (FTM) PWM Driver | ||
3 | * | ||
4 | * Copyright 2012-2013 Freescale Semiconductor, Inc. | ||
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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/mutex.h> | ||
18 | #include <linux/of_address.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/pwm.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #define FTM_SC 0x00 | ||
24 | #define FTM_SC_CLK_MASK 0x3 | ||
25 | #define FTM_SC_CLK_SHIFT 3 | ||
26 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) | ||
27 | #define FTM_SC_PS_MASK 0x7 | ||
28 | #define FTM_SC_PS_SHIFT 0 | ||
29 | |||
30 | #define FTM_CNT 0x04 | ||
31 | #define FTM_MOD 0x08 | ||
32 | |||
33 | #define FTM_CSC_BASE 0x0C | ||
34 | #define FTM_CSC_MSB BIT(5) | ||
35 | #define FTM_CSC_MSA BIT(4) | ||
36 | #define FTM_CSC_ELSB BIT(3) | ||
37 | #define FTM_CSC_ELSA BIT(2) | ||
38 | #define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) | ||
39 | |||
40 | #define FTM_CV_BASE 0x10 | ||
41 | #define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) | ||
42 | |||
43 | #define FTM_CNTIN 0x4C | ||
44 | #define FTM_STATUS 0x50 | ||
45 | |||
46 | #define FTM_MODE 0x54 | ||
47 | #define FTM_MODE_FTMEN BIT(0) | ||
48 | #define FTM_MODE_INIT BIT(2) | ||
49 | #define FTM_MODE_PWMSYNC BIT(3) | ||
50 | |||
51 | #define FTM_SYNC 0x58 | ||
52 | #define FTM_OUTINIT 0x5C | ||
53 | #define FTM_OUTMASK 0x60 | ||
54 | #define FTM_COMBINE 0x64 | ||
55 | #define FTM_DEADTIME 0x68 | ||
56 | #define FTM_EXTTRIG 0x6C | ||
57 | #define FTM_POL 0x70 | ||
58 | #define FTM_FMS 0x74 | ||
59 | #define FTM_FILTER 0x78 | ||
60 | #define FTM_FLTCTRL 0x7C | ||
61 | #define FTM_QDCTRL 0x80 | ||
62 | #define FTM_CONF 0x84 | ||
63 | #define FTM_FLTPOL 0x88 | ||
64 | #define FTM_SYNCONF 0x8C | ||
65 | #define FTM_INVCTRL 0x90 | ||
66 | #define FTM_SWOCTRL 0x94 | ||
67 | #define FTM_PWMLOAD 0x98 | ||
68 | |||
69 | enum fsl_pwm_clk { | ||
70 | FSL_PWM_CLK_SYS, | ||
71 | FSL_PWM_CLK_FIX, | ||
72 | FSL_PWM_CLK_EXT, | ||
73 | FSL_PWM_CLK_CNTEN, | ||
74 | FSL_PWM_CLK_MAX | ||
75 | }; | ||
76 | |||
77 | struct fsl_pwm_chip { | ||
78 | struct pwm_chip chip; | ||
79 | |||
80 | struct mutex lock; | ||
81 | |||
82 | unsigned int use_count; | ||
83 | unsigned int cnt_select; | ||
84 | unsigned int clk_ps; | ||
85 | |||
86 | void __iomem *base; | ||
87 | |||
88 | int period_ns; | ||
89 | |||
90 | struct clk *clk[FSL_PWM_CLK_MAX]; | ||
91 | }; | ||
92 | |||
93 | static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) | ||
94 | { | ||
95 | return container_of(chip, struct fsl_pwm_chip, chip); | ||
96 | } | ||
97 | |||
98 | static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
99 | { | ||
100 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
101 | |||
102 | return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); | ||
103 | } | ||
104 | |||
105 | static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
106 | { | ||
107 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
108 | |||
109 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); | ||
110 | } | ||
111 | |||
112 | static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc, | ||
113 | enum fsl_pwm_clk index) | ||
114 | { | ||
115 | unsigned long sys_rate, cnt_rate; | ||
116 | unsigned long long ratio; | ||
117 | |||
118 | sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]); | ||
119 | if (!sys_rate) | ||
120 | return -EINVAL; | ||
121 | |||
122 | cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]); | ||
123 | if (!cnt_rate) | ||
124 | return -EINVAL; | ||
125 | |||
126 | switch (index) { | ||
127 | case FSL_PWM_CLK_SYS: | ||
128 | fpc->clk_ps = 1; | ||
129 | break; | ||
130 | case FSL_PWM_CLK_FIX: | ||
131 | ratio = 2 * cnt_rate - 1; | ||
132 | do_div(ratio, sys_rate); | ||
133 | fpc->clk_ps = ratio; | ||
134 | break; | ||
135 | case FSL_PWM_CLK_EXT: | ||
136 | ratio = 4 * cnt_rate - 1; | ||
137 | do_div(ratio, sys_rate); | ||
138 | fpc->clk_ps = ratio; | ||
139 | break; | ||
140 | default: | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc, | ||
148 | unsigned long period_ns) | ||
149 | { | ||
150 | unsigned long long c, c0; | ||
151 | |||
152 | c = clk_get_rate(fpc->clk[fpc->cnt_select]); | ||
153 | c = c * period_ns; | ||
154 | do_div(c, 1000000000UL); | ||
155 | |||
156 | do { | ||
157 | c0 = c; | ||
158 | do_div(c0, (1 << fpc->clk_ps)); | ||
159 | if (c0 <= 0xFFFF) | ||
160 | return (unsigned long)c0; | ||
161 | } while (++fpc->clk_ps < 8); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc, | ||
167 | unsigned long period_ns, | ||
168 | enum fsl_pwm_clk index) | ||
169 | { | ||
170 | int ret; | ||
171 | |||
172 | ret = fsl_pwm_calculate_default_ps(fpc, index); | ||
173 | if (ret) { | ||
174 | dev_err(fpc->chip.dev, | ||
175 | "failed to calculate default prescaler: %d\n", | ||
176 | ret); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | return fsl_pwm_calculate_cycles(fpc, period_ns); | ||
181 | } | ||
182 | |||
183 | static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, | ||
184 | unsigned long period_ns) | ||
185 | { | ||
186 | enum fsl_pwm_clk m0, m1; | ||
187 | unsigned long fix_rate, ext_rate, cycles; | ||
188 | |||
189 | cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, | ||
190 | FSL_PWM_CLK_SYS); | ||
191 | if (cycles) { | ||
192 | fpc->cnt_select = FSL_PWM_CLK_SYS; | ||
193 | return cycles; | ||
194 | } | ||
195 | |||
196 | fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); | ||
197 | ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); | ||
198 | |||
199 | if (fix_rate > ext_rate) { | ||
200 | m0 = FSL_PWM_CLK_FIX; | ||
201 | m1 = FSL_PWM_CLK_EXT; | ||
202 | } else { | ||
203 | m0 = FSL_PWM_CLK_EXT; | ||
204 | m1 = FSL_PWM_CLK_FIX; | ||
205 | } | ||
206 | |||
207 | cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0); | ||
208 | if (cycles) { | ||
209 | fpc->cnt_select = m0; | ||
210 | return cycles; | ||
211 | } | ||
212 | |||
213 | fpc->cnt_select = m1; | ||
214 | |||
215 | return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1); | ||
216 | } | ||
217 | |||
218 | static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, | ||
219 | unsigned long period_ns, | ||
220 | unsigned long duty_ns) | ||
221 | { | ||
222 | unsigned long long val, duty; | ||
223 | |||
224 | val = readl(fpc->base + FTM_MOD); | ||
225 | duty = duty_ns * (val + 1); | ||
226 | do_div(duty, period_ns); | ||
227 | |||
228 | return (unsigned long)duty; | ||
229 | } | ||
230 | |||
231 | static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
232 | int duty_ns, int period_ns) | ||
233 | { | ||
234 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
235 | u32 val, period, duty; | ||
236 | |||
237 | mutex_lock(&fpc->lock); | ||
238 | |||
239 | /* | ||
240 | * The Freescale FTM controller supports only a single period for | ||
241 | * all PWM channels, therefore incompatible changes need to be | ||
242 | * refused. | ||
243 | */ | ||
244 | if (fpc->period_ns && fpc->period_ns != period_ns) { | ||
245 | dev_err(fpc->chip.dev, | ||
246 | "conflicting period requested for PWM %u\n", | ||
247 | pwm->hwpwm); | ||
248 | mutex_unlock(&fpc->lock); | ||
249 | return -EBUSY; | ||
250 | } | ||
251 | |||
252 | if (!fpc->period_ns && duty_ns) { | ||
253 | period = fsl_pwm_calculate_period(fpc, period_ns); | ||
254 | if (!period) { | ||
255 | dev_err(fpc->chip.dev, "failed to calculate period\n"); | ||
256 | mutex_unlock(&fpc->lock); | ||
257 | return -EINVAL; | ||
258 | } | ||
259 | |||
260 | val = readl(fpc->base + FTM_SC); | ||
261 | val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); | ||
262 | val |= fpc->clk_ps; | ||
263 | writel(val, fpc->base + FTM_SC); | ||
264 | writel(period - 1, fpc->base + FTM_MOD); | ||
265 | |||
266 | fpc->period_ns = period_ns; | ||
267 | } | ||
268 | |||
269 | mutex_unlock(&fpc->lock); | ||
270 | |||
271 | duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); | ||
272 | |||
273 | writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); | ||
274 | writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int fsl_pwm_set_polarity(struct pwm_chip *chip, | ||
280 | struct pwm_device *pwm, | ||
281 | enum pwm_polarity polarity) | ||
282 | { | ||
283 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
284 | u32 val; | ||
285 | |||
286 | val = readl(fpc->base + FTM_POL); | ||
287 | |||
288 | if (polarity == PWM_POLARITY_INVERSED) | ||
289 | val |= BIT(pwm->hwpwm); | ||
290 | else | ||
291 | val &= ~BIT(pwm->hwpwm); | ||
292 | |||
293 | writel(val, fpc->base + FTM_POL); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | ||
299 | { | ||
300 | u32 val; | ||
301 | int ret; | ||
302 | |||
303 | if (fpc->use_count != 0) | ||
304 | return 0; | ||
305 | |||
306 | /* select counter clock source */ | ||
307 | val = readl(fpc->base + FTM_SC); | ||
308 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | ||
309 | val |= FTM_SC_CLK(fpc->cnt_select); | ||
310 | writel(val, fpc->base + FTM_SC); | ||
311 | |||
312 | ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); | ||
313 | if (ret) | ||
314 | return ret; | ||
315 | |||
316 | ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); | ||
317 | if (ret) { | ||
318 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); | ||
319 | return ret; | ||
320 | } | ||
321 | |||
322 | fpc->use_count++; | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
328 | { | ||
329 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
330 | u32 val; | ||
331 | int ret; | ||
332 | |||
333 | mutex_lock(&fpc->lock); | ||
334 | val = readl(fpc->base + FTM_OUTMASK); | ||
335 | val &= ~BIT(pwm->hwpwm); | ||
336 | writel(val, fpc->base + FTM_OUTMASK); | ||
337 | |||
338 | ret = fsl_counter_clock_enable(fpc); | ||
339 | mutex_unlock(&fpc->lock); | ||
340 | |||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) | ||
345 | { | ||
346 | u32 val; | ||
347 | |||
348 | /* | ||
349 | * already disabled, do nothing | ||
350 | */ | ||
351 | if (fpc->use_count == 0) | ||
352 | return; | ||
353 | |||
354 | /* there are still users, so can't disable yet */ | ||
355 | if (--fpc->use_count > 0) | ||
356 | return; | ||
357 | |||
358 | /* no users left, disable PWM counter clock */ | ||
359 | val = readl(fpc->base + FTM_SC); | ||
360 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | ||
361 | writel(val, fpc->base + FTM_SC); | ||
362 | |||
363 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); | ||
364 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); | ||
365 | } | ||
366 | |||
367 | static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
368 | { | ||
369 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | ||
370 | u32 val; | ||
371 | |||
372 | mutex_lock(&fpc->lock); | ||
373 | val = readl(fpc->base + FTM_OUTMASK); | ||
374 | val |= BIT(pwm->hwpwm); | ||
375 | writel(val, fpc->base + FTM_OUTMASK); | ||
376 | |||
377 | fsl_counter_clock_disable(fpc); | ||
378 | |||
379 | val = readl(fpc->base + FTM_OUTMASK); | ||
380 | |||
381 | if ((val & 0xFF) == 0xFF) | ||
382 | fpc->period_ns = 0; | ||
383 | |||
384 | mutex_unlock(&fpc->lock); | ||
385 | } | ||
386 | |||
387 | static const struct pwm_ops fsl_pwm_ops = { | ||
388 | .request = fsl_pwm_request, | ||
389 | .free = fsl_pwm_free, | ||
390 | .config = fsl_pwm_config, | ||
391 | .set_polarity = fsl_pwm_set_polarity, | ||
392 | .enable = fsl_pwm_enable, | ||
393 | .disable = fsl_pwm_disable, | ||
394 | .owner = THIS_MODULE, | ||
395 | }; | ||
396 | |||
397 | static int fsl_pwm_init(struct fsl_pwm_chip *fpc) | ||
398 | { | ||
399 | int ret; | ||
400 | |||
401 | ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); | ||
402 | if (ret) | ||
403 | return ret; | ||
404 | |||
405 | writel(0x00, fpc->base + FTM_CNTIN); | ||
406 | writel(0x00, fpc->base + FTM_OUTINIT); | ||
407 | writel(0xFF, fpc->base + FTM_OUTMASK); | ||
408 | |||
409 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static int fsl_pwm_probe(struct platform_device *pdev) | ||
415 | { | ||
416 | struct fsl_pwm_chip *fpc; | ||
417 | struct resource *res; | ||
418 | int ret; | ||
419 | |||
420 | fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); | ||
421 | if (!fpc) | ||
422 | return -ENOMEM; | ||
423 | |||
424 | mutex_init(&fpc->lock); | ||
425 | |||
426 | fpc->chip.dev = &pdev->dev; | ||
427 | |||
428 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
429 | fpc->base = devm_ioremap_resource(&pdev->dev, res); | ||
430 | if (IS_ERR(fpc->base)) | ||
431 | return PTR_ERR(fpc->base); | ||
432 | |||
433 | fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); | ||
434 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { | ||
435 | dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n"); | ||
436 | return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]); | ||
437 | } | ||
438 | |||
439 | fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix"); | ||
440 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX])) | ||
441 | return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]); | ||
442 | |||
443 | fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext"); | ||
444 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT])) | ||
445 | return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]); | ||
446 | |||
447 | fpc->clk[FSL_PWM_CLK_CNTEN] = | ||
448 | devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en"); | ||
449 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN])) | ||
450 | return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]); | ||
451 | |||
452 | fpc->chip.ops = &fsl_pwm_ops; | ||
453 | fpc->chip.of_xlate = of_pwm_xlate_with_flags; | ||
454 | fpc->chip.of_pwm_n_cells = 3; | ||
455 | fpc->chip.base = -1; | ||
456 | fpc->chip.npwm = 8; | ||
457 | |||
458 | ret = pwmchip_add(&fpc->chip); | ||
459 | if (ret < 0) { | ||
460 | dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); | ||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | platform_set_drvdata(pdev, fpc); | ||
465 | |||
466 | return fsl_pwm_init(fpc); | ||
467 | } | ||
468 | |||
469 | static int fsl_pwm_remove(struct platform_device *pdev) | ||
470 | { | ||
471 | struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev); | ||
472 | |||
473 | return pwmchip_remove(&fpc->chip); | ||
474 | } | ||
475 | |||
476 | static const struct of_device_id fsl_pwm_dt_ids[] = { | ||
477 | { .compatible = "fsl,vf610-ftm-pwm", }, | ||
478 | { /* sentinel */ } | ||
479 | }; | ||
480 | MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids); | ||
481 | |||
482 | static struct platform_driver fsl_pwm_driver = { | ||
483 | .driver = { | ||
484 | .name = "fsl-ftm-pwm", | ||
485 | .of_match_table = fsl_pwm_dt_ids, | ||
486 | }, | ||
487 | .probe = fsl_pwm_probe, | ||
488 | .remove = fsl_pwm_remove, | ||
489 | }; | ||
490 | module_platform_driver(fsl_pwm_driver); | ||
491 | |||
492 | MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver"); | ||
493 | MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>"); | ||
494 | MODULE_ALIAS("platform:fsl-ftm-pwm"); | ||
495 | MODULE_LICENSE("GPL"); | ||