OMAP: timekeeping: time should not stop during suspend
authorKevin Hilman <khilman@deeprootsystems.com>
Thu, 24 Sep 2009 23:35:48 +0000 (16:35 -0700)
committerKevin Hilman <khilman@deeprootsystems.com>
Thu, 21 Jan 2010 02:16:08 +0000 (18:16 -0800)
During suspend, the kernel timekeeping subsystem is shut down.  Before
suspend and upon resume, it uses a weak function
read_persistent_clock() to determine the amount of time that elapsed
during suspend.

This function was not implemented on OMAP, so from the timekeeping
subsystem perspective (and thus userspace as well) it appeared that no
time elapsed during suspend.

This patch uses the 32k sync timer as a the persistent clock.

NOTE: This does *NOT* fully handle wrapping of the 32k sync timer, so
      more than one wrapping of the 32k sync timer during suspend may
      cause problems.  Also note there are not interrupts when the 32k
      sync timer wraps, so something else has to be done.

Reported-by: Jon Hunter <jon-hunter@ti.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
arch/arm/plat-omap/common.c

index bf1eaf3a27d452e915a83fb381ccc54490a4a88b..dddc0273bc8bc4157248c6677925bc3de3cc4446 100644 (file)
@@ -172,6 +172,32 @@ unsigned long long sched_clock(void)
                                  clocksource_32k.mult, clocksource_32k.shift);
 }
 
+/**
+ * read_persistent_clock -  Return time from a persistent clock.
+ *
+ * Reads the time from a source which isn't disabled during PM, the
+ * 32k sync timer.  Convert the cycles elapsed since last read into
+ * nsecs and adds to a monotonically increasing timespec.
+ */
+static struct timespec persistent_ts;
+static cycles_t cycles, last_cycles;
+void read_persistent_clock(struct timespec *ts)
+{
+       unsigned long long nsecs;
+       cycles_t delta;
+       struct timespec *tsp = &persistent_ts;
+
+       last_cycles = cycles;
+       cycles = clocksource_32k.read(&clocksource_32k);
+       delta = cycles - last_cycles;
+
+       nsecs = clocksource_cyc2ns(delta,
+                                  clocksource_32k.mult, clocksource_32k.shift);
+
+       timespec_add_ns(tsp, nsecs);
+       *ts = *tsp;
+}
+
 static int __init omap_init_clocksource_32k(void)
 {
        static char err[] __initdata = KERN_ERR