The time drift problem I have been chasing for a while, see
http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2012-December/025821.html
, seems to be due to an undocumented silicon bug.

The attached patch to master ( c22cf7755db6500df037cecdd6b290394d82392e
  Origin: git://gitorious.org/linux-davinci/linux-davinci.git )
exercises the problem. Example console output from my EVM6446 card:

----------------
Before  T0:0x5ed55a T1:0x5ed55d Diff:-3
After  T0:0x5ed817 T1:0x5ed824 Diff:-13
Before  T0:0x62a7e5 T1:0x62a7f3 Diff:-14
After  T0:0x62adff T1:0x62ae17 Diff:-24
Before  T0:0x66cbfb T1:0x66cc14 Diff:-25
After  T0:0x66d1f6 T1:0x66d219 Diff:-35
Before  T0:0x6aeab0 T1:0x6aead4 Diff:-36
After  T0:0x6af0ec T1:0x6af11a Diff:-46
Before  T0:0x6f09ef T1:0x6f0a1e Diff:-47
After  T0:0x6f1018 T1:0x6f1051 Diff:-57
Before  T0:0x732821 T1:0x73285b Diff:-58
After  T0:0x732e1a T1:0x732e5e Diff:-68
---------------

This shows that TIM34_0 misses 11 ticks every time a new event is setup
(10 ticks from my test loop + the original code 1).

If someone from TI can verify that the problem exists in all Davinci
versions, a workaround should be pretty straightforward, assuming this
is the only symptom of the bug.

Regards,
/Thomas


diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c
index 9847938..95be656 100644
--- a/arch/arm/mach-davinci/time.c
+++ b/arch/arm/mach-davinci/time.c
@@ -117,6 +117,61 @@ static char *id_to_name[] = {
 	[T1_TOP]	= "timer1_1",
 };
 
+/*******************************************************************
+  Test code by tho...@corelatus.se 121212
+  Exercises a timer problem with Davinci, likely a silicon bug.
+  For each write to TIM12, the TIM34 counter misses a tick.
+  With current kernel, this causes time to drift 37 ns every time
+  a new event is set. This can accumulate to 1000 ppms
+  (1 ms per second) if lots of context switches.
+
+  Verified to exist in:
+    DM6443 rev 2.1
+    DM6446 rev 1.3
+*********************************************************************/
+
+static inline void show_timer_diff(const char *str){
+	u32 tim34_0, tim34_1;
+
+	tim34_1 = __raw_readl(IO_ADDRESS(DAVINCI_TIMER1_BASE + TIM34));
+	tim34_0 = __raw_readl(IO_ADDRESS(DAVINCI_TIMER0_BASE + TIM34));
+
+	printk("%s  T0:0x%x T1:0x%x Diff:%d\n",
+	       str,
+	       tim34_0,
+	       tim34_1,
+	       tim34_0 - tim34_1);
+}
+
+/* When setting up Timer 0-34, setup Timer 1-34 with the same settings.
+   This way, they should always have about the same counter value. */
+static void mirror(struct timer_s *t){
+	/* Timer 1 is +0x400 */
+	volatile void *base = t->base + 0x400;
+	u32 tcr;
+
+	tcr = __raw_readl(base + TCR);
+
+	/* disable timer */
+	tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift);
+	__raw_writel(tcr, base + TCR);
+
+	/* reset counter to zero, set new period */
+	__raw_writel(0, base + t->tim_off);
+	__raw_writel(t->period, base + t->prd_off);
+
+	/* Set enable mode */
+	if (t->opts & TIMER_OPTS_ONESHOT)
+		tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift;
+	else if (t->opts & TIMER_OPTS_PERIODIC)
+		tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift;
+
+	__raw_writel(tcr, base + TCR);
+}
+
+#define CHECK_LAPS 20
+static u32 chk = ~0;
+
 static int timer32_config(struct timer_s *t)
 {
 	u32 tcr;
@@ -142,6 +197,19 @@ static int timer32_config(struct timer_s *t)
 		tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift);
 		__raw_writel(tcr, t->base + TCR);
 
+		if( chk < CHECK_LAPS && t->id == 0 ){
+			int i;
+			show_timer_diff("Before");
+			/* TIM34_0 seems to miss 1 tick every time we write
+			   to TIM12_0!
+			   So, this loop makes us miss 10 ticks: */
+			for(i=0;i<10;i++){
+				__raw_writel(0, t->base + t->tim_off);
+			}
+			show_timer_diff("After");
+			chk++;
+		}
+
 		/* reset counter to zero, set new period */
 		__raw_writel(0, t->base + t->tim_off);
 		__raw_writel(t->period, t->base + t->prd_off);
@@ -152,6 +220,15 @@ static int timer32_config(struct timer_s *t)
 		else if (t->opts & TIMER_OPTS_PERIODIC)
 			tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift;
 
+		if(t->id == 1){
+			/* Timer 0-34 setup
+			   Setup 1-34 as a mirror to 0-34 */
+			mirror(t);
+
+			/* Start checking TIM counters */
+			chk = 0;
+		}
+
 		__raw_writel(tcr, t->base + TCR);
 	}
 	return 0;
@@ -381,6 +458,10 @@ static void __init davinci_timer_init(void)
 	BUG_ON(IS_ERR(timer_clk));
 	clk_prepare_enable(timer_clk);
 
+	timer_clk = clk_get(NULL, "timer1");
+	BUG_ON(IS_ERR(timer_clk));
+	clk_enable(timer_clk);
+
 	/* init timer hw */
 	timer_init();
 

Attachment: config.gz
Description: Unix tar archive

_______________________________________________
Davinci-linux-open-source mailing list
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to