rtc: xgene: Fix suspend/resume

This patch fixes suspend/resume functions properly for the APM X-Gene
SoC RTC driver.

Signed-off-by: Loc Ho <lho@apm.com>
Reviewed-by: Mark Brown <broonie@linaro.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
Loc Ho 2014-04-14 12:09:04 -06:00 committed by Alexandre Belloni
parent 1856e0b2ac
commit d0bcd82b13
1 changed files with 32 additions and 9 deletions

View File

@ -52,6 +52,7 @@ struct xgene_rtc_dev {
void __iomem *csr_base; void __iomem *csr_base;
struct clk *clk; struct clk *clk;
unsigned int irq_wake; unsigned int irq_wake;
unsigned int irq_enabled;
}; };
static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm) static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
@ -104,15 +105,19 @@ static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
return 0; return 0;
} }
static int xgene_rtc_alarm_irq_enabled(struct device *dev)
{
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0;
}
static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{ {
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
unsigned long rtc_time;
unsigned long alarm_time; unsigned long alarm_time;
rtc_time = readl(pdata->csr_base + RTC_CCVR);
rtc_tm_to_time(&alrm->time, &alarm_time); rtc_tm_to_time(&alrm->time, &alarm_time);
pdata->alarm_time = alarm_time; pdata->alarm_time = alarm_time;
writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR); writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR);
@ -180,12 +185,18 @@ static int xgene_rtc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Couldn't get the clock for RTC\n"); dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
return -ENODEV; return -ENODEV;
} }
clk_prepare_enable(pdata->clk); ret = clk_prepare_enable(pdata->clk);
if (ret)
return ret;
/* Turn on the clock and the crystal */ /* Turn on the clock and the crystal */
writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR); writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR);
device_init_wakeup(&pdev->dev, 1); ret = device_init_wakeup(&pdev->dev, 1);
if (ret) {
clk_disable_unprepare(pdata->clk);
return ret;
}
pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&xgene_rtc_ops, THIS_MODULE); &xgene_rtc_ops, THIS_MODULE);
@ -218,14 +229,20 @@ static int xgene_rtc_suspend(struct device *dev)
int irq; int irq;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
/*
* If this RTC alarm will be used for waking the system up,
* don't disable it of course. Else we just disable the alarm
* and await suspension.
*/
if (device_may_wakeup(&pdev->dev)) { if (device_may_wakeup(&pdev->dev)) {
if (!enable_irq_wake(irq)) if (!enable_irq_wake(irq))
pdata->irq_wake = 1; pdata->irq_wake = 1;
} else { } else {
pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev);
xgene_rtc_alarm_irq_enable(dev, 0); xgene_rtc_alarm_irq_enable(dev, 0);
clk_disable(pdata->clk); clk_disable_unprepare(pdata->clk);
} }
return 0; return 0;
} }
@ -234,16 +251,22 @@ static int xgene_rtc_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
int irq; int irq;
int rc;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(&pdev->dev)) { if (device_may_wakeup(&pdev->dev)) {
if (pdata->irq_wake) { if (pdata->irq_wake) {
disable_irq_wake(irq); disable_irq_wake(irq);
pdata->irq_wake = 0; pdata->irq_wake = 0;
} }
} else { } else {
clk_enable(pdata->clk); rc = clk_prepare_enable(pdata->clk);
xgene_rtc_alarm_irq_enable(dev, 1); if (rc) {
dev_err(dev, "Unable to enable clock error %d\n", rc);
return rc;
}
xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled);
} }
return 0; return 0;