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

[microblaze-uclinux] [patch] microblaze kernel time precision



Hi folks,

I'm starting some work on strengthening kernel timekeeping for microblaze.

Attached is the first patch which enables effectively microsecond 
precision in the kernel timekeeping (inspired by m68knommu's handing of 
same).  Currently, the gettimeofday() system call returns times with 
only 10ms precision (ie the system tick) - for example the elapsed times 
returned by ping.  This patch uses the system timer to allow sub-tick 
level time reporting.

Yashi, I've included support for suzaku - can you please test it and let 
me know?  I'll wait until I hear from you before applying to CVS.

Cheers,

John

Index: arch/microblaze/kernel/mach.h
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/mach.h,v
retrieving revision 1.1
diff -u -b -B -w -p -r1.1 mach.h
--- arch/microblaze/kernel/mach.h	2 Apr 2003 05:07:14 -0000	1.1
+++ arch/microblaze/kernel/mach.h	14 Jul 2004 06:43:40 -0000
@@ -30,6 +30,7 @@
 void mach_heartbeat(unsigned int value);
 void mach_setup (char **cmdline);
 void mach_gettimeofday (struct timeval *tv);
+unsigned long mach_gettimeoffset(void);
 void mach_sched_init (struct irqaction *timer_action);
 void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len);
 void mach_init_irqs (void);
Index: arch/microblaze/kernel/mbvanilla.c
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/mbvanilla.c,v
retrieving revision 1.10
diff -u -b -B -w -p -r1.10 mbvanilla.c
--- arch/microblaze/kernel/mbvanilla.c	9 Jul 2004 00:24:40 -0000	1.10
+++ arch/microblaze/kernel/mbvanilla.c	14 Jul 2004 06:43:41 -0000
@@ -70,6 +70,32 @@ void mach_heartbeat(unsigned int value)
 	microblaze_gpio_write(MICROBLAZE_GPIO_BASE_ADDR,cur_val);
 }
 
+/* mach_gettimeoffset returns the number of microseconds elapsed since the
+   start of the current tick */
+unsigned long mach_gettimeoffset(void)
+{
+	unsigned long tctr=timer_get_time(MICROBLAZE_TIMER_BASE_ADDR,0);
+	unsigned long tcmp=timer_get_compare(MICROBLAZE_TIMER_BASE_ADDR,0);
+	unsigned long offset;
+
+	/*
+	 * Timer used in count up mode, so subtract load value from
+	 * counter register to get elapsed ticks */
+	offset = (tctr-tcmp)/(CONFIG_CPU_CLOCK_FREQ/1000000);
+
+	/*
+	 * If we are still in the first half of the upcount and a
+	 * timer interupt is pending, then add on a tick's worth of time.
+	 */
+
+	if (((offset * 2) < (1000000/HZ)) && 
+		(timer_get_csr(MICROBLAZE_TIMER_BASE_ADDR,0)&TIMER_INTERRUPT))
+	{
+		offset += 1000000/HZ;
+	}
+
+	return offset;
+}
 
 /* mach_early_init() is called very early, after the kernel stack and SDA 
    pointers are configured, but before BSS/SBSS are zeroed etc.  Be careful 
Index: arch/microblaze/kernel/microblaze_timer.c
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/microblaze_timer.c,v
retrieving revision 1.3
diff -u -b -B -w -p -r1.3 microblaze_timer.c
--- arch/microblaze/kernel/microblaze_timer.c	5 Apr 2004 22:33:49 -0000	1.3
+++ arch/microblaze/kernel/microblaze_timer.c	14 Jul 2004 06:43:42 -0000
@@ -27,11 +27,11 @@ void microblaze_timer_configure (unsigne
 	 * Work out timer compare value for a given clock freq 
 	 * and interrupt rate
 	 */
-	count = CONFIG_CPU_CLOCK_FREQ / rate;
+	count = ((unsigned)-1) - CONFIG_CPU_CLOCK_FREQ / rate - 2;
 
 	/* Do the actual hardware timer initialization:  */
 
-	/* Enable timer, enable interrupt generation, and put into count down mode */
+	/* Enable timer, enable interrupt generation, and put into count up mode */
 	/* Set the compare counter */
 	timer_set_compare(MICROBLAZE_TIMER_BASE_ADDR, timer, count);
 
@@ -41,6 +41,7 @@ void microblaze_timer_configure (unsigne
 
 	/* start the timer */
 	timer_set_csr(MICROBLAZE_TIMER_BASE_ADDR, timer, 
-			TIMER_ENABLE | TIMER_ENABLE_INTR | TIMER_RELOAD | TIMER_DOWN_COUNT);
+			TIMER_ENABLE | TIMER_ENABLE_INTR | TIMER_RELOAD);
+			/* TIMER_ENABLE | TIMER_ENABLE_INTR | TIMER_RELOAD | TIMER_UP_COUNT); */
 
 }
Index: arch/microblaze/kernel/suzaku.c
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/suzaku.c,v
retrieving revision 1.3
diff -u -b -B -w -p -r1.3 suzaku.c
--- arch/microblaze/kernel/suzaku.c	9 Jul 2004 00:24:40 -0000	1.3
+++ arch/microblaze/kernel/suzaku.c	14 Jul 2004 06:43:42 -0000
@@ -71,6 +71,33 @@ void mach_heartbeat(unsigned int value)
 }
 
 
+/* mach_gettimeoffset returns the number of microseconds elapsed since the
+   start of the current tick */
+unsigned long mach_gettimeoffset(void)
+{
+	unsigned long tctr=timer_get_time(MICROBLAZE_TIMER_BASE_ADDR,0);
+	unsigned long tcmp=timer_get_compare(MICROBLAZE_TIMER_BASE_ADDR,0);
+	unsigned long offset;
+
+	/*
+	 * Timer used in count up mode, so subtract load value from
+	 * counter register to get elapsed ticks */
+	offset = (tctr-tcmp)/(CONFIG_CPU_CLOCK_FREQ/1000000);
+
+	/*
+	 * If we are still in the first half of the upcount and a
+	 * timer interupt is pending, then add on a tick's worth of time.
+	 */
+
+	if (((offset * 2) < (1000000/HZ)) && 
+		(timer_get_csr(MICROBLAZE_TIMER_BASE_ADDR,0)&TIMER_INTERRUPT))
+	{
+		offset += 1000000/HZ;
+	}
+
+	return offset;
+}
+
 /* mach_early_init() is called very early, after the kernel stack and SDA 
    pointers are configured, but before BSS/SBSS are zeroed etc.  Be careful 
    what you do in here.  The bss and sbss sections are zeroed in here */
Index: arch/microblaze/kernel/time.c
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/arch/microblaze/kernel/time.c,v
retrieving revision 1.1
diff -u -b -B -w -p -r1.1 time.c
--- arch/microblaze/kernel/time.c	2 Apr 2003 05:07:14 -0000	1.1
+++ arch/microblaze/kernel/time.c	14 Jul 2004 06:43:42 -0000
@@ -63,6 +64,7 @@ static void timer_interrupt (int irq, vo
 	if (! user_mode (regs))
 		do_profile (regs->pc);
 
+	/* Simple tick tock timer */
 	{
 		static int ticks=0, value=0, period=25;
 		if(ticks++>=period)
@@ -102,34 +104,27 @@ static void timer_interrupt (int irq, vo
 	/* write the timer status back to the control register, 
 	 * to clear the interrupt.  Once again, timer 0 is hard coded */
 	timer_set_csr(MICROBLAZE_TIMER_BASE_ADDR, 0, csr);
-	
 }
 
 extern rwlock_t xtime_lock;
 
 /*
  * This version of gettimeofday has near microsecond resolution.
+ * Taken from /arch/m68knommu/kernel/time.c
  */
 void do_gettimeofday (struct timeval *tv)
 {
-#if 0 /* DAVIDM later if possible */
-	extern volatile unsigned long lost_ticks;
-	unsigned long lost;
-#endif
+	static long prev_sec=0, prev_usec=0;
+	extern volatile unsigned long wall_jiffies;
 	unsigned long flags;
-	unsigned long usec, sec;
+	unsigned long usec, sec, lost;
 
 	read_lock_irqsave (&xtime_lock, flags);
-#if 0
 	usec = mach_gettimeoffset ? mach_gettimeoffset () : 0;
-#else
-	usec = 0;
-#endif
-#if 0 /* DAVIDM later if possible */
-	lost = lost_ticks;
+
+	lost = jiffies-wall_jiffies;
 	if (lost)
 		usec += lost * (1000000/HZ);
-#endif
 	sec = xtime.tv_sec;
 	usec += xtime.tv_usec;
 	read_unlock_irqrestore (&xtime_lock, flags);
@@ -141,6 +136,9 @@ void do_gettimeofday (struct timeval *tv
 
 	tv->tv_sec = sec;
 	tv->tv_usec = usec;
+
+	prev_sec=sec;
+	prev_usec=usec;
 }
 
 void do_settimeofday (struct timeval *tv)
@@ -181,6 +179,7 @@ static struct irqaction timer_irqaction 
 
 void time_init (void)
 {
+	/* query RTC (if avail) to set time on startup */
 	mach_gettimeofday (&xtime);
 	mach_sched_init (&timer_irqaction);
 }
Index: include/asm-microblaze/microblaze_timer.h
===================================================================
RCS file: /var/cvs/uClinux-2.4.x/include/asm-microblaze/microblaze_timer.h,v
retrieving revision 1.1
diff -u -b -B -w -p -r1.1 microblaze_timer.h
--- include/asm-microblaze/microblaze_timer.h	2 Apr 2003 05:07:16 -0000	1.1
+++ include/asm-microblaze/microblaze_timer.h	14 Jul 2004 06:44:14 -0000
@@ -83,6 +83,12 @@
 #define timer_get_capture(base_addr, timer_number) (*TIMER_TCCR(base_addr, timer_number))
 
 /*
+ * Get the compare register
+ */
+
+#define timer_get_compare(base_addr, timer_number) (*TIMER_TCCR(base_addr, timer_number))
+
+/*
  * Set the compare register
  */