pwm: atmel-tcb: Support backup mode
Subsystems (2)
PATCH v5 1/1 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 <[email protected]>
Acked-by Nicolas Ferre <[email protected]>
---
Changes in v5:
* extract from the patch series, and send as a standalone patch
drivers/pwm/pwm-atmel-tcb.c | 63 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-)
drivers/pwm/pwm-atmel-tcb.c +61 -2
@@ -37,11 +37,20 @@ struct atmel_tcb_pwm_device {unsigned period;/* PWM period expressed in clk cycles */ }; +struct atmel_tcb_channel { +u32 enabled; +u32 cmr; +u32 ra; +u32 rb; +u32 rc; +}; +struct atmel_tcb_pwm_chip {struct pwm_chip chip;spinlock_t lock;struct atmel_tc *tc;struct atmel_tcb_pwm_device *pwms[NPWM ]; +struct atmel_tcb_channel bkup[NPWM /2 ]; };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)* Use software trigger to apply the new setting. * If both PWM devices in this group are disabled we stop the clock. */ -if (!(cmr & (ATMEL_TC_ACPC |ATMEL_TC_BCPC ))) +if (!(cmr & (ATMEL_TC_ACPC |ATMEL_TC_BCPC ))) {__raw_writel (ATMEL_TC_SWTRG |ATMEL_TC_CLKDIS , regs +ATMEL_TC_REG (group,CCR )); -else + tcbpwmc->bkup[group].enabled =1 ; + }else {__raw_writel (ATMEL_TC_SWTRG , regs +ATMEL_TC_REG (group,CCR )); + tcbpwmc->bkup[group].enabled =0 ; + }spin_unlock (&tcbpwmc->lock); }
@@ -263,6 +275,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)/* Use software trigger to apply the new setting */ __raw_writel (ATMEL_TC_CLKEN |ATMEL_TC_SWTRG , regs +ATMEL_TC_REG (group,CCR )); + tcbpwmc->bkup[group].enabled =1 ;spin_unlock (&tcbpwmc->lock);return 0 ; }
@@ -445,10 +458,56 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { };MODULE_DEVICE_TABLE (of, atmel_tcb_pwm_dt_ids); +#ifdef CONFIG_PM_SLEEP +static int atmel_tcb_pwm_suspend (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct atmel_tcb_pwm_chip *tcbpwm =platform_get_drvdata (pdev); +void __iomem *base = tcbpwm->tc->regs; +int i; + +for (i =0 ; i < (NPWM /2 ); i++) { +struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + + chan->cmr =readl (base +ATMEL_TC_REG (i,CMR )); + chan->ra =readl (base +ATMEL_TC_REG (i,RA )); + chan->rb =readl (base +ATMEL_TC_REG (i,RB )); + chan->rc =readl (base +ATMEL_TC_REG (i,RC )); + } +return 0 ; +} + +static int atmel_tcb_pwm_resume (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct atmel_tcb_pwm_chip *tcbpwm =platform_get_drvdata (pdev); +void __iomem *base = tcbpwm->tc->regs; +int i; + +for (i =0 ; i < (NPWM /2 ); i++) { +struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + +writel (chan->cmr, base +ATMEL_TC_REG (i,CMR )); +writel (chan->ra, base +ATMEL_TC_REG (i,RA )); +writel (chan->rb, base +ATMEL_TC_REG (i,RB )); +writel (chan->rc, base +ATMEL_TC_REG (i,RC )); +if (chan->enabled) { +writel (ATMEL_TC_CLKEN |ATMEL_TC_SWTRG , + base +ATMEL_TC_REG (i,CCR )); + } + } +return 0 ; +} +#endif + +static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, + atmel_tcb_pwm_resume); +static struct platform_driver atmel_tcb_pwm_driver = { .driver = { .name ="atmel-tcb-pwm" , .of_match_table = atmel_tcb_pwm_dt_ids, + .pm = &atmel_tcb_pwm_pm_ops, }, .probe = atmel_tcb_pwm_probe, .remove = atmel_tcb_pwm_remove,
Signature · 1 line
2.11.0