On 15/09/2017 at 16:04, Romain Izard wrote: > 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@xxxxxxxxx> Seems good to me: Acked-by: Nicolas Ferre <nicolas.ferre@xxxxxxxxxxxxx> > --- > drivers/pwm/pwm-atmel-tcb.c | 63 +++++++++++++++++++++++++++++++++++++++++++-- > 1 file 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 { > 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, > -- Nicolas Ferre -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html