aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <anson.huang@nxp.com>2019-05-09 09:29:29 -0400
committerThierry Reding <thierry.reding@gmail.com>2019-05-09 11:01:48 -0400
commit738a1cfec2edb4a6c53ac34fcb29447227dd135e (patch)
treedaffcb27cd031e7cf6b2229af542ca1576826782
parent2caf03843609af9dd4aa9c3b0f434f35016d4e15 (diff)
pwm: Add i.MX TPM PWM driver support
i.MX7ULP has TPM(Low Power Timer/Pulse Width Modulation Module) inside, it can support multiple PWM channels, all the channels share same counter and period setting, but each channel can configure its duty and polarity independently. There are several TPM modules in i.MX7ULP, the number of channels in TPM modules are different, it can be read from each TPM module's PARAM register. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r--drivers/pwm/Kconfig11
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-imx-tpm.c449
3 files changed, 461 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index c054bd1dba36..1311b54089be 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -210,6 +210,17 @@ config PWM_IMX27
210 To compile this driver as a module, choose M here: the module 210 To compile this driver as a module, choose M here: the module
211 will be called pwm-imx27. 211 will be called pwm-imx27.
212 212
213config PWM_IMX_TPM
214 tristate "i.MX TPM PWM support"
215 depends on ARCH_MXC || COMPILE_TEST
216 depends on HAVE_CLK && HAS_IOMEM
217 help
218 Generic PWM framework driver for i.MX7ULP TPM module, TPM's full
219 name is Low Power Timer/Pulse Width Modulation Module.
220
221 To compile this driver as a module, choose M here: the module
222 will be called pwm-imx-tpm.
223
213config PWM_JZ4740 224config PWM_JZ4740
214 tristate "Ingenic JZ47xx PWM support" 225 tristate "Ingenic JZ47xx PWM support"
215 depends on MACH_INGENIC 226 depends on MACH_INGENIC
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 448825e892bc..c368599d36c0 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
19obj-$(CONFIG_PWM_IMG) += pwm-img.o 19obj-$(CONFIG_PWM_IMG) += pwm-img.o
20obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o 20obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
21obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o 21obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
22obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o
22obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o 23obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
23obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o 24obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
24obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o 25obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c
new file mode 100644
index 000000000000..e8385c1cf342
--- /dev/null
+++ b/drivers/pwm/pwm-imx-tpm.c
@@ -0,0 +1,449 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2018-2019 NXP.
4 *
5 * Limitations:
6 * - The TPM counter and period counter are shared between
7 * multiple channels, so all channels should use same period
8 * settings.
9 * - Changes to polarity cannot be latched at the time of the
10 * next period start.
11 * - Changing period and duty cycle together isn't atomic,
12 * with the wrong timing it might happen that a period is
13 * produced with old duty cycle but new period settings.
14 */
15
16#include <linux/bitfield.h>
17#include <linux/bitops.h>
18#include <linux/clk.h>
19#include <linux/err.h>
20#include <linux/io.h>
21#include <linux/log2.h>
22#include <linux/module.h>
23#include <linux/of.h>
24#include <linux/of_address.h>
25#include <linux/platform_device.h>
26#include <linux/pwm.h>
27#include <linux/slab.h>
28
29#define PWM_IMX_TPM_PARAM 0x4
30#define PWM_IMX_TPM_GLOBAL 0x8
31#define PWM_IMX_TPM_SC 0x10
32#define PWM_IMX_TPM_CNT 0x14
33#define PWM_IMX_TPM_MOD 0x18
34#define PWM_IMX_TPM_CnSC(n) (0x20 + (n) * 0x8)
35#define PWM_IMX_TPM_CnV(n) (0x24 + (n) * 0x8)
36
37#define PWM_IMX_TPM_PARAM_CHAN GENMASK(7, 0)
38
39#define PWM_IMX_TPM_SC_PS GENMASK(2, 0)
40#define PWM_IMX_TPM_SC_CMOD GENMASK(4, 3)
41#define PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK FIELD_PREP(PWM_IMX_TPM_SC_CMOD, 1)
42#define PWM_IMX_TPM_SC_CPWMS BIT(5)
43
44#define PWM_IMX_TPM_CnSC_CHF BIT(7)
45#define PWM_IMX_TPM_CnSC_MSB BIT(5)
46#define PWM_IMX_TPM_CnSC_MSA BIT(4)
47
48/*
49 * The reference manual describes this field as two separate bits. The
50 * semantic of the two bits isn't orthogonal though, so they are treated
51 * together as a 2-bit field here.
52 */
53#define PWM_IMX_TPM_CnSC_ELS GENMASK(3, 2)
54#define PWM_IMX_TPM_CnSC_ELS_INVERSED FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 1)
55#define PWM_IMX_TPM_CnSC_ELS_NORMAL FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 2)
56
57
58#define PWM_IMX_TPM_MOD_WIDTH 16
59#define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0)
60
61struct imx_tpm_pwm_chip {
62 struct pwm_chip chip;
63 struct clk *clk;
64 void __iomem *base;
65 struct mutex lock;
66 u32 user_count;
67 u32 enable_count;
68 u32 real_period;
69};
70
71struct imx_tpm_pwm_param {
72 u8 prescale;
73 u32 mod;
74 u32 val;
75};
76
77static inline struct imx_tpm_pwm_chip *
78to_imx_tpm_pwm_chip(struct pwm_chip *chip)
79{
80 return container_of(chip, struct imx_tpm_pwm_chip, chip);
81}
82
83/*
84 * This function determines for a given pwm_state *state that a consumer
85 * might request the pwm_state *real_state that eventually is implemented
86 * by the hardware and the necessary register values (in *p) to achieve
87 * this.
88 */
89static int pwm_imx_tpm_round_state(struct pwm_chip *chip,
90 struct imx_tpm_pwm_param *p,
91 struct pwm_state *real_state,
92 struct pwm_state *state)
93{
94 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
95 u32 rate, prescale, period_count, clock_unit;
96 u64 tmp;
97
98 rate = clk_get_rate(tpm->clk);
99 tmp = (u64)state->period * rate;
100 clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC);
101 if (clock_unit <= PWM_IMX_TPM_MOD_MOD)
102 prescale = 0;
103 else
104 prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH;
105
106 if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale)))
107 return -ERANGE;
108 p->prescale = prescale;
109
110 period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale;
111 p->mod = period_count;
112
113 /* calculate real period HW can support */
114 tmp = (u64)period_count << prescale;
115 tmp *= NSEC_PER_SEC;
116 real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
117
118 /*
119 * if eventually the PWM output is inactive, either
120 * duty cycle is 0 or status is disabled, need to
121 * make sure the output pin is inactive.
122 */
123 if (!state->enabled)
124 real_state->duty_cycle = 0;
125 else
126 real_state->duty_cycle = state->duty_cycle;
127
128 tmp = (u64)p->mod * real_state->duty_cycle;
129 p->val = DIV_ROUND_CLOSEST_ULL(tmp, real_state->period);
130
131 real_state->polarity = state->polarity;
132 real_state->enabled = state->enabled;
133
134 return 0;
135}
136
137static void pwm_imx_tpm_get_state(struct pwm_chip *chip,
138 struct pwm_device *pwm,
139 struct pwm_state *state)
140{
141 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
142 u32 rate, val, prescale;
143 u64 tmp;
144
145 /* get period */
146 state->period = tpm->real_period;
147
148 /* get duty cycle */
149 rate = clk_get_rate(tpm->clk);
150 val = readl(tpm->base + PWM_IMX_TPM_SC);
151 prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
152 tmp = readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
153 tmp = (tmp << prescale) * NSEC_PER_SEC;
154 state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
155
156 /* get polarity */
157 val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
158 if ((val & PWM_IMX_TPM_CnSC_ELS) == PWM_IMX_TPM_CnSC_ELS_INVERSED)
159 state->polarity = PWM_POLARITY_INVERSED;
160 else
161 /*
162 * Assume reserved values (2b00 and 2b11) to yield
163 * normal polarity.
164 */
165 state->polarity = PWM_POLARITY_NORMAL;
166
167 /* get channel status */
168 state->enabled = FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val) ? true : false;
169}
170
171/* this function is supposed to be called with mutex hold */
172static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
173 struct imx_tpm_pwm_param *p,
174 struct pwm_state *state,
175 struct pwm_device *pwm)
176{
177 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
178 bool period_update = false;
179 bool duty_update = false;
180 u32 val, cmod, cur_prescale;
181 unsigned long timeout;
182 struct pwm_state c;
183
184 if (state->period != tpm->real_period) {
185 /*
186 * TPM counter is shared by multiple channels, so
187 * prescale and period can NOT be modified when
188 * there are multiple channels in use with different
189 * period settings.
190 */
191 if (tpm->user_count > 1)
192 return -EBUSY;
193
194 val = readl(tpm->base + PWM_IMX_TPM_SC);
195 cmod = FIELD_GET(PWM_IMX_TPM_SC_CMOD, val);
196 cur_prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
197 if (cmod && cur_prescale != p->prescale)
198 return -EBUSY;
199
200 /* set TPM counter prescale */
201 val &= ~PWM_IMX_TPM_SC_PS;
202 val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
203 writel(val, tpm->base + PWM_IMX_TPM_SC);
204
205 /*
206 * set period count:
207 * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register
208 * is updated when MOD register is written.
209 *
210 * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the period length
211 * is latched into hardware when the next period starts.
212 */
213 writel(p->mod, tpm->base + PWM_IMX_TPM_MOD);
214 tpm->real_period = state->period;
215 period_update = true;
216 }
217
218 pwm_imx_tpm_get_state(chip, pwm, &c);
219
220 /* polarity is NOT allowed to be changed if PWM is active */
221 if (c.enabled && c.polarity != state->polarity)
222 return -EBUSY;
223
224 if (state->duty_cycle != c.duty_cycle) {
225 /*
226 * set channel value:
227 * if the PWM is disabled (CMOD[1:0] = 2b00), then CnV register
228 * is updated when CnV register is written.
229 *
230 * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the duty length
231 * is latched into hardware when the next period starts.
232 */
233 writel(p->val, tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
234 duty_update = true;
235 }
236
237 /* make sure MOD & CnV registers are updated */
238 if (period_update || duty_update) {
239 timeout = jiffies + msecs_to_jiffies(tpm->real_period /
240 NSEC_PER_MSEC + 1);
241 while (readl(tpm->base + PWM_IMX_TPM_MOD) != p->mod
242 || readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm))
243 != p->val) {
244 if (time_after(jiffies, timeout))
245 return -ETIME;
246 cpu_relax();
247 }
248 }
249
250 /*
251 * polarity settings will enabled/disable output status
252 * immediately, so if the channel is disabled, need to
253 * make sure MSA/MSB/ELS are set to 0 which means channel
254 * disabled.
255 */
256 val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
257 val &= ~(PWM_IMX_TPM_CnSC_ELS | PWM_IMX_TPM_CnSC_MSA |
258 PWM_IMX_TPM_CnSC_MSB);
259 if (state->enabled) {
260 /*
261 * set polarity (for edge-aligned PWM modes)
262 *
263 * ELS[1:0] = 2b10 yields normal polarity behaviour,
264 * ELS[1:0] = 2b01 yields inversed polarity.
265 * The other values are reserved.
266 */
267 val |= PWM_IMX_TPM_CnSC_MSB;
268 val |= (state->polarity == PWM_POLARITY_NORMAL) ?
269 PWM_IMX_TPM_CnSC_ELS_NORMAL :
270 PWM_IMX_TPM_CnSC_ELS_INVERSED;
271 }
272 writel(val, tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
273
274 /* control the counter status */
275 if (state->enabled != c.enabled) {
276 val = readl(tpm->base + PWM_IMX_TPM_SC);
277 if (state->enabled) {
278 if (++tpm->enable_count == 1)
279 val |= PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK;
280 } else {
281 if (--tpm->enable_count == 0)
282 val &= ~PWM_IMX_TPM_SC_CMOD;
283 }
284 writel(val, tpm->base + PWM_IMX_TPM_SC);
285 }
286
287 return 0;
288}
289
290static int pwm_imx_tpm_apply(struct pwm_chip *chip,
291 struct pwm_device *pwm,
292 struct pwm_state *state)
293{
294 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
295 struct imx_tpm_pwm_param param;
296 struct pwm_state real_state;
297 int ret;
298
299 ret = pwm_imx_tpm_round_state(chip, &param, &real_state, state);
300 if (ret)
301 return ret;
302
303 mutex_lock(&tpm->lock);
304 ret = pwm_imx_tpm_apply_hw(chip, &param, &real_state, pwm);
305 mutex_unlock(&tpm->lock);
306
307 return ret;
308}
309
310static int pwm_imx_tpm_request(struct pwm_chip *chip, struct pwm_device *pwm)
311{
312 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
313
314 mutex_lock(&tpm->lock);
315 tpm->user_count++;
316 mutex_unlock(&tpm->lock);
317
318 return 0;
319}
320
321static void pwm_imx_tpm_free(struct pwm_chip *chip, struct pwm_device *pwm)
322{
323 struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
324
325 mutex_lock(&tpm->lock);
326 tpm->user_count--;
327 mutex_unlock(&tpm->lock);
328}
329
330static const struct pwm_ops imx_tpm_pwm_ops = {
331 .request = pwm_imx_tpm_request,
332 .free = pwm_imx_tpm_free,
333 .get_state = pwm_imx_tpm_get_state,
334 .apply = pwm_imx_tpm_apply,
335 .owner = THIS_MODULE,
336};
337
338static int pwm_imx_tpm_probe(struct platform_device *pdev)
339{
340 struct imx_tpm_pwm_chip *tpm;
341 int ret;
342 u32 val;
343
344 tpm = devm_kzalloc(&pdev->dev, sizeof(*tpm), GFP_KERNEL);
345 if (!tpm)
346 return -ENOMEM;
347
348 platform_set_drvdata(pdev, tpm);
349
350 tpm->base = devm_platform_ioremap_resource(pdev, 0);
351 if (IS_ERR(tpm->base))
352 return PTR_ERR(tpm->base);
353
354 tpm->clk = devm_clk_get(&pdev->dev, NULL);
355 if (IS_ERR(tpm->clk)) {
356 ret = PTR_ERR(tpm->clk);
357 if (ret != -EPROBE_DEFER)
358 dev_err(&pdev->dev,
359 "failed to get PWM clock: %d\n", ret);
360 return ret;
361 }
362
363 ret = clk_prepare_enable(tpm->clk);
364 if (ret) {
365 dev_err(&pdev->dev,
366 "failed to prepare or enable clock: %d\n", ret);
367 return ret;
368 }
369
370 tpm->chip.dev = &pdev->dev;
371 tpm->chip.ops = &imx_tpm_pwm_ops;
372 tpm->chip.base = -1;
373 tpm->chip.of_xlate = of_pwm_xlate_with_flags;
374 tpm->chip.of_pwm_n_cells = 3;
375
376 /* get number of channels */
377 val = readl(tpm->base + PWM_IMX_TPM_PARAM);
378 tpm->chip.npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val);
379
380 mutex_init(&tpm->lock);
381
382 ret = pwmchip_add(&tpm->chip);
383 if (ret) {
384 dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
385 clk_disable_unprepare(tpm->clk);
386 }
387
388 return ret;
389}
390
391static int pwm_imx_tpm_remove(struct platform_device *pdev)
392{
393 struct imx_tpm_pwm_chip *tpm = platform_get_drvdata(pdev);
394 int ret = pwmchip_remove(&tpm->chip);
395
396 clk_disable_unprepare(tpm->clk);
397
398 return ret;
399}
400
401static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev)
402{
403 struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
404
405 if (tpm->enable_count > 0)
406 return -EBUSY;
407
408 clk_disable_unprepare(tpm->clk);
409
410 return 0;
411}
412
413static int __maybe_unused pwm_imx_tpm_resume(struct device *dev)
414{
415 struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
416 int ret = 0;
417
418 ret = clk_prepare_enable(tpm->clk);
419 if (ret)
420 dev_err(dev,
421 "failed to prepare or enable clock: %d\n",
422 ret);
423
424 return ret;
425}
426
427static SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm,
428 pwm_imx_tpm_suspend, pwm_imx_tpm_resume);
429
430static const struct of_device_id imx_tpm_pwm_dt_ids[] = {
431 { .compatible = "fsl,imx7ulp-pwm", },
432 { /* sentinel */ }
433};
434MODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids);
435
436static struct platform_driver imx_tpm_pwm_driver = {
437 .driver = {
438 .name = "imx7ulp-tpm-pwm",
439 .of_match_table = imx_tpm_pwm_dt_ids,
440 .pm = &imx_tpm_pwm_pm,
441 },
442 .probe = pwm_imx_tpm_probe,
443 .remove = pwm_imx_tpm_remove,
444};
445module_platform_driver(imx_tpm_pwm_driver);
446
447MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
448MODULE_DESCRIPTION("i.MX TPM PWM Driver");
449MODULE_LICENSE("GPL v2");