aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Izard <romain.izard.pro@gmail.com>2017-10-19 12:44:10 -0400
committerThierry Reding <thierry.reding@gmail.com>2017-11-15 04:51:31 -0500
commit1b3d9a93ed8313e42f108f871133346694de5ac3 (patch)
treefb4f91f498af07611a64f98d9f602e6062d6f352
parentccb4e74aebb6fbd56fc6783f4d5c6ded48bc2f5d (diff)
pwm: atmel-tcb: Support backup mode
Save and restore registers for the PWM on suspend and resume, which makes hibernation and backup modes possible. Signed-off-by: Romain Izard <romain.izard.pro@gmail.com> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 75db585a2a94..acd3ce8ecf3f 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {
37 unsigned period; /* PWM period expressed in clk cycles */ 37 unsigned period; /* PWM period expressed in clk cycles */
38}; 38};
39 39
40struct atmel_tcb_channel {
41 u32 enabled;
42 u32 cmr;
43 u32 ra;
44 u32 rb;
45 u32 rc;
46};
47
40struct atmel_tcb_pwm_chip { 48struct atmel_tcb_pwm_chip {
41 struct pwm_chip chip; 49 struct pwm_chip chip;
42 spinlock_t lock; 50 spinlock_t lock;
43 struct atmel_tc *tc; 51 struct atmel_tc *tc;
44 struct atmel_tcb_pwm_device *pwms[NPWM]; 52 struct atmel_tcb_pwm_device *pwms[NPWM];
53 struct atmel_tcb_channel bkup[NPWM / 2];
45}; 54};
46 55
47static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) 56static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
@@ -175,12 +184,15 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
175 * Use software trigger to apply the new setting. 184 * Use software trigger to apply the new setting.
176 * If both PWM devices in this group are disabled we stop the clock. 185 * If both PWM devices in this group are disabled we stop the clock.
177 */ 186 */
178 if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) 187 if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
179 __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, 188 __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS,
180 regs + ATMEL_TC_REG(group, CCR)); 189 regs + ATMEL_TC_REG(group, CCR));
181 else 190 tcbpwmc->bkup[group].enabled = 1;
191 } else {
182 __raw_writel(ATMEL_TC_SWTRG, regs + 192 __raw_writel(ATMEL_TC_SWTRG, regs +
183 ATMEL_TC_REG(group, CCR)); 193 ATMEL_TC_REG(group, CCR));
194 tcbpwmc->bkup[group].enabled = 0;
195 }
184 196
185 spin_unlock(&tcbpwmc->lock); 197 spin_unlock(&tcbpwmc->lock);
186} 198}
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
263 /* Use software trigger to apply the new setting */ 275 /* Use software trigger to apply the new setting */
264 __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, 276 __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
265 regs + ATMEL_TC_REG(group, CCR)); 277 regs + ATMEL_TC_REG(group, CCR));
278 tcbpwmc->bkup[group].enabled = 1;
266 spin_unlock(&tcbpwmc->lock); 279 spin_unlock(&tcbpwmc->lock);
267 return 0; 280 return 0;
268} 281}
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = {
445}; 458};
446MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); 459MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
447 460
461#ifdef CONFIG_PM_SLEEP
462static int atmel_tcb_pwm_suspend(struct device *dev)
463{
464 struct platform_device *pdev = to_platform_device(dev);
465 struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
466 void __iomem *base = tcbpwm->tc->regs;
467 int i;
468
469 for (i = 0; i < (NPWM / 2); i++) {
470 struct atmel_tcb_channel *chan = &tcbpwm->bkup[i];
471
472 chan->cmr = readl(base + ATMEL_TC_REG(i, CMR));
473 chan->ra = readl(base + ATMEL_TC_REG(i, RA));
474 chan->rb = readl(base + ATMEL_TC_REG(i, RB));
475 chan->rc = readl(base + ATMEL_TC_REG(i, RC));
476 }
477 return 0;
478}
479
480static int atmel_tcb_pwm_resume(struct device *dev)
481{
482 struct platform_device *pdev = to_platform_device(dev);
483 struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
484 void __iomem *base = tcbpwm->tc->regs;
485 int i;
486
487 for (i = 0; i < (NPWM / 2); i++) {
488 struct atmel_tcb_channel *chan = &tcbpwm->bkup[i];
489
490 writel(chan->cmr, base + ATMEL_TC_REG(i, CMR));
491 writel(chan->ra, base + ATMEL_TC_REG(i, RA));
492 writel(chan->rb, base + ATMEL_TC_REG(i, RB));
493 writel(chan->rc, base + ATMEL_TC_REG(i, RC));
494 if (chan->enabled) {
495 writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
496 base + ATMEL_TC_REG(i, CCR));
497 }
498 }
499 return 0;
500}
501#endif
502
503static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
504 atmel_tcb_pwm_resume);
505
448static struct platform_driver atmel_tcb_pwm_driver = { 506static struct platform_driver atmel_tcb_pwm_driver = {
449 .driver = { 507 .driver = {
450 .name = "atmel-tcb-pwm", 508 .name = "atmel-tcb-pwm",
451 .of_match_table = atmel_tcb_pwm_dt_ids, 509 .of_match_table = atmel_tcb_pwm_dt_ids,
510 .pm = &atmel_tcb_pwm_pm_ops,
452 }, 511 },
453 .probe = atmel_tcb_pwm_probe, 512 .probe = atmel_tcb_pwm_probe,
454 .remove = atmel_tcb_pwm_remove, 513 .remove = atmel_tcb_pwm_remove,