Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b3d9a93 authored by Romain Izard's avatar Romain Izard Committed by Thierry Reding
Browse files

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: default avatarRomain Izard <romain.izard.pro@gmail.com>
Acked-by: default avatarNicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent ccb4e74a
Loading
Loading
Loading
Loading
+61 −2
Original line number Diff line number Diff line
@@ -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,