Subject: footbridge support for timecounter
To: None <port-cats@NetBSD.org, port-netwinder@NetBSD.org,>
From: Garrett D'Amore <garrett_damore@tadpole.com>
List: port-arm
Date: 09/10/2006 22:41:05
This is a multi-part message in MIME format.
--------------070400000505030402010803
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

The following patch is my attempt at enabling timecounters for
footbridge based ARM32 systems.  As far as I can see, this is only cats
and netwinder.

Thus I've dispensed with all the checks for __HAVE_TIMECOUNTER and just
assumed it for both ports.

I've changed the semantics of the delay timer (previously used for
delay()) slightly -- it now is let run free, rather than being
periodic.  This works better for timecounters.

I have _not_ tested this patch, although it does compile.  If someone
could verify it, I would be grateful, as it converts not one but _two_
ports. :-)

Thanks.

-- 
Garrett D'Amore, Principal Software Engineer
Tadpole Computer / Computing Technologies Division,
General Dynamics C4 Systems
http://www.tadpolecomputer.com/
Phone: 951 325-2134  Fax: 951 325-2191


--------------070400000505030402010803
Content-Type: text/x-patch;
 name="footbridge.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="footbridge.diff"

Index: sys/arch/netwinder/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/netwinder/include/types.h,v
retrieving revision 1.5
diff -d -p -u -r1.5 types.h
--- sys/arch/netwinder/include/types.h	3 Sep 2006 13:51:23 -0000	1.5
+++ sys/arch/netwinder/include/types.h	11 Sep 2006 05:33:21 -0000
@@ -7,5 +7,6 @@
 
 #define __HAVE_GENERIC_SOFT_INTERRUPTS
 #define __HAVE_DEVICE_REGISTER
+#define	__HAVE_TIMECOUNTER
 
 #endif
Index: sys/arch/cats/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cats/include/types.h,v
retrieving revision 1.6
diff -d -p -u -r1.6 types.h
--- sys/arch/cats/include/types.h	3 Sep 2006 13:51:23 -0000	1.6
+++ sys/arch/cats/include/types.h	11 Sep 2006 05:33:21 -0000
@@ -6,5 +6,6 @@
 #include <arm/arm32/types.h>
 #define __HAVE_GENERIC_SOFT_INTERRUPTS
 #define __HAVE_DEVICE_REGISTER
+#define	__HAVE_TIMECOUNTER
 
 #endif
Index: sys/arch/arm/footbridge/footbridge_clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/footbridge/footbridge_clock.c,v
retrieving revision 1.21
diff -d -p -u -r1.21 footbridge_clock.c
--- sys/arch/arm/footbridge/footbridge_clock.c	17 Apr 2006 00:03:17 -0000	1.21
+++ sys/arch/arm/footbridge/footbridge_clock.c	11 Sep 2006 05:33:21 -0000
@@ -44,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: footbridge_c
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/time.h>
+#include <sys/timetc.h>
 #include <sys/device.h>
 
 #include <machine/intr.h>
@@ -57,9 +58,9 @@ __KERNEL_RCSID(0, "$NetBSD: footbridge_c
 extern struct footbridge_softc *clock_sc;
 extern u_int dc21285_fclk;
 
-int clockhandler __P((void *));
-int statclockhandler __P((void *));
-static int load_timer __P((int, int));
+int clockhandler(void *);
+int statclockhandler (void *);
+static int load_timer(int, int);
 
 /*
  * Statistics clock variance, in usec.  Variance must be a
@@ -74,9 +75,11 @@ int statmin;			/* minimum stat clock cou
 int statcountperusec;		/* number of ticks per usec at current stathz */
 int statprev;			/* last value of we set statclock to */
 
+void footbridge_tc_init(void);
+
 #if 0
-static int clockmatch	__P((struct device *parent, struct cfdata *cf, void *aux));
-static void clockattach	__P((struct device *parent, struct device *self, void *aux));
+static int clockmatch(struct device *parent, struct cfdata *cf, void *aux);
+static void clockattach(struct device *parent, struct device *self, void *aux);
 
 CFATTACH_DECL(footbridge_clock, sizeof(struct clock_softc),
     clockmatch, clockattach, NULL, NULL);
@@ -88,16 +91,13 @@ CFATTACH_DECL(footbridge_clock, sizeof(s
  */ 
  
 static int
-clockmatch(parent, cf, aux)
-	struct device *parent;
-	struct cfdata *cf;
-	void *aux;
+clockmatch(struct device *parent, struct cfdata *cf, void *aux)
 {
 	union footbridge_attach_args *fba = aux;
 
 	if (strcmp(fba->fba_ca.ca_name, "clk") == 0)
-		return(1);
-	return(0);
+		return 1;
+	return 0;
 }
 
 
@@ -107,10 +107,7 @@ clockmatch(parent, cf, aux)
  */
   
 static void
-clockattach(parent, self, aux)
-	struct device *parent;
-	struct device *self;
-	void *aux;
+clockattach(struct device *parent, struct device *self, void *aux)
 {
 	struct clock_softc *sc = (struct clock_softc *)self;
 	union footbridge_attach_args *fba = aux;
@@ -134,14 +131,13 @@ clockattach(parent, self, aux)
  */
 
 int
-clockhandler(aframe)
-	void *aframe;
+clockhandler(void *aframe)
 {
 	struct clockframe *frame = aframe;
 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
 	    TIMER_1_CLEAR, 0);
 	hardclock(frame);
-	return(0);	/* Pass the interrupt on down the chain */
+	return 0;	/* Pass the interrupt on down the chain */
 }
 
 /*
@@ -152,8 +148,7 @@ clockhandler(aframe)
  */
  
 int
-statclockhandler(aframe)
-	void *aframe;
+statclockhandler(void *aframe)
 {
 	struct clockframe *frame = aframe;
 	int newint, r;
@@ -201,13 +196,11 @@ statclockhandler(aframe)
 		 */
 		statclock(frame);
 
-	return(0);	/* Pass the interrupt on down the chain */
+	return 0;	/* Pass the interrupt on down the chain */
 }
 
 static int
-load_timer(base, herz)
-	int base;
-	int herz;
+load_timer(int base, int herz)
 {
 	unsigned int timer_count;
 	int control;
@@ -229,7 +222,7 @@ load_timer(base, herz)
 	    base + TIMER_CONTROL, control);
 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
 	    base + TIMER_CLEAR, 0);
-	return(timer_count);
+	return timer_count;
 }
 
 /*
@@ -239,8 +232,7 @@ load_timer(base, herz)
  */
 
 void
-setstatclockrate(herz)
-	int herz;
+setstatclockrate(int herz)
 {
 	int statint;
 	int countpersecond;
@@ -273,7 +265,7 @@ setstatclockrate(herz)
  */
  
 void
-cpu_initclocks()
+cpu_initclocks(void)
 {
 	/* stathz and profhz should be set to something, we have the timer */
 	if (stathz == 0)
@@ -311,62 +303,29 @@ cpu_initclocks()
 			panic("%s: Cannot install timer 2 interrupt handler",
 			    clock_sc->sc_dev.dv_xname);
 	}
-}
 
+	footbridge_tc_init();
+}
 
-/*
- * void microtime(struct timeval *tvp)
- *
- * Fill in the specified timeval struct with the current time
- * accurate to the microsecond.
- */
+static uint32_t
+fclk_get_count(struct timecounter *tc)
+{
+	return (TIMER_MAX_VAL -
+	    bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
+	    TIMER_3_VALUE));
+}
 
 void
-microtime(tvp)
-	struct timeval *tvp;
+footbridge_tc_init(void)
 {
-	int s;
-	int tm;
-	int deltatm;
-	static struct timeval oldtv;
-
-	if (clock_sc == NULL || clock_sc->sc_clock_count == 0)
-		return;
-
-	s = splhigh();
-
-	tm = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
-	    TIMER_1_VALUE);
-
-	deltatm = clock_sc->sc_clock_count - tm;
-
-#ifdef DIAGNOSTIC
-	if (deltatm < 0)
-		panic("opps deltatm < 0 tm=%d deltatm=%d", tm, deltatm);
-#endif
-
-	/* Fill in the timeval struct */
-	*tvp = time;
-	tvp->tv_usec += ((deltatm << 8) / clock_sc->sc_clock_ticks_per_256us);
-
-	/* Make sure the micro seconds don't overflow. */
-	while (tvp->tv_usec >= 1000000) {
-		tvp->tv_usec -= 1000000;
-		++tvp->tv_sec;
-	}
-
-	/* 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;
-		}
-	}
-
-	oldtv = *tvp;
-	(void)splx(s);		
+	struct timecounter fb_tc = {
+		.tc_get_timecount = fclk_get_count,
+		.tc_counter_mask = TIMER_MAX_VAL,
+		.tc_name = "dc21285_fclk",
+		.tc_quality = 100
+	};
+	fb_tc.tc_frequency = dc21285_fclk;
+	tc_init(&fb_tc);
 }
 
 /*
@@ -374,96 +333,75 @@ microtime(tvp)
  * rely on an estimated loop, however footbridge is attached very early on.
  */
 
-static int delay_clock_count = 0;
 static int delay_count_per_usec = 0;
 
 void
 calibrate_delay(void)
 {
-     delay_clock_count = load_timer(TIMER_3_BASE, 100);
-     delay_count_per_usec = delay_clock_count/10000;
-#ifdef VERBOSE_DELAY_CALIBRATION
-     printf("delay calibration: delay_cc = %d, delay_c/us=%d\n",
-		     delay_clock_count, delay_count_per_usec);
-     
-     printf("0..");
-     delay(1000000);
-     printf("1..");
-     delay(1000000);
-     printf("2..");
-     delay(1000000);
-     printf("3..");
-     delay(1000000);
-     printf("4..");
-      delay(1000000);
-     printf("5..");
-      delay(1000000);
-     printf("6..");
-      delay(1000000);
-     printf("7..");
-      delay(1000000);
-     printf("8..");
-      delay(1000000);
-     printf("9..");
-      delay(1000000);
-     printf("10\n");
-#endif
+	/*
+	 * For all current footbridge hardware, the fclk runs at a
+	 * rate that is sufficiently slow enough that we don't need to
+	 * use a prescaler.  A prescaler would be needed if the fclk
+	 * could wrap within 2 hardclock periods (2 * HZ).  With
+	 * normal values of HZ (100 and higher), this is unlikely to
+	 * ever happen.
+	 *
+	 * We let TIMER 3 just run free.
+	 */
+	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
+	    TIMER_3_BASE + TIMER_CONTROL, TIMER_ENABLE);
+	delay_count_per_usec = dc21285_fclk / 1000000;
+	if (dc21285_fclk % 1000000)
+		delay_count_per_usec += 1;
 }
 
-int delaycount = 25000;
-
 void
-delay(n)
-	u_int n;
+delay(unsigned n)
 {
-	volatile u_int i;
 	uint32_t cur, last, delta, usecs;
 
-	if (n == 0) return;
+	if (n == 0)
+		return;
 
 	/* 
 	 * not calibrated the timer yet, so try to live with this horrible
 	 * loop!
+	 *
+	 * Note: a much better solution might be to have the timers
+	 * get get calibrated out of mach_init.  Of course, the
+	 * clock_sc needs to be set up, so we can read/write the clock
+	 * registers.
 	 */
-	if (delay_clock_count == 0)
+	if (!delay_count_per_usec)
 	{
-	    while (n-- > 0) {
-		for (i = delaycount; --i;);
-	    }
-	    return;	
+		int delaycount = 25000;
+		volatile int i;
+
+		while (n-- > 0) {
+			for (i = delaycount; --i;);
+		}
+		return;	
 	}
 
-	/* 
-	 * read the current value (do not reset it as delay is reentrant)
-	 */
 	last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
+	    TIMER_3_VALUE);
+	delta = usecs = 0;
+	
+	while (n > usecs) {
+		cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
 		    TIMER_3_VALUE);
-	 
-	delta = 0;
+		if (last < cur)
+			/* timer has wrapped */
+			delta += ((TIMER_MAX_VAL - cur) + last);
+		else
+			delta += (last - cur);
 
-	usecs = n * delay_count_per_usec;
+		last = cur;
 
-	while (usecs > delta)
-	{
-	    cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
-		    TIMER_3_VALUE);
-	    if (last < cur)
-		/* timer has wrapped */
-		delta += ((delay_clock_count - cur) + last);
-	    else
-		delta += (last - cur);
-	    
-	    if (cur == 0)
-	    {
-		/*
-		 * reset the timer, note that if something blocks us for more
-		 * than 1/100s we may delay for too long, but I believe that
-		 * is fairly unlikely.
-		 */
-		bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
-			TIMER_3_CLEAR, 0);
-	    }
-	    last = cur;
+		while (delta >= delay_count_per_usec) {
+			delta -= delay_count_per_usec;
+			usecs++;
+		}
 	}
 }
 
Index: sys/arch/arm/footbridge/todclock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/footbridge/todclock.c,v
retrieving revision 1.9
diff -d -p -u -r1.9 todclock.c
--- sys/arch/arm/footbridge/todclock.c	24 Dec 2005 20:06:52 -0000	1.9
+++ sys/arch/arm/footbridge/todclock.c	11 Sep 2006 05:33:21 -0000
@@ -198,7 +198,7 @@ resettodr()
 	if (todclock_sc->sc_rtc_write == NULL)
 		return;
 
-	sec = time.tv_sec;
+	sec = time_second;
 	sec -= rtc_offset * 60;
 	year = (sec / SECPER4YEARS) * 4;
 	sec %= SECPER4YEARS;
@@ -269,8 +269,7 @@ inittodr(base)
 	 */
 
 	/* Use the suggested time as a fall back */
-	time.tv_sec = base;
-	time.tv_usec = 0;
+	time_second = base;
 
 	/* Can we read an RTC ? */
 	if (todclock_sc != NULL && todclock_sc->sc_rtc_read) {
@@ -302,8 +301,7 @@ inittodr(base)
 
 	n += rtc_offset * 60;
 
-	time.tv_sec = n;
-	time.tv_usec = 0;
+	time_second = n;
 
 	/* timeset is used to ensure the time is valid before a resettodr() */
 

--------------070400000505030402010803--