[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [Q] how to deal with 'clock vs hwclock' call

Avery Pennarun <apenwarr@worldvisions.ca> writes:
> > And I don't quite see why this call has to be there. The kernel already
> > does what I think you're trying to do:
> Unfortunately, it doesn't do it properly.  I haven't had time to look at it
> closely, but my laptop loses _large_ amounts of time when it goes into
> suspend.

Uhm, are you sure your laptop's clock isn't simply running too slowly? Run
hwclock in a loop and see if it loses time. If so you need a patch to reset
the timer interrupt after a resume. I'm pretty sure your model needs this.

> One interesting effect is due to time zones:  last time I looked, the kernel
> records the GMT offset the first time you go into suspend/standby, and uses
> it forever after.  Interesting side effects:

Oh ick, yes as i mentioned, if you set your CMOS clock to local time you're
going to lose in a number of circumstances. You'll lose around the switch to
and from daylight saving time, you'll lose if the cpu clock slows down before
the kernel measures the offset from GMT, you'll lose all over the place. 

The same patch i mentioned will solve that too. 

I'll include it here because it's short and some other people may also find it
valuable. It's probably not suitable for inclusion in the kernel because it
hardcodes the 100hz interrupt timer. But the other piece should be reasonable.
I sent it to Alan Cox but he didn't seem interested in putting it in 2.0.34.

I should mention, I didn't write this, I got it off linuxch-kernel.

> Here's a sample of apmd --help with my patch:
> 	usage: apmd [-VvW] [-p percent-change] [-w warn-percent]
> 	        [-s on-suspend] [-S on-standby]
>                 [-r on-resume-suspend] [-R on-resume-standby]

I would suggest setting the init.d scripts (or adding a configuration file in
/etc and configuring it there) to run-parts something like /etc/apmd-suspend.d
and /etc/apmd-resume.d. That way users can add whatever they want.

> I use this to run 'hdparm' to (re-)enable hard drive spindown when I come
> out of suspend.  We could also use it to run the right 'hwclock' program
> from a script.

I'm using the modified update (aka bdflush) daemon written by Pavel Machek
that attempts to spin the disk down when possible. It does a decent job,
though I can think of some improvements. It checks /proc/stat and when no
reads have been done in a specified period (it can't check writes because of
its own buffer flushing) it stops flushing buffers and calls a script in
/etc/rc.spindown to explicitly spin down the disk using hdparm.

I've been thinking of packaging this, but I was under the impression it was
going to be in the stock bdflush daemon. Apparently it hasn't happened though.


Subject: patch for apm_bios.c (kernel 2.0.30)
Date: 	Fri, 27 Jun 1997 08:48:37 -0400 (EDT)
From: Dong Chen <chen@ctpa04.mit.edu>
To: linux-kernel@vger.rutgers.edu

(I sent this several days ago, but it seems got lost.  So here it is again.)


This is a patch for "drivers/char/apm_bios.c", it fixes the following

(1) On some notebooks (AST J series, for example), the timer on interrupt 0
is reset to DOS default: 18 Hz.  This patch re-initialize it to 100 Hz.
Thanks to Pavel (pavel@Elf.mj.gts.cz) for pointing out to me that I should
add some delays after the outb_p() and outb() calls.

(2) The clock is not correctly restored after a standby().

There are still some problems with not getting the correct time after APM
suspend or standby, namely before the first suspend() or standby()
call, if the clock is already slowed by CPU_IDLE call, then the estimate
time zone "clock_cmos_diff" would be wrong.  Ideally, "clock_cmos_diff"
should be setup at boot time after the time zone is set.  But that
will require changing code other than "apm_bios.c".  Also, APM will not
correct for the change between daylight savings time and normal time.

Dong Chen

---------------------------CUT HERE-------------------------------------
--- drivers/char/apm_bios.c.orig	Mon May 26 11:05:15 1997
+++ drivers/char/apm_bios.c	Tue Jun 24 12:09:06 1997
@@ -73,6 +73,18 @@
 #include <linux/miscdevice.h>
 #include <linux/apm_bios.h>
+ * INIT_TIMER_AFTER_SUSPEND: define to re-initialize the interrupt 0 timer
+ * to 100 Hz after a suspend.
+ */
+#include <linux/timex.h>
+#include <asm/io.h>
+#include <linux/delay.h>
 static struct symbol_table	apm_syms = {
 #include <linux/symtab_begin.h>
@@ -627,28 +639,53 @@
 	unsigned long	flags;
 	int		err;
-				/* Estimate time zone so that set_time can
-                                   update the clock */
-	save_flags(flags);
-	clock_cmos_diff = -get_cmos_time();
-	cli();
-	clock_cmos_diff += CURRENT_TIME;
-	got_clock_diff = 1;
-	restore_flags(flags);
+	if (!got_clock_diff) {
+				/* Estimate time zone */
+		save_flags(flags);
+		clock_cmos_diff = -get_cmos_time();
+		cli();
+		clock_cmos_diff += CURRENT_TIME;
+		got_clock_diff = 1;
+		restore_flags(flags);
+	}
 	err = apm_set_power_state(APM_STATE_SUSPEND);
 	if (err)
 		apm_error("suspend", err);
+	cli();
+        /* set the clock to 100 Hz */
+        outb_p(0x34,0x43);              /* binary, mode 2, LSB/MSB, ch 0 */
+        udelay(10);
+        outb_p(LATCH & 0xff , 0x40);    /* LSB */
+        udelay(10);
+        outb(LATCH >> 8 , 0x40);        /* MSB */
+        udelay(10);
 static void standby(void)
+	unsigned long	flags;
 	int	err;
+	if (!got_clock_diff) {
+				/* Estimate time zone */
+		save_flags(flags);
+		clock_cmos_diff = -get_cmos_time();
+		cli();
+		clock_cmos_diff += CURRENT_TIME;
+		got_clock_diff = 1;
+		restore_flags(flags);
+	}
 	err = apm_set_power_state(APM_STATE_STANDBY);
 	if (err)
 		apm_error("standby", err);
+	set_time();
 static apm_event_t get_event(void)

To UNSUBSCRIBE, email to debian-devel-request@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org

Reply to: