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>
This commit is contained in:
parent
ccb4e74aeb
commit
1b3d9a93ed
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue