Subject: timecounters was Re:Fixes for acorn32 build after GCC4 upgrade
To: Ben Harris <bjh21@NetBSD.org>
From: Mike Pumford <mpumford@black-star.demon.co.uk>
List: port-acorn32
Date: 08/03/2006 00:09:05
This is a multi-part message in MIME format.
--------------020506000306020003050608
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Ben Harris wrote:

> That will only work so long as timer 1 isn't being used to provide a 
> properly randomised statclock, which it really should be made to do.  It 
> would be better to build something based on timer 0, using the 
> interrupt-pending bit and the hardclock handler to emulate the high-order 
> bits of the timecounter.
> 
Okay, I've now done this (apart from the interrupt pending bit part) 
modelled on the i8254 timer from i386. I don't think the i8254 code 
actually utilises the interrupt pending flag either.

Not done much testing of the patch but ntpd doesn't seem too upset and 
the system still seems to keep reasonable time. Don't know if there are 
any other timecounter tests I can do.

Patch is attached to this message.

Next up a delay() function that doesn't rely on a ridiculously 
inaccurate for loop.

Mike

--------------020506000306020003050608
Content-Type: text/plain;
 name="iomd_timecounter.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="iomd_timecounter.patch"

Index: sys/arch/acorn32/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/acorn32/include/types.h,v
retrieving revision 1.3
diff -u -r1.3 types.h
--- sys/arch/acorn32/include/types.h	28 Feb 2002 03:17:24 -0000	1.3
+++ sys/arch/acorn32/include/types.h	2 Aug 2006 22:59:42 -0000
@@ -7,5 +7,5 @@
 
 #define	__HAVE_DEVICE_REGISTER
 #define	__HAVE_NWSCONS
-
+#define __HAVE_TIMECOUNTER
 #endif
Index: sys/arch/arm/iomd/iomd_clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd_clock.c,v
retrieving revision 1.18
diff -u -r1.18 iomd_clock.c
--- sys/arch/arm/iomd/iomd_clock.c	25 Jun 2006 21:32:41 -0000	1.18
+++ sys/arch/arm/iomd/iomd_clock.c	2 Aug 2006 22:59:42 -0000
@@ -52,7 +52,9 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/time.h>
+#include <sys/timetc.h>
 #include <sys/device.h>
+#include <sys/lock.h>
 
 #include <dev/clock_subr.h>
 
@@ -76,6 +78,7 @@
 static void *statclockirq;
 static struct clock_softc *clock_sc;
 static int timer0_count;
+static int timeset;
 
 static int clockmatch	__P((struct device *parent, struct cfdata *cf, void *aux));
 static void clockattach	__P((struct device *parent, struct device *self, void *aux));
@@ -83,6 +86,26 @@
 static void checkdelay	__P((void));
 #endif
 
+static u_int iomd_timecounter0_get(struct timecounter *tc);
+
+
+static volatile uint32_t timer0_lastcount;
+static volatile uint32_t timer0_offset;
+static volatile int timer0_ticked;
+/* TODO: Get IRQ status */
+
+static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER;  /* protect TC timer variables */
+
+
+static struct timecounter iomd_timecounter = {
+	iomd_timecounter0_get,
+	0, /* No poll_pps */
+	~0, /* 32bit accuracy */
+	TIMER_FREQUENCY,
+	"iomd_timer0",
+	100 
+};
+
 int clockhandler	__P((void *));
 int statclockhandler	__P((void *));
 
@@ -136,6 +159,24 @@
 }
 
 
+static void
+tickle_tc(void) 
+{
+	if (timer0_count && 
+	    timecounter->tc_get_timecount == iomd_timecounter0_get) {
+		simple_lock(&tmr_lock);
+		if (timer0_ticked)
+			timer0_ticked    = 0;
+		else {
+			timer0_offset   += timer0_count;
+			timer0_lastcount = 0;
+		}
+		simple_unlock(&tmr_lock);
+	}
+
+}
+
+
 /*
  * int clockhandler(struct clockframe *frame)
  *
@@ -149,6 +190,7 @@
 	void *cookie;
 {
 	struct clockframe *frame = cookie;
+	tickle_tc();
 
 	hardclock(frame);
 	return(0);	/* Pass the interrupt on down the chain */
@@ -268,35 +310,21 @@
 #ifdef DIAGNOSTIC
 	checkdelay();
 #endif
+	tc_init(&iomd_timecounter);
 }
 
 
-/*
- * void microtime(struct timeval *tvp)
- *
- * Fill in the specified timeval struct with the current time
- * accurate to the microsecond.
- */
 
-void
-microtime(tvp)
-	struct timeval *tvp;
+static u_int iomd_timecounter0_get(struct timecounter *tc)
 {
 	int s;
-	int tm;
-	int deltatm;
-	static struct timeval oldtv;
-
-	if (timer0_count == 0)
-		return;
-
-	s = splhigh();
+	u_int tm;
 
 	/*
 	 * Latch the current value of the timer and then read it.
 	 * This garentees an atmoic reading of the time.
 	 */
-
+	s = splhigh();
 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
 	    IOMD_T0LATCH, 0);
  
@@ -304,37 +332,27 @@
 	    IOMD_T0LOW);
 	tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
 	    IOMD_T0HIGH) << 8);
+	splx(s);
+	simple_lock(&tmr_lock);
 
-	deltatm = timer0_count - tm;
-	if (deltatm < 0)
-		printf("opps deltatm < 0 tm=%d deltatm=%d\n",
-		    tm, deltatm);
-
-	/* Fill in the timeval struct */
-	*tvp = time;
-
-	tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND);
-
-	/* Make sure the micro seconds don't overflow. */
-	while (tvp->tv_usec >= 1000000) {
-		tvp->tv_usec -= 1000000;
-		++tvp->tv_sec;
-	}
+	tm = timer0_count - tm;
+	
 
-	/* Make sure the time has advanced. */
-	if (tvp->tv_sec == oldtv.tv_sec &&
-	    tvp->tv_usec <= oldtv.tv_usec) {
-		tvp->tv_usec = oldtv.tv_usec + 1;
-		if (tvp->tv_usec >= 1000000) {
-			tvp->tv_usec -= 1000000;
-			++tvp->tv_sec;
-		}
+	if (timer0_count &&
+	    (tm < timer0_lastcount || (!timer0_ticked && FALSE/* XXX: clkintr_pending */))) {
+		timer0_ticked = 1;
+		timer0_offset += timer0_count;
 	}
-	    
-	oldtv = *tvp;
-	(void)splx(s);		
+
+	timer0_lastcount = tm;
+	tm += timer0_offset;
+
+	simple_unlock(&tmr_lock);
+	return tm;
 }
 
+
+
 /*
  * Estimated loop for n microseconds
  */
@@ -389,7 +407,13 @@
 {
 	time_t deltat;
 	int badbase;
-
+	int badtime;
+	struct timeval time;
+	struct timespec tc_time; /* for timecounters */
+	
+	/* Default is to assume that time from somewhere will be okay. 
+	 * If not badtime will be set to 1 */
+	badtime = 0;
 	if (base < (MINYEAR - 1970) * SECYR) {
 		printf("WARNING: preposterous time in file system");
 		/* read the system clock anyway */
@@ -411,7 +435,7 @@
 			printf("WARNING: preposterous clock chip time\n");
 			resettodr();
 		}
-		goto bad;
+		badtime = 1;
 	}
 
 	if (!badbase) {
@@ -422,14 +446,20 @@
 		deltat = time.tv_sec - base;
 		if (deltat < 0)
 			deltat = -deltat;
-		if (deltat < 2 * SECDAY)
-			return;		/* all is well */
+		if (deltat >= 2 * SECDAY)
 		printf("WARNING: clock %s %ld days\n",
-		    time.tv_sec < base ? "lost" : "gained",
-		    (long)deltat / SECDAY);
+		       time.tv_sec < base ? "lost" : "gained",
+		       (long)deltat / SECDAY);
 	}
- bad:
-	printf("WARNING: CHECK AND RESET THE DATE!\n");
+	/* At this point time is a time value we can initialise the system 
+	 * clock with */
+	tc_time.tv_sec = time.tv_sec;
+	tc_time.tv_nsec = time.tv_usec * 1000;
+	tc_setclock(&tc_time);
+	timeset = 1;
+	
+	if (badtime)
+		printf("WARNING: CHECK AND RESET THE DATE!\n");
 }
 
 /*
@@ -440,10 +470,11 @@
 void
 resettodr(void)
 {
-
-	if (time.tv_sec == 0)
+	struct timeval time;
+	if (!timeset)
 		return;
 
+	getmicrotime(&time);
 	if (todr_handle != NULL &&
 	    todr_settime(todr_handle, &time) != 0)
 		printf("resettodr: failed to set time\n");

--------------020506000306020003050608--