pwm: fsl-ftm: Clean up the code
[linux-block.git] / drivers / pwm / pwm-fsl-ftm.c
CommitLineData
b505183b
XL
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
cd6d92d2
XL
24#define FTM_SC_CLK_MASK_SHIFT 3
25#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT)
26#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT)
b505183b 27#define FTM_SC_PS_MASK 0x7
b505183b
XL
28
29#define FTM_CNT 0x04
30#define FTM_MOD 0x08
31
32#define FTM_CSC_BASE 0x0C
33#define FTM_CSC_MSB BIT(5)
34#define FTM_CSC_MSA BIT(4)
35#define FTM_CSC_ELSB BIT(3)
36#define FTM_CSC_ELSA BIT(2)
37#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8))
38
39#define FTM_CV_BASE 0x10
40#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8))
41
42#define FTM_CNTIN 0x4C
43#define FTM_STATUS 0x50
44
45#define FTM_MODE 0x54
46#define FTM_MODE_FTMEN BIT(0)
47#define FTM_MODE_INIT BIT(2)
48#define FTM_MODE_PWMSYNC BIT(3)
49
50#define FTM_SYNC 0x58
51#define FTM_OUTINIT 0x5C
52#define FTM_OUTMASK 0x60
53#define FTM_COMBINE 0x64
54#define FTM_DEADTIME 0x68
55#define FTM_EXTTRIG 0x6C
56#define FTM_POL 0x70
57#define FTM_FMS 0x74
58#define FTM_FILTER 0x78
59#define FTM_FLTCTRL 0x7C
60#define FTM_QDCTRL 0x80
61#define FTM_CONF 0x84
62#define FTM_FLTPOL 0x88
63#define FTM_SYNCONF 0x8C
64#define FTM_INVCTRL 0x90
65#define FTM_SWOCTRL 0x94
66#define FTM_PWMLOAD 0x98
67
68enum fsl_pwm_clk {
69 FSL_PWM_CLK_SYS,
70 FSL_PWM_CLK_FIX,
71 FSL_PWM_CLK_EXT,
72 FSL_PWM_CLK_CNTEN,
73 FSL_PWM_CLK_MAX
74};
75
76struct fsl_pwm_chip {
77 struct pwm_chip chip;
78
79 struct mutex lock;
80
81 unsigned int use_count;
82 unsigned int cnt_select;
83 unsigned int clk_ps;
84
85 void __iomem *base;
86
87 int period_ns;
88
89 struct clk *clk[FSL_PWM_CLK_MAX];
90};
91
92static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
93{
94 return container_of(chip, struct fsl_pwm_chip, chip);
95}
96
97static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
98{
99 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
100
101 return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
102}
103
104static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
105{
106 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
107
108 clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
109}
110
111static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc,
112 enum fsl_pwm_clk index)
113{
114 unsigned long sys_rate, cnt_rate;
115 unsigned long long ratio;
116
117 sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]);
118 if (!sys_rate)
119 return -EINVAL;
120
121 cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]);
122 if (!cnt_rate)
123 return -EINVAL;
124
125 switch (index) {
126 case FSL_PWM_CLK_SYS:
127 fpc->clk_ps = 1;
128 break;
129 case FSL_PWM_CLK_FIX:
130 ratio = 2 * cnt_rate - 1;
131 do_div(ratio, sys_rate);
132 fpc->clk_ps = ratio;
133 break;
134 case FSL_PWM_CLK_EXT:
135 ratio = 4 * cnt_rate - 1;
136 do_div(ratio, sys_rate);
137 fpc->clk_ps = ratio;
138 break;
139 default:
140 return -EINVAL;
141 }
142
143 return 0;
144}
145
146static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc,
147 unsigned long period_ns)
148{
149 unsigned long long c, c0;
150
151 c = clk_get_rate(fpc->clk[fpc->cnt_select]);
152 c = c * period_ns;
153 do_div(c, 1000000000UL);
154
155 do {
156 c0 = c;
157 do_div(c0, (1 << fpc->clk_ps));
158 if (c0 <= 0xFFFF)
159 return (unsigned long)c0;
160 } while (++fpc->clk_ps < 8);
161
162 return 0;
163}
164
165static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc,
166 unsigned long period_ns,
167 enum fsl_pwm_clk index)
168{
169 int ret;
170
171 ret = fsl_pwm_calculate_default_ps(fpc, index);
172 if (ret) {
173 dev_err(fpc->chip.dev,
174 "failed to calculate default prescaler: %d\n",
175 ret);
176 return 0;
177 }
178
179 return fsl_pwm_calculate_cycles(fpc, period_ns);
180}
181
182static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc,
183 unsigned long period_ns)
184{
185 enum fsl_pwm_clk m0, m1;
186 unsigned long fix_rate, ext_rate, cycles;
187
188 cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns,
189 FSL_PWM_CLK_SYS);
190 if (cycles) {
191 fpc->cnt_select = FSL_PWM_CLK_SYS;
192 return cycles;
193 }
194
195 fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]);
196 ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]);
197
198 if (fix_rate > ext_rate) {
199 m0 = FSL_PWM_CLK_FIX;
200 m1 = FSL_PWM_CLK_EXT;
201 } else {
202 m0 = FSL_PWM_CLK_EXT;
203 m1 = FSL_PWM_CLK_FIX;
204 }
205
206 cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0);
207 if (cycles) {
208 fpc->cnt_select = m0;
209 return cycles;
210 }
211
212 fpc->cnt_select = m1;
213
214 return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1);
215}
216
217static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
218 unsigned long period_ns,
219 unsigned long duty_ns)
220{
221 unsigned long long val, duty;
222
223 val = readl(fpc->base + FTM_MOD);
224 duty = duty_ns * (val + 1);
225 do_div(duty, period_ns);
226
227 return (unsigned long)duty;
228}
229
230static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
231 int duty_ns, int period_ns)
232{
233 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
234 u32 val, period, duty;
235
236 mutex_lock(&fpc->lock);
237
238 /*
239 * The Freescale FTM controller supports only a single period for
240 * all PWM channels, therefore incompatible changes need to be
241 * refused.
242 */
243 if (fpc->period_ns && fpc->period_ns != period_ns) {
244 dev_err(fpc->chip.dev,
245 "conflicting period requested for PWM %u\n",
246 pwm->hwpwm);
247 mutex_unlock(&fpc->lock);
248 return -EBUSY;
249 }
250
251 if (!fpc->period_ns && duty_ns) {
252 period = fsl_pwm_calculate_period(fpc, period_ns);
253 if (!period) {
254 dev_err(fpc->chip.dev, "failed to calculate period\n");
255 mutex_unlock(&fpc->lock);
256 return -EINVAL;
257 }
258
259 val = readl(fpc->base + FTM_SC);
cd6d92d2 260 val &= ~FTM_SC_PS_MASK;
b505183b
XL
261 val |= fpc->clk_ps;
262 writel(val, fpc->base + FTM_SC);
263 writel(period - 1, fpc->base + FTM_MOD);
264
265 fpc->period_ns = period_ns;
266 }
267
268 mutex_unlock(&fpc->lock);
269
270 duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
271
272 writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
273 writel(duty, fpc->base + FTM_CV(pwm->hwpwm));
274
275 return 0;
276}
277
278static int fsl_pwm_set_polarity(struct pwm_chip *chip,
279 struct pwm_device *pwm,
280 enum pwm_polarity polarity)
281{
282 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
283 u32 val;
284
285 val = readl(fpc->base + FTM_POL);
286
287 if (polarity == PWM_POLARITY_INVERSED)
288 val |= BIT(pwm->hwpwm);
289 else
290 val &= ~BIT(pwm->hwpwm);
291
292 writel(val, fpc->base + FTM_POL);
293
294 return 0;
295}
296
297static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
298{
299 u32 val;
300 int ret;
301
302 if (fpc->use_count != 0)
303 return 0;
304
305 /* select counter clock source */
306 val = readl(fpc->base + FTM_SC);
cd6d92d2 307 val &= ~FTM_SC_CLK_MASK;
b505183b
XL
308 val |= FTM_SC_CLK(fpc->cnt_select);
309 writel(val, fpc->base + FTM_SC);
310
311 ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
312 if (ret)
313 return ret;
314
315 ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
316 if (ret) {
317 clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
318 return ret;
319 }
320
321 fpc->use_count++;
322
323 return 0;
324}
325
326static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
327{
328 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
329 u32 val;
330 int ret;
331
332 mutex_lock(&fpc->lock);
333 val = readl(fpc->base + FTM_OUTMASK);
334 val &= ~BIT(pwm->hwpwm);
335 writel(val, fpc->base + FTM_OUTMASK);
336
337 ret = fsl_counter_clock_enable(fpc);
338 mutex_unlock(&fpc->lock);
339
340 return ret;
341}
342
343static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
344{
345 u32 val;
346
347 /*
348 * already disabled, do nothing
349 */
350 if (fpc->use_count == 0)
351 return;
352
353 /* there are still users, so can't disable yet */
354 if (--fpc->use_count > 0)
355 return;
356
357 /* no users left, disable PWM counter clock */
358 val = readl(fpc->base + FTM_SC);
cd6d92d2 359 val &= ~FTM_SC_CLK_MASK;
b505183b
XL
360 writel(val, fpc->base + FTM_SC);
361
362 clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
363 clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
364}
365
366static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
367{
368 struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
369 u32 val;
370
371 mutex_lock(&fpc->lock);
372 val = readl(fpc->base + FTM_OUTMASK);
373 val |= BIT(pwm->hwpwm);
374 writel(val, fpc->base + FTM_OUTMASK);
375
376 fsl_counter_clock_disable(fpc);
377
378 val = readl(fpc->base + FTM_OUTMASK);
379
380 if ((val & 0xFF) == 0xFF)
381 fpc->period_ns = 0;
382
383 mutex_unlock(&fpc->lock);
384}
385
386static const struct pwm_ops fsl_pwm_ops = {
387 .request = fsl_pwm_request,
388 .free = fsl_pwm_free,
389 .config = fsl_pwm_config,
390 .set_polarity = fsl_pwm_set_polarity,
391 .enable = fsl_pwm_enable,
392 .disable = fsl_pwm_disable,
393 .owner = THIS_MODULE,
394};
395
396static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
397{
398 int ret;
399
400 ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
401 if (ret)
402 return ret;
403
404 writel(0x00, fpc->base + FTM_CNTIN);
405 writel(0x00, fpc->base + FTM_OUTINIT);
406 writel(0xFF, fpc->base + FTM_OUTMASK);
407
408 clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
409
410 return 0;
411}
412
413static int fsl_pwm_probe(struct platform_device *pdev)
414{
415 struct fsl_pwm_chip *fpc;
416 struct resource *res;
417 int ret;
418
419 fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
420 if (!fpc)
421 return -ENOMEM;
422
423 mutex_init(&fpc->lock);
424
425 fpc->chip.dev = &pdev->dev;
426
427 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
428 fpc->base = devm_ioremap_resource(&pdev->dev, res);
429 if (IS_ERR(fpc->base))
430 return PTR_ERR(fpc->base);
431
432 fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
433 if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
434 dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n");
435 return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
436 }
437
438 fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix");
439 if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
440 return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
441
442 fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext");
443 if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
444 return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
445
446 fpc->clk[FSL_PWM_CLK_CNTEN] =
447 devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en");
448 if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
449 return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
450
451 fpc->chip.ops = &fsl_pwm_ops;
452 fpc->chip.of_xlate = of_pwm_xlate_with_flags;
453 fpc->chip.of_pwm_n_cells = 3;
454 fpc->chip.base = -1;
455 fpc->chip.npwm = 8;
39fd3f99 456 fpc->chip.can_sleep = true;
b505183b
XL
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
469static 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
476static const struct of_device_id fsl_pwm_dt_ids[] = {
477 { .compatible = "fsl,vf610-ftm-pwm", },
478 { /* sentinel */ }
479};
480MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
481
482static 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};
490module_platform_driver(fsl_pwm_driver);
491
492MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver");
493MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>");
494MODULE_ALIAS("platform:fsl-ftm-pwm");
495MODULE_LICENSE("GPL");