RTC: LPC32xx: Improve RTC invalid state detection
authorKevin Wells <wellsk40@gmail.com>
Mon, 10 Jan 2011 22:30:44 +0000 (14:30 -0800)
committerKevin Wells <wellsk40@gmail.com>
Mon, 10 Jan 2011 22:30:44 +0000 (14:30 -0800)
When a power cut of few seconds occurs (time depending on the board power
stage), RTC SRAM can be corrupted. When the RTC voltage starts to deteriorate,
bits in the RTC register change state. But if the key register stays valid and
other bits in the RTC like the up/down time change and the system is repowered
on with the key register still valid, the software will think the RTC is ok.

In these cases, you can see some weird dates like 2035, 1982, ...

This patch uses the down counter value to validate the up counter one (it must
be equal to u32 inverted up counter value). If values don't match, it reset
both counters.

Signed-off by: Martin Chaplet <m.chaplet@kernlink.fr>

drivers/rtc/rtc-lpc32xx.c

index ec8701c..df3c773 100644 (file)
@@ -202,7 +202,7 @@ static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
        struct lpc32xx_rtc *rtc;
        resource_size_t size;
        int rtcirq;
-       u32 tmp;
+       u32 tmp, ucount, dcount;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -272,6 +272,24 @@ static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, rtc);
 
+       /*
+       * Check SRAM integrity by comparing UP and DOWN counters
+       * If they don't match, reset them.
+       */
+
+       /* Disable RTC during read (preventing count between reads) */
+       tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+       rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS);
+       ucount = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
+       dcount = rtc_readl(rtc, LPC32XX_RTC_DCOUNT);
+       rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS);
+
+       if( (dcount + ucount) != 0xFFFFFFFF )
+       {
+               dev_dbg(&pdev->dev, "Dcount (%08X) and Ucount (%08X) mismatch, reset them.\n", dcount, ucount );
+               lpc32xx_rtc_set_mmss(&pdev->dev, 0);
+       }
+
        rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops,
                THIS_MODULE);
        if (IS_ERR(rtc->rtc)) {