aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-imx27.c
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2019-01-07 14:53:52 -0500
committerThierry Reding <thierry.reding@gmail.com>2019-01-16 02:45:33 -0500
commitd80f8206905c1a8c3857d90f12bbfd6293b48a4b (patch)
treea41575aea576d5f2c02c1b979d0ebf2e29ccb872 /drivers/pwm/pwm-imx27.c
parentb9a5c60bc2f65561535dc05d0c740aa6e9e3bdf2 (diff)
pwm: imx: Split into two drivers
The two PWM implementations called v1 (for i.MX1 and i.MX21) and v2 (for i.MX27 and later) have nothing in common apart from needing two clocks named "per" and "ipg" and being integrated in a SoC named i.MX. So split the file containing the two disjunct drivers into two files and two complete separate drivers. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> [thierry.reding@gmail.com: fix a modular build issue] Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-imx27.c')
-rw-r--r--drivers/pwm/pwm-imx27.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
new file mode 100644
index 000000000000..8b8b1c6b7f29
--- /dev/null
+++ b/drivers/pwm/pwm-imx27.c
@@ -0,0 +1,361 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * simple driver for PWM (Pulse Width Modulator) controller
4 *
5 * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
6 */
7
8#include <linux/bitfield.h>
9#include <linux/bitops.h>
10#include <linux/clk.h>
11#include <linux/delay.h>
12#include <linux/err.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_device.h>
18#include <linux/platform_device.h>
19#include <linux/pwm.h>
20#include <linux/slab.h>
21
22#define MX3_PWMCR 0x00 /* PWM Control Register */
23#define MX3_PWMSR 0x04 /* PWM Status Register */
24#define MX3_PWMSAR 0x0C /* PWM Sample Register */
25#define MX3_PWMPR 0x10 /* PWM Period Register */
26
27#define MX3_PWMCR_FWM GENMASK(27, 26)
28#define MX3_PWMCR_STOPEN BIT(25)
29#define MX3_PWMCR_DOZEN BIT(24)
30#define MX3_PWMCR_WAITEN BIT(23)
31#define MX3_PWMCR_DBGEN BIT(22)
32#define MX3_PWMCR_BCTR BIT(21)
33#define MX3_PWMCR_HCTR BIT(20)
34
35#define MX3_PWMCR_POUTC GENMASK(19, 18)
36#define MX3_PWMCR_POUTC_NORMAL 0
37#define MX3_PWMCR_POUTC_INVERTED 1
38#define MX3_PWMCR_POUTC_OFF 2
39
40#define MX3_PWMCR_CLKSRC GENMASK(17, 16)
41#define MX3_PWMCR_CLKSRC_OFF 0
42#define MX3_PWMCR_CLKSRC_IPG 1
43#define MX3_PWMCR_CLKSRC_IPG_HIGH 2
44#define MX3_PWMCR_CLKSRC_IPG_32K 3
45
46#define MX3_PWMCR_PRESCALER GENMASK(15, 4)
47
48#define MX3_PWMCR_SWR BIT(3)
49
50#define MX3_PWMCR_REPEAT GENMASK(2, 1)
51#define MX3_PWMCR_REPEAT_1X 0
52#define MX3_PWMCR_REPEAT_2X 1
53#define MX3_PWMCR_REPEAT_4X 2
54#define MX3_PWMCR_REPEAT_8X 3
55
56#define MX3_PWMCR_EN BIT(0)
57
58#define MX3_PWMSR_FWE BIT(6)
59#define MX3_PWMSR_CMP BIT(5)
60#define MX3_PWMSR_ROV BIT(4)
61#define MX3_PWMSR_FE BIT(3)
62
63#define MX3_PWMSR_FIFOAV GENMASK(2, 0)
64#define MX3_PWMSR_FIFOAV_EMPTY 0
65#define MX3_PWMSR_FIFOAV_1WORD 1
66#define MX3_PWMSR_FIFOAV_2WORDS 2
67#define MX3_PWMSR_FIFOAV_3WORDS 3
68#define MX3_PWMSR_FIFOAV_4WORDS 4
69
70#define MX3_PWMCR_PRESCALER_SET(x) FIELD_PREP(MX3_PWMCR_PRESCALER, (x) - 1)
71#define MX3_PWMCR_PRESCALER_GET(x) (FIELD_GET(MX3_PWMCR_PRESCALER, \
72 (x)) + 1)
73
74#define MX3_PWM_SWR_LOOP 5
75
76/* PWMPR register value of 0xffff has the same effect as 0xfffe */
77#define MX3_PWMPR_MAX 0xfffe
78
79struct pwm_imx27_chip {
80 struct clk *clk_ipg;
81 struct clk *clk_per;
82 void __iomem *mmio_base;
83 struct pwm_chip chip;
84};
85
86#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
87
88static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
89{
90 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
91 int ret;
92
93 ret = clk_prepare_enable(imx->clk_ipg);
94 if (ret)
95 return ret;
96
97 ret = clk_prepare_enable(imx->clk_per);
98 if (ret) {
99 clk_disable_unprepare(imx->clk_ipg);
100 return ret;
101 }
102
103 return 0;
104}
105
106static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip)
107{
108 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
109
110 clk_disable_unprepare(imx->clk_per);
111 clk_disable_unprepare(imx->clk_ipg);
112}
113
114static void pwm_imx27_get_state(struct pwm_chip *chip,
115 struct pwm_device *pwm, struct pwm_state *state)
116{
117 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
118 u32 period, prescaler, pwm_clk, ret, val;
119 u64 tmp;
120
121 ret = pwm_imx27_clk_prepare_enable(chip);
122 if (ret < 0)
123 return;
124
125 val = readl(imx->mmio_base + MX3_PWMCR);
126
127 if (val & MX3_PWMCR_EN) {
128 state->enabled = true;
129 ret = pwm_imx27_clk_prepare_enable(chip);
130 if (ret)
131 return;
132 } else {
133 state->enabled = false;
134 }
135
136 switch (FIELD_GET(MX3_PWMCR_POUTC, val)) {
137 case MX3_PWMCR_POUTC_NORMAL:
138 state->polarity = PWM_POLARITY_NORMAL;
139 break;
140 case MX3_PWMCR_POUTC_INVERTED:
141 state->polarity = PWM_POLARITY_INVERSED;
142 break;
143 default:
144 dev_warn(chip->dev, "can't set polarity, output disconnected");
145 }
146
147 prescaler = MX3_PWMCR_PRESCALER_GET(val);
148 pwm_clk = clk_get_rate(imx->clk_per);
149 pwm_clk = DIV_ROUND_CLOSEST_ULL(pwm_clk, prescaler);
150 val = readl(imx->mmio_base + MX3_PWMPR);
151 period = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
152
153 /* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
154 tmp = NSEC_PER_SEC * (u64)(period + 2);
155 state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
156
157 /* PWMSAR can be read only if PWM is enabled */
158 if (state->enabled) {
159 val = readl(imx->mmio_base + MX3_PWMSAR);
160 tmp = NSEC_PER_SEC * (u64)(val);
161 state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
162 } else {
163 state->duty_cycle = 0;
164 }
165
166 pwm_imx27_clk_disable_unprepare(chip);
167}
168
169static void pwm_imx27_sw_reset(struct pwm_chip *chip)
170{
171 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
172 struct device *dev = chip->dev;
173 int wait_count = 0;
174 u32 cr;
175
176 writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR);
177 do {
178 usleep_range(200, 1000);
179 cr = readl(imx->mmio_base + MX3_PWMCR);
180 } while ((cr & MX3_PWMCR_SWR) &&
181 (wait_count++ < MX3_PWM_SWR_LOOP));
182
183 if (cr & MX3_PWMCR_SWR)
184 dev_warn(dev, "software reset timeout\n");
185}
186
187static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
188 struct pwm_device *pwm)
189{
190 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
191 struct device *dev = chip->dev;
192 unsigned int period_ms;
193 int fifoav;
194 u32 sr;
195
196 sr = readl(imx->mmio_base + MX3_PWMSR);
197 fifoav = FIELD_GET(MX3_PWMSR_FIFOAV, sr);
198 if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) {
199 period_ms = DIV_ROUND_UP(pwm_get_period(pwm),
200 NSEC_PER_MSEC);
201 msleep(period_ms);
202
203 sr = readl(imx->mmio_base + MX3_PWMSR);
204 if (fifoav == FIELD_GET(MX3_PWMSR_FIFOAV, sr))
205 dev_warn(dev, "there is no free FIFO slot\n");
206 }
207}
208
209static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
210 struct pwm_state *state)
211{
212 unsigned long period_cycles, duty_cycles, prescale;
213 struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
214 struct pwm_state cstate;
215 unsigned long long c;
216 int ret;
217 u32 cr;
218
219 pwm_get_state(pwm, &cstate);
220
221 if (state->enabled) {
222 c = clk_get_rate(imx->clk_per);
223 c *= state->period;
224
225 do_div(c, 1000000000);
226 period_cycles = c;
227
228 prescale = period_cycles / 0x10000 + 1;
229
230 period_cycles /= prescale;
231 c = (unsigned long long)period_cycles * state->duty_cycle;
232 do_div(c, state->period);
233 duty_cycles = c;
234
235 /*
236 * according to imx pwm RM, the real period value should be
237 * PERIOD value in PWMPR plus 2.
238 */
239 if (period_cycles > 2)
240 period_cycles -= 2;
241 else
242 period_cycles = 0;
243
244 /*
245 * Wait for a free FIFO slot if the PWM is already enabled, and
246 * flush the FIFO if the PWM was disabled and is about to be
247 * enabled.
248 */
249 if (cstate.enabled) {
250 pwm_imx27_wait_fifo_slot(chip, pwm);
251 } else {
252 ret = pwm_imx27_clk_prepare_enable(chip);
253 if (ret)
254 return ret;
255
256 pwm_imx27_sw_reset(chip);
257 }
258
259 writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
260 writel(period_cycles, imx->mmio_base + MX3_PWMPR);
261
262 cr = MX3_PWMCR_PRESCALER_SET(prescale) |
263 MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
264 FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |
265 MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
266
267 if (state->polarity == PWM_POLARITY_INVERSED)
268 cr |= FIELD_PREP(MX3_PWMCR_POUTC,
269 MX3_PWMCR_POUTC_INVERTED);
270
271 writel(cr, imx->mmio_base + MX3_PWMCR);
272 } else if (cstate.enabled) {
273 writel(0, imx->mmio_base + MX3_PWMCR);
274
275 pwm_imx27_clk_disable_unprepare(chip);
276 }
277
278 return 0;
279}
280
281static const struct pwm_ops pwm_imx27_ops = {
282 .apply = pwm_imx27_apply,
283 .get_state = pwm_imx27_get_state,
284 .owner = THIS_MODULE,
285};
286
287static const struct of_device_id pwm_imx27_dt_ids[] = {
288 { .compatible = "fsl,imx27-pwm", },
289 { /* sentinel */ }
290};
291MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
292
293static int pwm_imx27_probe(struct platform_device *pdev)
294{
295 struct pwm_imx27_chip *imx;
296 struct resource *r;
297
298 imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
299 if (imx == NULL)
300 return -ENOMEM;
301
302 platform_set_drvdata(pdev, imx);
303
304 imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
305 if (IS_ERR(imx->clk_ipg)) {
306 dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
307 PTR_ERR(imx->clk_ipg));
308 return PTR_ERR(imx->clk_ipg);
309 }
310
311 imx->clk_per = devm_clk_get(&pdev->dev, "per");
312 if (IS_ERR(imx->clk_per)) {
313 int ret = PTR_ERR(imx->clk_per);
314
315 if (ret != -EPROBE_DEFER)
316 dev_err(&pdev->dev,
317 "failed to get peripheral clock: %d\n",
318 ret);
319
320 return ret;
321 }
322
323 imx->chip.ops = &pwm_imx27_ops;
324 imx->chip.dev = &pdev->dev;
325 imx->chip.base = -1;
326 imx->chip.npwm = 1;
327
328 imx->chip.of_xlate = of_pwm_xlate_with_flags;
329 imx->chip.of_pwm_n_cells = 3;
330
331 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
332 imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
333 if (IS_ERR(imx->mmio_base))
334 return PTR_ERR(imx->mmio_base);
335
336 return pwmchip_add(&imx->chip);
337}
338
339static int pwm_imx27_remove(struct platform_device *pdev)
340{
341 struct pwm_imx27_chip *imx;
342
343 imx = platform_get_drvdata(pdev);
344
345 pwm_imx27_clk_disable_unprepare(&imx->chip);
346
347 return pwmchip_remove(&imx->chip);
348}
349
350static struct platform_driver imx_pwm_driver = {
351 .driver = {
352 .name = "pwm-imx27",
353 .of_match_table = pwm_imx27_dt_ids,
354 },
355 .probe = pwm_imx27_probe,
356 .remove = pwm_imx27_remove,
357};
358module_platform_driver(imx_pwm_driver);
359
360MODULE_LICENSE("GPL v2");
361MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");