rtc: stmp3xxx: use optional crystal in low power states
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Thu, 16 Apr 2015 19:45:04 +0000 (12:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Apr 2015 13:03:59 +0000 (09:03 -0400)
The rtc's status register allows to determine if a 32k crystal is
connected to keep the rtc running in low power states provided the
corresponding fuse bits were blown correctly during production.  (In case
they were not, the right frequency can be stated in the device tree.) If
there is no such crystal available force the 24 MHz XTAL clock to keep
running to retain the right date and time.  Otherwise use the crystal to
save some power.

It would be nice to only switch to the crystal when the XTAL clock is
about to be disabled and keep the crystal off when unneeded because XTAL
is always on while the chip is powered on.  But as sudden power loss isn't
detectable this is not save.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/devicetree/bindings/rtc/stmp3xxx-rtc.txt
drivers/rtc/rtc-stmp3xxx.c

index b800070..fa6a942 100644 (file)
@@ -7,6 +7,11 @@ Required properties:
   region.
 - interrupts: rtc alarm interrupt
 
+Optional properties:
+- stmp,crystal-freq: override crystal frequency as determined from fuse bits.
+  Only <32000> and <32768> are possible for the hardware.  Use <0> for
+  "no crystal".
+
 Example:
 
 rtc@80056000 {
index 2939cdc..eb09edd 100644 (file)
@@ -42,6 +42,8 @@
 #define STMP3XXX_RTC_STAT                      0x10
 #define STMP3XXX_RTC_STAT_STALE_SHIFT          16
 #define STMP3XXX_RTC_STAT_RTC_PRESENT          0x80000000
+#define STMP3XXX_RTC_STAT_XTAL32000_PRESENT    0x10000000
+#define STMP3XXX_RTC_STAT_XTAL32768_PRESENT    0x08000000
 
 #define STMP3XXX_RTC_SECONDS                   0x30
 
 #define STMP3XXX_RTC_PERSISTENT0               0x60
 #define STMP3XXX_RTC_PERSISTENT0_SET           0x64
 #define STMP3XXX_RTC_PERSISTENT0_CLR           0x68
-#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002
-#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN      0x00000004
-#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE    0x00000080
+#define STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE           (1 << 0)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN         (1 << 1)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN              (1 << 2)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP       (1 << 4)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP       (1 << 5)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ           (1 << 6)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE            (1 << 7)
 
 #define STMP3XXX_RTC_PERSISTENT1               0x70
 /* missing bitmask in headers */
@@ -248,6 +254,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
 {
        struct stmp3xxx_rtc_data *rtc_data;
        struct resource *r;
+       u32 rtc_stat;
+       u32 pers0_set, pers0_clr;
+       u32 crystalfreq = 0;
        int err;
 
        rtc_data = devm_kzalloc(&pdev->dev, sizeof(*rtc_data), GFP_KERNEL);
@@ -268,8 +277,8 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
 
        rtc_data->irq_alarm = platform_get_irq(pdev, 0);
 
-       if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) &
-                       STMP3XXX_RTC_STAT_RTC_PRESENT)) {
+       rtc_stat = readl(rtc_data->io + STMP3XXX_RTC_STAT);
+       if (!(rtc_stat & STMP3XXX_RTC_STAT_RTC_PRESENT)) {
                dev_err(&pdev->dev, "no device onboard\n");
                return -ENODEV;
        }
@@ -282,9 +291,54 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
                return err;
        }
 
+       /*
+        * Obviously the rtc needs a clock input to be able to run.
+        * This clock can be provided by an external 32k crystal. If that one is
+        * missing XTAL must not be disabled in suspend which consumes a
+        * lot of power. Normally the presence and exact frequency (supported
+        * are 32000 Hz and 32768 Hz) is detectable from fuses, but as reality
+        * proves these fuses are not blown correctly on all machines, so the
+        * frequency can be overridden in the device tree.
+        */
+       if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32000_PRESENT)
+               crystalfreq = 32000;
+       else if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32768_PRESENT)
+               crystalfreq = 32768;
+
+       of_property_read_u32(pdev->dev.of_node, "stmp,crystal-freq",
+                            &crystalfreq);
+
+       switch (crystalfreq) {
+       case 32000:
+               /* keep 32kHz crystal running in low-power mode */
+               pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ |
+                       STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+                       STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+               pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP;
+               break;
+       case 32768:
+               /* keep 32.768kHz crystal running in low-power mode */
+               pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+                       STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+               pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP |
+                       STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ;
+               break;
+       default:
+               dev_warn(&pdev->dev,
+                        "invalid crystal-freq specified in device-tree. Assuming no crystal\n");
+               /* fall-through */
+       case 0:
+               /* keep XTAL on in low-power mode */
+               pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP;
+               pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+                       STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+       }
+
+       writel(pers0_set, rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET);
+
        writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
                        STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
-                       STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
+                       STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE | pers0_clr,
                        rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
 
        writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN |