Re: 500 ms offset in i386 Real Time Clock setting

Paul Gortmaker
2001-01-07T12:56:10Z

BJerrick@easystreet.com wrote:
>
> Neither hwclock nor the /dev/rtc driver takes the following comment from
> set_rtc_mmss() in arch/i386/kernel/time.c into account. As a result, using
> hwclock --systohc or --adjust always leaves the Hardware Clock 500 ms ahead of
> the System Clock:

[...]

> Shouldn't there be some kernel interface that hides this machine-dependency
> from user-level code; i.e., that sets time more precisely?

I don't have a problem with the rtc driver delaying 500ms before setting the
time since that is in fact a feature of the hardware, and it probably should
have done this from the beginning. However I would have a problem with the
rtc driver using system time (kernel xtime) to synchronize the start of that
delay - the user may want to synchronize the rtc with some external source.

Simple patch (2.4.0) to add the delay follows, along with a $0.02 test prog
I used to make sure things behave as desired. (I guess hwclock will still
require its own delay for when /dev/rtc isn't present and it goes direct.)

Paul.

--- drivers/char/rtc.c~ Sat Jan 6 05:40:24 2001
+++ drivers/char/rtc.c Sun Jan 7 07:17:39 2001
@@ -40,6 +40,7 @@
* 1.10b Andrew Morton: SMP lock fix
* 1.10c Cesar Barros: SMP locking fixes and cleanup
* 1.10d Paul Gortmaker: delete paranoia check in rtc_exit
+ * 1.10e Paul Gortmaker: wait 0.5s before writing time to rtc.
*/

#define RTC_VERSION "1.10d"
@@ -414,6 +415,13 @@
BIN_TO_BCD(mon);
BIN_TO_BCD(yrs);
}
+
+ /* The second for which the clock is set is only 0.5s long */
+ spin_unlock_irq(&rtc_lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+ set_current_state(TASK_RUNNING);
+ spin_lock_irq(&rtc_lock);

save_control = CMOS_READ(RTC_CONTROL);
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);

----------------------8<-------------------8<-----------------------
/*
* set RTC when system time ticks over, and then read system time on
* ten RTC interrupts. An offset of 500,000 usec (0.5sec) will appear
* for rtc drivers that don't schedule_timeout(HZ/2) before setting rtc
* time, since the second for which the rtc is set is only 1/2 sec long.
* With schedule_timeout(), it will be between (n-1)990,000 and (n)10,000
* (Assumes RTC=UTC; change gmtime() to localtime() if not the case)
*/

#include <stdio.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>

void main(void)

{

int i, fd, retval;
unsigned long data;
struct tm *tm;

struct timezone tz = {0,};
struct timeval start, now;

fd = open ("/dev/rtc", O_RDONLY);

if (fd == -1) {
perror("/dev/rtc");
exit(errno);
}

printf("ZZZzzz. Waiting for next second...\n");
gettimeofday(&start, &tz);
do {
gettimeofday(&now, &tz);
} while (now.tv_sec == start.tv_sec);

tm = gmtime(&start.tv_sec);

retval = ioctl(fd, RTC_SET_TIME, tm); /* need root */
if (retval == -1) {
perror("ioctl");
exit(errno);
}

ioctl(fd, RTC_UIE_ON);
printf("10 gettimeofday values (sec, usec) exactly when RTC updates:\n");

gettimeofday(&start, &tz);
for (i=0;i<10;i++){
read(fd, &data, sizeof(unsigned long)); /* this blocks */
gettimeofday(&now, &tz);
printf("\t%ld\t%ld\n", now.tv_sec-start.tv_sec, now.tv_usec);
}

}

_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com