.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for Atmel Pulse Width Modulation Controller |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2013 Atmel Corporation |
---|
5 | 6 | * Bo Shen <voice.shen@atmel.com> |
---|
6 | 7 | * |
---|
7 | | - * Licensed under GPLv2. |
---|
| 8 | + * Links to reference manuals for the supported PWM chips can be found in |
---|
| 9 | + * Documentation/arm/microchip.rst. |
---|
| 10 | + * |
---|
| 11 | + * Limitations: |
---|
| 12 | + * - Periods start with the inactive level. |
---|
| 13 | + * - Hardware has to be stopped in general to update settings. |
---|
| 14 | + * |
---|
| 15 | + * Software bugs/possible improvements: |
---|
| 16 | + * - When atmel_pwm_apply() is called with state->enabled=false a change in |
---|
| 17 | + * state->polarity isn't honored. |
---|
| 18 | + * - Instead of sleeping to wait for a completed period, the interrupt |
---|
| 19 | + * functionality could be used. |
---|
8 | 20 | */ |
---|
9 | 21 | |
---|
10 | 22 | #include <linux/clk.h> |
---|
.. | .. |
---|
48 | 60 | #define PWMV2_CPRD 0x0C |
---|
49 | 61 | #define PWMV2_CPRDUPD 0x10 |
---|
50 | 62 | |
---|
51 | | -/* |
---|
52 | | - * Max value for duty and period |
---|
53 | | - * |
---|
54 | | - * Although the duty and period register is 32 bit, |
---|
55 | | - * however only the LSB 16 bits are significant. |
---|
56 | | - */ |
---|
57 | | -#define PWM_MAX_DTY 0xFFFF |
---|
58 | | -#define PWM_MAX_PRD 0xFFFF |
---|
59 | | -#define PRD_MAX_PRES 10 |
---|
| 63 | +#define PWM_MAX_PRES 10 |
---|
60 | 64 | |
---|
61 | 65 | struct atmel_pwm_registers { |
---|
62 | 66 | u8 period; |
---|
.. | .. |
---|
65 | 69 | u8 duty_upd; |
---|
66 | 70 | }; |
---|
67 | 71 | |
---|
| 72 | +struct atmel_pwm_config { |
---|
| 73 | + u32 period_bits; |
---|
| 74 | +}; |
---|
| 75 | + |
---|
| 76 | +struct atmel_pwm_data { |
---|
| 77 | + struct atmel_pwm_registers regs; |
---|
| 78 | + struct atmel_pwm_config cfg; |
---|
| 79 | +}; |
---|
| 80 | + |
---|
68 | 81 | struct atmel_pwm_chip { |
---|
69 | 82 | struct pwm_chip chip; |
---|
70 | 83 | struct clk *clk; |
---|
71 | 84 | void __iomem *base; |
---|
72 | | - const struct atmel_pwm_registers *regs; |
---|
| 85 | + const struct atmel_pwm_data *data; |
---|
73 | 86 | |
---|
74 | 87 | unsigned int updated_pwms; |
---|
75 | 88 | /* ISR is cleared when read, ensure only one thread does that */ |
---|
.. | .. |
---|
98 | 111 | { |
---|
99 | 112 | unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE; |
---|
100 | 113 | |
---|
101 | | - return readl_relaxed(chip->base + base + offset); |
---|
| 114 | + return atmel_pwm_readl(chip, base + offset); |
---|
102 | 115 | } |
---|
103 | 116 | |
---|
104 | 117 | static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip, |
---|
.. | .. |
---|
107 | 120 | { |
---|
108 | 121 | unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE; |
---|
109 | 122 | |
---|
110 | | - writel_relaxed(val, chip->base + base + offset); |
---|
| 123 | + atmel_pwm_writel(chip, base + offset, val); |
---|
111 | 124 | } |
---|
112 | 125 | |
---|
113 | 126 | static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, |
---|
.. | .. |
---|
116 | 129 | { |
---|
117 | 130 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
---|
118 | 131 | unsigned long long cycles = state->period; |
---|
| 132 | + int shift; |
---|
119 | 133 | |
---|
120 | 134 | /* Calculate the period cycles and prescale value */ |
---|
121 | 135 | cycles *= clk_get_rate(atmel_pwm->clk); |
---|
122 | 136 | do_div(cycles, NSEC_PER_SEC); |
---|
123 | 137 | |
---|
124 | | - for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1) |
---|
125 | | - (*pres)++; |
---|
| 138 | + /* |
---|
| 139 | + * The register for the period length is cfg.period_bits bits wide. |
---|
| 140 | + * So for each bit the number of clock cycles is wider divide the input |
---|
| 141 | + * clock frequency by two using pres and shift cprd accordingly. |
---|
| 142 | + */ |
---|
| 143 | + shift = fls(cycles) - atmel_pwm->data->cfg.period_bits; |
---|
126 | 144 | |
---|
127 | | - if (*pres > PRD_MAX_PRES) { |
---|
| 145 | + if (shift > PWM_MAX_PRES) { |
---|
128 | 146 | dev_err(chip->dev, "pres exceeds the maximum value\n"); |
---|
129 | 147 | return -EINVAL; |
---|
| 148 | + } else if (shift > 0) { |
---|
| 149 | + *pres = shift; |
---|
| 150 | + cycles >>= *pres; |
---|
| 151 | + } else { |
---|
| 152 | + *pres = 0; |
---|
130 | 153 | } |
---|
131 | 154 | |
---|
132 | 155 | *cprd = cycles; |
---|
.. | .. |
---|
150 | 173 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
---|
151 | 174 | u32 val; |
---|
152 | 175 | |
---|
153 | | - if (atmel_pwm->regs->duty_upd == |
---|
154 | | - atmel_pwm->regs->period_upd) { |
---|
| 176 | + if (atmel_pwm->data->regs.duty_upd == |
---|
| 177 | + atmel_pwm->data->regs.period_upd) { |
---|
155 | 178 | val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); |
---|
156 | 179 | val &= ~PWM_CMR_UPD_CDTY; |
---|
157 | 180 | atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); |
---|
158 | 181 | } |
---|
159 | 182 | |
---|
160 | 183 | atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, |
---|
161 | | - atmel_pwm->regs->duty_upd, cdty); |
---|
| 184 | + atmel_pwm->data->regs.duty_upd, cdty); |
---|
162 | 185 | } |
---|
163 | 186 | |
---|
164 | 187 | static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, |
---|
.. | .. |
---|
168 | 191 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
---|
169 | 192 | |
---|
170 | 193 | atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, |
---|
171 | | - atmel_pwm->regs->duty, cdty); |
---|
| 194 | + atmel_pwm->data->regs.duty, cdty); |
---|
172 | 195 | atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, |
---|
173 | | - atmel_pwm->regs->period, cprd); |
---|
| 196 | + atmel_pwm->data->regs.period, cprd); |
---|
174 | 197 | } |
---|
175 | 198 | |
---|
176 | 199 | static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
.. | .. |
---|
210 | 233 | } |
---|
211 | 234 | |
---|
212 | 235 | static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
213 | | - struct pwm_state *state) |
---|
| 236 | + const struct pwm_state *state) |
---|
214 | 237 | { |
---|
215 | 238 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
---|
216 | 239 | struct pwm_state cstate; |
---|
.. | .. |
---|
225 | 248 | cstate.polarity == state->polarity && |
---|
226 | 249 | cstate.period == state->period) { |
---|
227 | 250 | cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, |
---|
228 | | - atmel_pwm->regs->period); |
---|
| 251 | + atmel_pwm->data->regs.period); |
---|
229 | 252 | atmel_pwm_calculate_cdty(state, cprd, &cdty); |
---|
230 | 253 | atmel_pwm_update_cdty(chip, pwm, cdty); |
---|
231 | 254 | return 0; |
---|
.. | .. |
---|
272 | 295 | return 0; |
---|
273 | 296 | } |
---|
274 | 297 | |
---|
| 298 | +static void atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
| 299 | + struct pwm_state *state) |
---|
| 300 | +{ |
---|
| 301 | + struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
---|
| 302 | + u32 sr, cmr; |
---|
| 303 | + |
---|
| 304 | + sr = atmel_pwm_readl(atmel_pwm, PWM_SR); |
---|
| 305 | + cmr = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); |
---|
| 306 | + |
---|
| 307 | + if (sr & (1 << pwm->hwpwm)) { |
---|
| 308 | + unsigned long rate = clk_get_rate(atmel_pwm->clk); |
---|
| 309 | + u32 cdty, cprd, pres; |
---|
| 310 | + u64 tmp; |
---|
| 311 | + |
---|
| 312 | + pres = cmr & PWM_CMR_CPRE_MSK; |
---|
| 313 | + |
---|
| 314 | + cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, |
---|
| 315 | + atmel_pwm->data->regs.period); |
---|
| 316 | + tmp = (u64)cprd * NSEC_PER_SEC; |
---|
| 317 | + tmp <<= pres; |
---|
| 318 | + state->period = DIV64_U64_ROUND_UP(tmp, rate); |
---|
| 319 | + |
---|
| 320 | + cdty = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, |
---|
| 321 | + atmel_pwm->data->regs.duty); |
---|
| 322 | + tmp = (u64)(cprd - cdty) * NSEC_PER_SEC; |
---|
| 323 | + tmp <<= pres; |
---|
| 324 | + state->duty_cycle = DIV64_U64_ROUND_UP(tmp, rate); |
---|
| 325 | + |
---|
| 326 | + state->enabled = true; |
---|
| 327 | + } else { |
---|
| 328 | + state->enabled = false; |
---|
| 329 | + } |
---|
| 330 | + |
---|
| 331 | + if (cmr & PWM_CMR_CPOL) |
---|
| 332 | + state->polarity = PWM_POLARITY_INVERSED; |
---|
| 333 | + else |
---|
| 334 | + state->polarity = PWM_POLARITY_NORMAL; |
---|
| 335 | +} |
---|
| 336 | + |
---|
275 | 337 | static const struct pwm_ops atmel_pwm_ops = { |
---|
276 | 338 | .apply = atmel_pwm_apply, |
---|
| 339 | + .get_state = atmel_pwm_get_state, |
---|
277 | 340 | .owner = THIS_MODULE, |
---|
278 | 341 | }; |
---|
279 | 342 | |
---|
280 | | -static const struct atmel_pwm_registers atmel_pwm_regs_v1 = { |
---|
281 | | - .period = PWMV1_CPRD, |
---|
282 | | - .period_upd = PWMV1_CUPD, |
---|
283 | | - .duty = PWMV1_CDTY, |
---|
284 | | - .duty_upd = PWMV1_CUPD, |
---|
285 | | -}; |
---|
286 | | - |
---|
287 | | -static const struct atmel_pwm_registers atmel_pwm_regs_v2 = { |
---|
288 | | - .period = PWMV2_CPRD, |
---|
289 | | - .period_upd = PWMV2_CPRDUPD, |
---|
290 | | - .duty = PWMV2_CDTY, |
---|
291 | | - .duty_upd = PWMV2_CDTYUPD, |
---|
292 | | -}; |
---|
293 | | - |
---|
294 | | -static const struct platform_device_id atmel_pwm_devtypes[] = { |
---|
295 | | - { |
---|
296 | | - .name = "at91sam9rl-pwm", |
---|
297 | | - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1, |
---|
298 | | - }, { |
---|
299 | | - .name = "sama5d3-pwm", |
---|
300 | | - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2, |
---|
301 | | - }, { |
---|
302 | | - /* sentinel */ |
---|
| 343 | +static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { |
---|
| 344 | + .regs = { |
---|
| 345 | + .period = PWMV1_CPRD, |
---|
| 346 | + .period_upd = PWMV1_CUPD, |
---|
| 347 | + .duty = PWMV1_CDTY, |
---|
| 348 | + .duty_upd = PWMV1_CUPD, |
---|
| 349 | + }, |
---|
| 350 | + .cfg = { |
---|
| 351 | + /* 16 bits to keep period and duty. */ |
---|
| 352 | + .period_bits = 16, |
---|
303 | 353 | }, |
---|
304 | 354 | }; |
---|
305 | | -MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes); |
---|
| 355 | + |
---|
| 356 | +static const struct atmel_pwm_data atmel_sama5_pwm_data = { |
---|
| 357 | + .regs = { |
---|
| 358 | + .period = PWMV2_CPRD, |
---|
| 359 | + .period_upd = PWMV2_CPRDUPD, |
---|
| 360 | + .duty = PWMV2_CDTY, |
---|
| 361 | + .duty_upd = PWMV2_CDTYUPD, |
---|
| 362 | + }, |
---|
| 363 | + .cfg = { |
---|
| 364 | + /* 16 bits to keep period and duty. */ |
---|
| 365 | + .period_bits = 16, |
---|
| 366 | + }, |
---|
| 367 | +}; |
---|
| 368 | + |
---|
| 369 | +static const struct atmel_pwm_data mchp_sam9x60_pwm_data = { |
---|
| 370 | + .regs = { |
---|
| 371 | + .period = PWMV1_CPRD, |
---|
| 372 | + .period_upd = PWMV1_CUPD, |
---|
| 373 | + .duty = PWMV1_CDTY, |
---|
| 374 | + .duty_upd = PWMV1_CUPD, |
---|
| 375 | + }, |
---|
| 376 | + .cfg = { |
---|
| 377 | + /* 32 bits to keep period and duty. */ |
---|
| 378 | + .period_bits = 32, |
---|
| 379 | + }, |
---|
| 380 | +}; |
---|
306 | 381 | |
---|
307 | 382 | static const struct of_device_id atmel_pwm_dt_ids[] = { |
---|
308 | 383 | { |
---|
309 | 384 | .compatible = "atmel,at91sam9rl-pwm", |
---|
310 | | - .data = &atmel_pwm_regs_v1, |
---|
| 385 | + .data = &atmel_sam9rl_pwm_data, |
---|
311 | 386 | }, { |
---|
312 | 387 | .compatible = "atmel,sama5d3-pwm", |
---|
313 | | - .data = &atmel_pwm_regs_v2, |
---|
| 388 | + .data = &atmel_sama5_pwm_data, |
---|
314 | 389 | }, { |
---|
315 | 390 | .compatible = "atmel,sama5d2-pwm", |
---|
316 | | - .data = &atmel_pwm_regs_v2, |
---|
| 391 | + .data = &atmel_sama5_pwm_data, |
---|
| 392 | + }, { |
---|
| 393 | + .compatible = "microchip,sam9x60-pwm", |
---|
| 394 | + .data = &mchp_sam9x60_pwm_data, |
---|
317 | 395 | }, { |
---|
318 | 396 | /* sentinel */ |
---|
319 | 397 | }, |
---|
320 | 398 | }; |
---|
321 | 399 | MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); |
---|
322 | 400 | |
---|
323 | | -static inline const struct atmel_pwm_registers * |
---|
324 | | -atmel_pwm_get_driver_data(struct platform_device *pdev) |
---|
325 | | -{ |
---|
326 | | - const struct platform_device_id *id; |
---|
327 | | - |
---|
328 | | - if (pdev->dev.of_node) |
---|
329 | | - return of_device_get_match_data(&pdev->dev); |
---|
330 | | - |
---|
331 | | - id = platform_get_device_id(pdev); |
---|
332 | | - |
---|
333 | | - return (struct atmel_pwm_registers *)id->driver_data; |
---|
334 | | -} |
---|
335 | | - |
---|
336 | 401 | static int atmel_pwm_probe(struct platform_device *pdev) |
---|
337 | 402 | { |
---|
338 | | - const struct atmel_pwm_registers *regs; |
---|
339 | 403 | struct atmel_pwm_chip *atmel_pwm; |
---|
340 | 404 | struct resource *res; |
---|
341 | 405 | int ret; |
---|
342 | 406 | |
---|
343 | | - regs = atmel_pwm_get_driver_data(pdev); |
---|
344 | | - if (!regs) |
---|
345 | | - return -ENODEV; |
---|
346 | | - |
---|
347 | 407 | atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); |
---|
348 | 408 | if (!atmel_pwm) |
---|
349 | 409 | return -ENOMEM; |
---|
| 410 | + |
---|
| 411 | + mutex_init(&atmel_pwm->isr_lock); |
---|
| 412 | + atmel_pwm->data = of_device_get_match_data(&pdev->dev); |
---|
| 413 | + atmel_pwm->updated_pwms = 0; |
---|
350 | 414 | |
---|
351 | 415 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
352 | 416 | atmel_pwm->base = devm_ioremap_resource(&pdev->dev, res); |
---|
.. | .. |
---|
365 | 429 | |
---|
366 | 430 | atmel_pwm->chip.dev = &pdev->dev; |
---|
367 | 431 | atmel_pwm->chip.ops = &atmel_pwm_ops; |
---|
368 | | - |
---|
369 | | - if (pdev->dev.of_node) { |
---|
370 | | - atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags; |
---|
371 | | - atmel_pwm->chip.of_pwm_n_cells = 3; |
---|
372 | | - } |
---|
373 | | - |
---|
| 432 | + atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags; |
---|
| 433 | + atmel_pwm->chip.of_pwm_n_cells = 3; |
---|
374 | 434 | atmel_pwm->chip.base = -1; |
---|
375 | 435 | atmel_pwm->chip.npwm = 4; |
---|
376 | | - atmel_pwm->regs = regs; |
---|
377 | | - atmel_pwm->updated_pwms = 0; |
---|
378 | | - mutex_init(&atmel_pwm->isr_lock); |
---|
379 | 436 | |
---|
380 | 437 | ret = pwmchip_add(&atmel_pwm->chip); |
---|
381 | 438 | if (ret < 0) { |
---|
.. | .. |
---|
407 | 464 | .name = "atmel-pwm", |
---|
408 | 465 | .of_match_table = of_match_ptr(atmel_pwm_dt_ids), |
---|
409 | 466 | }, |
---|
410 | | - .id_table = atmel_pwm_devtypes, |
---|
411 | 467 | .probe = atmel_pwm_probe, |
---|
412 | 468 | .remove = atmel_pwm_remove, |
---|
413 | 469 | }; |
---|