Implement suspend and resume power management specific
function to allow PWM controller to correctly suspend
and resume.

Signed-off-by: Claudiu Beznea <[email protected]>
---
 drivers/pwm/pwm-atmel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 530d7dc..75177c6 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -58,6 +58,8 @@
 #define PWM_MAX_PRD            0xFFFF
 #define PRD_MAX_PRES           10
 
+#define PWM_MAX_CH_NUM         (4)
+
 struct atmel_pwm_registers {
        u8 period;
        u8 period_upd;
@@ -65,11 +67,18 @@ struct atmel_pwm_registers {
        u8 duty_upd;
 };
 
+struct atmel_pwm_pm_ctx {
+       u32 cmr;
+       u32 cdty;
+       u32 cprd;
+};
+
 struct atmel_pwm_chip {
        struct pwm_chip chip;
        struct clk *clk;
        void __iomem *base;
        const struct atmel_pwm_registers *regs;
+       struct atmel_pwm_pm_ctx ctx[PWM_MAX_CH_NUM];
 
        unsigned int updated_pwms;
        /* ISR is cleared when read, ensure only one thread does that */
@@ -333,6 +342,77 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
        return (struct atmel_pwm_registers *)id->driver_data;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int atmel_pwm_suspend(struct device *dev)
+{
+       struct atmel_pwm_chip *atmel_pwm = dev_get_drvdata(dev);
+       struct pwm_device *pwm = atmel_pwm->chip.pwms;
+       int i;
+       bool disable_clk = false;
+
+       for (i = 0; i < atmel_pwm->chip.npwm; i++, pwm++) {
+               if (!pwm_is_enabled(pwm))
+                       continue;
+
+               disable_clk = true;
+               atmel_pwm->ctx[i].cdty =
+                       atmel_pwm_ch_readl(atmel_pwm, i,
+                                          atmel_pwm->regs->duty);
+               atmel_pwm->ctx[i].cprd =
+                       atmel_pwm_ch_readl(atmel_pwm, i,
+                                          atmel_pwm->regs->period);
+               atmel_pwm->ctx[i].cmr =
+                       atmel_pwm_ch_readl(atmel_pwm, i, PWM_CMR);
+
+               atmel_pwm_disable(&atmel_pwm->chip, pwm, false);
+       }
+
+       if (disable_clk)
+               clk_disable(atmel_pwm->clk);
+
+       return 0;
+}
+
+static int atmel_pwm_resume(struct device *dev)
+{
+       struct atmel_pwm_chip *atmel_pwm = dev_get_drvdata(dev);
+       struct pwm_device *pwm = atmel_pwm->chip.pwms;
+       int i, ret;
+       bool disable_clk = true;
+
+       ret = clk_enable(atmel_pwm->clk);
+       if (ret) {
+               dev_err(dev, "failed to enable clock\n");
+               return ret;
+       }
+
+       for (i = 0; i < atmel_pwm->chip.npwm; i++, pwm++) {
+               if (!pwm_is_enabled(pwm))
+                       continue;
+
+               disable_clk = false;
+               atmel_pwm_ch_writel(atmel_pwm, i, PWM_CMR,
+                                   atmel_pwm->ctx[i].cmr);
+               atmel_pwm_set_cprd_cdty(&atmel_pwm->chip, pwm,
+                                       atmel_pwm->ctx[i].cprd,
+                                       atmel_pwm->ctx[i].cdty);
+               mutex_lock(&atmel_pwm->isr_lock);
+               atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+               atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm);
+               mutex_unlock(&atmel_pwm->isr_lock);
+               atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << pwm->hwpwm);
+       }
+
+       if (disable_clk)
+               clk_disable(atmel_pwm->clk);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_pwm_pm_ops,
+                        atmel_pwm_suspend, atmel_pwm_resume);
+
 static int atmel_pwm_probe(struct platform_device *pdev)
 {
        const struct atmel_pwm_registers *regs;
@@ -406,6 +486,7 @@ static struct platform_driver atmel_pwm_driver = {
        .driver = {
                .name = "atmel-pwm",
                .of_match_table = of_match_ptr(atmel_pwm_dt_ids),
+               .pm = &atmel_pwm_pm_ops,
        },
        .id_table = atmel_pwm_devtypes,
        .probe = atmel_pwm_probe,
-- 
2.7.4

Reply via email to