Commit 7f48b21b authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Linus Torvalds

rtc: stmp3xxx: use optional crystal in low power states

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: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b0c57b59
...@@ -7,6 +7,11 @@ Required properties: ...@@ -7,6 +7,11 @@ Required properties:
region. region.
- interrupts: rtc alarm interrupt - 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: Example:
rtc@80056000 { rtc@80056000 {
......
...@@ -42,6 +42,8 @@ ...@@ -42,6 +42,8 @@
#define STMP3XXX_RTC_STAT 0x10 #define STMP3XXX_RTC_STAT 0x10
#define STMP3XXX_RTC_STAT_STALE_SHIFT 16 #define STMP3XXX_RTC_STAT_STALE_SHIFT 16
#define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000 #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_SECONDS 0x30
...@@ -52,9 +54,13 @@ ...@@ -52,9 +54,13 @@
#define STMP3XXX_RTC_PERSISTENT0 0x60 #define STMP3XXX_RTC_PERSISTENT0 0x60
#define STMP3XXX_RTC_PERSISTENT0_SET 0x64 #define STMP3XXX_RTC_PERSISTENT0_SET 0x64
#define STMP3XXX_RTC_PERSISTENT0_CLR 0x68 #define STMP3XXX_RTC_PERSISTENT0_CLR 0x68
#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002 #define STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE (1 << 0)
#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1)
#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 #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 #define STMP3XXX_RTC_PERSISTENT1 0x70
/* missing bitmask in headers */ /* missing bitmask in headers */
...@@ -248,6 +254,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) ...@@ -248,6 +254,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
{ {
struct stmp3xxx_rtc_data *rtc_data; struct stmp3xxx_rtc_data *rtc_data;
struct resource *r; struct resource *r;
u32 rtc_stat;
u32 pers0_set, pers0_clr;
u32 crystalfreq = 0;
int err; int err;
rtc_data = devm_kzalloc(&pdev->dev, sizeof(*rtc_data), GFP_KERNEL); rtc_data = devm_kzalloc(&pdev->dev, sizeof(*rtc_data), GFP_KERNEL);
...@@ -268,8 +277,8 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) ...@@ -268,8 +277,8 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
rtc_data->irq_alarm = platform_get_irq(pdev, 0); rtc_data->irq_alarm = platform_get_irq(pdev, 0);
if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) & rtc_stat = readl(rtc_data->io + STMP3XXX_RTC_STAT);
STMP3XXX_RTC_STAT_RTC_PRESENT)) { if (!(rtc_stat & STMP3XXX_RTC_STAT_RTC_PRESENT)) {
dev_err(&pdev->dev, "no device onboard\n"); dev_err(&pdev->dev, "no device onboard\n");
return -ENODEV; return -ENODEV;
} }
...@@ -282,9 +291,54 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) ...@@ -282,9 +291,54 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
return err; 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 | writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_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); rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN |
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment