[PATCH] drivers: pwm: pwm-atmel: implement suspend/resume functions

From: Claudiu Beznea
Date: Mon Apr 10 2017 - 10:20:42 EST


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

Signed-off-by: Claudiu Beznea <claudiu.beznea@xxxxxxxxxxxxx>
---
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