diff options
Diffstat (limited to 'drivers/pwm/pwm-atmel-tcb.c')
| -rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 63 |
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 | ||
| 40 | struct atmel_tcb_channel { | ||
| 41 | u32 enabled; | ||
| 42 | u32 cmr; | ||
| 43 | u32 ra; | ||
| 44 | u32 rb; | ||
| 45 | u32 rc; | ||
| 46 | }; | ||
| 47 | |||
| 40 | struct atmel_tcb_pwm_chip { | 48 | struct 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 | ||
| 47 | static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) | 56 | static 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 | }; |
| 446 | MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); | 459 | MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); |
| 447 | 460 | ||
| 461 | #ifdef CONFIG_PM_SLEEP | ||
| 462 | static 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 | |||
| 480 | static 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 | |||
| 503 | static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, | ||
| 504 | atmel_tcb_pwm_resume); | ||
| 505 | |||
| 448 | static struct platform_driver atmel_tcb_pwm_driver = { | 506 | static 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, |
