public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] OMAP2: timer+irq fix up
@ 2006-03-06 16:10 Woodruff, Richard
  2006-03-06 21:59 ` Tony Lindgren
  0 siblings, 1 reply; 6+ messages in thread
From: Woodruff, Richard @ 2006-03-06 16:10 UTC (permalink / raw)
  To: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 877 bytes --]

This patch fixes up some aspects of the git timer-gp code and peripheral
synchronization. 

For timer-gp.c:
- add GPT enables.
	-- shouldn't assume GPT I&F clk are enabled
- add setting of GPT2 source to sysclk.
	-- shouldn't assumed sys_clk is source, 32K is default.
	-- set_parent -> recalc sys_clk -> gpt rate correct. 
- move first timer access past clock enable.
	-- access will abort if clock off.
- sys_clk is always on, no need for enable

For irq.c:
	- add write barriers to synchronize posted ARM peripheral port
accesses with ARM pipeline and ARM memory port.
	-- ARM pipe and memory port are synchronized by default, p-port
is not.


Note:
	- Strange that the timer offset function was dropped in porting
over from TI code.  Compensating for latency and giving higher
resolution time stamps seems like a good thing.

Regards,
Richard W.

[-- Attachment #2: timer_irqsync.diff --]
[-- Type: application/octet-stream, Size: 2706 bytes --]

diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
 #define INTC_MIR_CLEAR0	0x0088
 #define INTC_MIR_SET0	0x008c
 
+#define sync_p_port_write()	\
+	({ int dv = 0;	\
+		__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv));	\
+	})
+
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
 static void omap_ack_irq(unsigned int irq)
 {
 	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+	sync_p_port_write();
 }
 
 static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..6231635 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -93,28 +93,34 @@ static struct irqaction omap2_gp_timer_i
 
 static void __init omap2_gp_timer_init(void)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
-	u32 l;
+	struct clk *sys_ck, *gpt_fck, *gpt_ick;
+	u32 l, tick_period = 120000;
 
-	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	sys_ck  = clk_get(NULL, "sys_ck");
+	gpt_fck = clk_get(NULL, "gpt2_fck");
+	gpt_ick = clk_get(NULL, "gpt2_ick");
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
+	if (IS_ERR(sys_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick))
+		printk(KERN_ERR "Serious problem starting GPT2 \n");
 	else {
-		clk_enable(sys_ck);
+		clk_set_parent(gpt_fck, sys_ck);
+		clk_enable(gpt_fck);
+		clk_enable(gpt_ick);
 		tick_period = clk_get_rate(sys_ck) / 100;
 		clk_put(sys_ck);
+		clk_put(gpt_fck);
+		clk_put(gpt_ick);
 	}
 
+	/* Reset clock and prescale value */
+	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+
 	tick_period /= 2;	/* Minimum prescale divider is 2 */
 	tick_period -= 1;
 
 	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
+	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", 
+		(l >> 4) & 0x0f, l & 0x0f);
 
 	setup_irq(38, &omap2_gp_timer_irq);

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP2: timer+irq fix up
  2006-03-06 16:10 [PATCH] OMAP2: timer+irq fix up Woodruff, Richard
@ 2006-03-06 21:59 ` Tony Lindgren
  0 siblings, 0 replies; 6+ messages in thread
From: Tony Lindgren @ 2006-03-06 21:59 UTC (permalink / raw)
  To: Woodruff, Richard; +Cc: linux-omap-open-source

* Woodruff, Richard <r-woodruff2@ti.com> [060306 08:10]:
> 
> Note:
> 	- Strange that the timer offset function was dropped in porting
> over from TI code.  Compensating for latency and giving higher
> resolution time stamps seems like a good thing.

Yeah the gettimeoffset function should be there. We could also use
the 32KHz sync timer for gettimeoffset as done in timer32k.c, might be
worth doing some testing to see if that accuracy is enough.

Regards,

Tony

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH] OMAP2: timer+irq fix up
@ 2006-03-07  1:08 Woodruff, Richard
  0 siblings, 0 replies; 6+ messages in thread
From: Woodruff, Richard @ 2006-03-07  1:08 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 1701 bytes --]

Hi Tony,

Well, how about this than.  It pretty much does everything and seems to
test ok and values look reasonable at hardware probes.

For irq.c:
- add write barriers to synchronize posted ARM peripheral port accesses
with ARM pipeline and ARM memory port.
	-- ARM pipe and memory port are synchronized by default, p-port
is not.

For timer-gp.c:
- add GPT enables.
	-- shouldn't assume GPT I&F clk are enabled
- add setting of GPT2 source to sysclk.
	-- shouldn't assumed sys_clk is source, 32K is default.
	-- set_parent -> recalc sys_ck -> gpt rate correct. 
- move first timer access past clock enable.
	-- access will abort if clock off.
- sys_ck is always on, no need for enable
- implement sched_clock
- implement gettimeoffset
- make generic for any gptimer usage

For clock.h
- fix gptimer2 enable bit.

For timex.h
- fix CLOCK_TICK_RATE to behave like it does for the rest of the ARMs.

Regards,
Richard W.

> -----Original Message-----
> From: Tony Lindgren [mailto:tony@atomide.com]
> Sent: Monday, March 06, 2006 4:00 PM
> To: Woodruff, Richard
> Cc: linux-omap-open-source@linux.omap.com
> Subject: Re: [PATCH] OMAP2: timer+irq fix up
> 
> * Woodruff, Richard <r-woodruff2@ti.com> [060306 08:10]:
> >
> > Note:
> > 	- Strange that the timer offset function was dropped in porting
> > over from TI code.  Compensating for latency and giving higher
> > resolution time stamps seems like a good thing.
> 
> Yeah the gettimeoffset function should be there. We could also use
> the 32KHz sync timer for gettimeoffset as done in timer32k.c, might be
> worth doing some testing to see if that accuracy is enough.
> 
> Regards,
> 
> Tony

[-- Attachment #2: timer.diff --]
[-- Type: application/octet-stream, Size: 9878 bytes --]

diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
 	.parent		= &l4_ck,
 	.flags		= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
 	.enable_reg	= (void __iomem *)&CM_ICLKEN1_CORE,	/* Bit4 */
-	.enable_bit	= 0,
+	.enable_bit	= 4,
 	.recalc		= &omap2_followparent_recalc,
 };
 
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
 #define INTC_MIR_CLEAR0	0x0088
 #define INTC_MIR_SET0	0x008c
 
+#define sync_p_port_write()	\
+	({ int dv = 0;	\
+		__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv));	\
+	})
+
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
 static void omap_ack_irq(unsigned int irq)
 {
 	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+	sync_p_port_write();
 }
 
 static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..bd40fe7 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
  *         Juha Yrjölä <juha.yrjola@nokia.com>
  *
  * Some parts based off of TI's 24xx code:
- *
- *   Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
  *
  * Roughly modelled after the OMAP1 MPU timer code.
  *
@@ -40,7 +39,17 @@
 #define GP_TIMER_TLDR		0x2c
 #define GP_TIMER_TSICR		0x40
 
+/* system ticker */
 #define OS_TIMER_NR		1  /* GP timer 2 */
+#define OS_TIMER_NR_IRQ		INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF		2  /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ	INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK		"sys_ck"
+
+#define TIMER_ON		1
+#define TIMER_OFF		0
 
 static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +58,12 @@ static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
 };
 
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last;	/* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+unsigned long long ns_per_overflow;
+unsigned long long ov_elapsed_ns = 0;
+
 static inline unsigned int timer_read_reg(int nr, unsigned int reg)
 {
 	return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +75,7 @@ static inline void timer_write_reg(int n
 }
 
 /* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
 {
 	unsigned int tmp;
 
@@ -68,13 +83,27 @@ static inline void omap2_gp_timer_start(
 
 	timer_write_reg(nr, GP_TIMER_TLDR, tmp);
 	timer_write_reg(nr, GP_TIMER_TCRR, tmp);
-	timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+	timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
 	timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
 }
 
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 					    struct pt_regs *regs)
 {
+	u32 now, latency;
+
+	/* get current tick time stamp */
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	/* get ticks since autoreload from ffffffff to ffffxyz */
+	latency = 0xfffffff - timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR);
+	/* adjust for latency, overflow works out with u32 size */
+	omap_mpu_timer_last = now - latency;
+
 	write_seqlock(&xtime_lock);
 
 	timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +114,140 @@ static irqreturn_t omap2_gp_timer_interr
 	return IRQ_HANDLED;
 }
 
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off, 
+				char *n_src)
+{
+	struct clk *src_ck, *gpt_fck, *gpt_ick;
+	char n_fck[16], n_ick[16];
+
+	++nr;
+	snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+	snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+	src_ck  = clk_get(NULL, n_src);
+	gpt_fck = clk_get(NULL, n_fck);
+	gpt_ick = clk_get(NULL, n_ick);
+
+	if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+		printk(KERN_ERR "Serious problem starting system GPT timer \n");
+		return 0;
+	}
+	if(on_off){
+		clk_set_parent(gpt_fck, src_ck);
+		clk_enable(gpt_fck);
+		clk_enable(gpt_ick);
+		if(rate != NULL)
+			*rate = clk_get_rate(src_ck);
+	} else {
+		clk_disable(gpt_fck);
+		clk_disable(gpt_ick);
+	}
+	clk_put(src_ck);
+	clk_put(gpt_fck);
+	clk_put(gpt_ick);
+	return 1;
+}
+
 static struct irqaction omap2_gp_timer_irq = {
 	.name		= "gp timer",
 	.flags		= SA_INTERRUPT,
 	.handler	= omap2_gp_timer_interrupt,
 };
 
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in 
+ * the MHz range.  The usual source will be sys_ck divided by 2
+ */
+static void omap2_gp_timer_init(int nr)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
+	unsigned long tick_period, gprate;
 	u32 l;
 
+	/* enable timer clocks & set parent to sys_ck */
+	omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+	gprate /= 2;	/* Min timer prescale divider is 2 */
+	gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+	tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+	tick_period -= 1;
+
 	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+	l = timer_read_reg(nr, GP_TIMER_TIDR);
+	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", 
+		(l >> 4) & 0x0f, l & 0x0f);
+
+	setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+	omap2_gp_timer_start(nr, tick_period, 1);
+}
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
-	else {
-		clk_enable(sys_ck);
-		tick_period = clk_get_rate(sys_ck) / 100;
-		clk_put(sys_ck);
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+					     struct pt_regs *regs)
+{
+	ov_elapsed_ns +=  ns_per_overflow;
+	timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+	.name		= "gp timer overflow",
+	.flags		= SA_INTERRUPT,
+	.handler	= omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+	if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+		timer_write_reg(nr, GP_TIMER_TCLR, 0);
+		omap2_gp_timer_start(nr, 0xffffffff, 1);
+		setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
 	}
+}
 
-	tick_period /= 2;	/* Minimum prescale divider is 2 */
-	tick_period -= 1;
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+	/* round up to nearest usec*/
+	return ((gpticks / (gpticks_per_msec / 1000)) + 1) >> 1;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+	u32 elapsed_cycles, elapsed_usec;
+	
+	elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);	
+	return (ov_elapsed_ns + elapsed_usec * 1000);
+}
 
-	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+	u32 now, elapsed_usec, elapsed_cycles;
 
-	setup_irq(38, &omap2_gp_timer_irq);
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_cycles = now - omap_mpu_timer_last;
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+	return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+	u32 gpticks_per_ns;
 
-	omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+	omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+	omap2_gp_timer_init(OS_TIMER_NR);
+	gpticks_per_ns = gpticks_per_msec / 1000000;
+	omap_timer.offset = omap2_gettimeoffset;
+	ns_per_overflow = 0xffffffff * gpticks_per_ns;
 }
 
 struct sys_timer omap_timer = {
-	.init	= omap2_gp_timer_init,
+	.init	= omap2_timer_init,
+	.offset = NULL,
 };
 
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
 #define INT_24XX_GPIO_BANK2	30
 #define INT_24XX_GPIO_BANK3	31
 #define INT_24XX_GPIO_BANK4	32
+#define INT_24XX_GPTIMER2	38
+#define INT_24XX_GPTIMER3	39
 #define INT_24XX_MCBSP1_IRQ_TX	59
 #define INT_24XX_MCBSP1_IRQ_RX	60
 #define INT_24XX_MCBSP2_IRQ_TX	62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
  */
 #ifdef CONFIG_OMAP_32K_TIMER
 #define CLOCK_TICK_RATE		(CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE 	(6000000)	/* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE 	(6500000)
 #else
-#define CLOCK_TICK_RATE		(HZ * 100000UL)
+#define CLOCK_TICK_RATE		(6000000)
 #endif
 
 #endif /* __ASM_ARCH_OMAP_TIMEX_H */

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: [PATCH] OMAP2: timer+irq fix up
@ 2006-03-07  1:39 Woodruff, Richard
  0 siblings, 0 replies; 6+ messages in thread
From: Woodruff, Richard @ 2006-03-07  1:39 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 2087 bytes --]

This is the one, the usec conversion was wrong in the other.  This
allows lmbench to gauge granularity to sub-jiffy and has a meaningful
granularity measurement.

Lmbench can now guess the frequency in a couple of seconds instead of
297 seconds.

Regards,
Richard W.


> Hi Tony,
> 
> Well, how about this than.  It pretty much does everything and seems
to
> test ok and values look reasonable at hardware probes.
> 
> For irq.c:
> - add write barriers to synchronize posted ARM peripheral port
accesses
> with ARM pipeline and ARM memory port.
> 	-- ARM pipe and memory port are synchronized by default, p-port
> is not.
> 
> For timer-gp.c:
> - add GPT enables.
> 	-- shouldn't assume GPT I&F clk are enabled
> - add setting of GPT2 source to sysclk.
> 	-- shouldn't assumed sys_clk is source, 32K is default.
> 	-- set_parent -> recalc sys_ck -> gpt rate correct.
> - move first timer access past clock enable.
> 	-- access will abort if clock off.
> - sys_ck is always on, no need for enable
> - implement sched_clock
> - implement gettimeoffset
> - make generic for any gptimer usage
> 
> For clock.h
> - fix gptimer2 enable bit.
> 
> For timex.h
> - fix CLOCK_TICK_RATE to behave like it does for the rest of the ARMs.
> 
> Regards,
> Richard W.
> 
> > -----Original Message-----
> > From: Tony Lindgren [mailto:tony@atomide.com]
> > Sent: Monday, March 06, 2006 4:00 PM
> > To: Woodruff, Richard
> > Cc: linux-omap-open-source@linux.omap.com
> > Subject: Re: [PATCH] OMAP2: timer+irq fix up
> >
> > * Woodruff, Richard <r-woodruff2@ti.com> [060306 08:10]:
> > >
> > > Note:
> > > 	- Strange that the timer offset function was dropped in porting
> > > over from TI code.  Compensating for latency and giving higher
> > > resolution time stamps seems like a good thing.
> >
> > Yeah the gettimeoffset function should be there. We could also use
> > the 32KHz sync timer for gettimeoffset as done in timer32k.c, might
be
> > worth doing some testing to see if that accuracy is enough.
> >
> > Regards,
> >
> > Tony

[-- Attachment #2: timer.diff --]
[-- Type: application/octet-stream, Size: 9849 bytes --]

diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
 	.parent		= &l4_ck,
 	.flags		= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
 	.enable_reg	= (void __iomem *)&CM_ICLKEN1_CORE,	/* Bit4 */
-	.enable_bit	= 0,
+	.enable_bit	= 4,
 	.recalc		= &omap2_followparent_recalc,
 };
 
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
 #define INTC_MIR_CLEAR0	0x0088
 #define INTC_MIR_SET0	0x008c
 
+#define sync_p_port_write()	\
+	({ int dv = 0;	\
+		__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv));	\
+	})
+
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
 static void omap_ack_irq(unsigned int irq)
 {
 	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+	sync_p_port_write();
 }
 
 static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..98e9358 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
  *         Juha Yrjölä <juha.yrjola@nokia.com>
  *
  * Some parts based off of TI's 24xx code:
- *
- *   Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
  *
  * Roughly modelled after the OMAP1 MPU timer code.
  *
@@ -40,7 +39,17 @@
 #define GP_TIMER_TLDR		0x2c
 #define GP_TIMER_TSICR		0x40
 
+/* system ticker */
 #define OS_TIMER_NR		1  /* GP timer 2 */
+#define OS_TIMER_NR_IRQ		INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF		2  /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ	INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK		"sys_ck"
+
+#define TIMER_ON		1
+#define TIMER_OFF		0
 
 static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +58,12 @@ static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
 };
 
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last;	/* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+unsigned long long ns_per_overflow;
+unsigned long long ov_elapsed_ns = 0;
+
 static inline unsigned int timer_read_reg(int nr, unsigned int reg)
 {
 	return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +75,7 @@ static inline void timer_write_reg(int n
 }
 
 /* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
 {
 	unsigned int tmp;
 
@@ -68,13 +83,27 @@ static inline void omap2_gp_timer_start(
 
 	timer_write_reg(nr, GP_TIMER_TLDR, tmp);
 	timer_write_reg(nr, GP_TIMER_TCRR, tmp);
-	timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+	timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
 	timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
 }
 
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 					    struct pt_regs *regs)
 {
+	u32 now, latency;
+
+	/* get current tick time stamp */
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	/* get ticks since autoreload from ffffffff to ffffxyz */
+	latency = 0xfffffff - timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR);
+	/* adjust for latency, overflow works out with u32 size */
+	omap_mpu_timer_last = now - latency;
+
 	write_seqlock(&xtime_lock);
 
 	timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +114,139 @@ static irqreturn_t omap2_gp_timer_interr
 	return IRQ_HANDLED;
 }
 
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off, 
+				char *n_src)
+{
+	struct clk *src_ck, *gpt_fck, *gpt_ick;
+	char n_fck[16], n_ick[16];
+
+	++nr;
+	snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+	snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+	src_ck  = clk_get(NULL, n_src);
+	gpt_fck = clk_get(NULL, n_fck);
+	gpt_ick = clk_get(NULL, n_ick);
+
+	if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+		printk(KERN_ERR "Serious problem starting system GPT timer \n");
+		return 0;
+	}
+	if(on_off){
+		clk_set_parent(gpt_fck, src_ck);
+		clk_enable(gpt_fck);
+		clk_enable(gpt_ick);
+		if(rate != NULL)
+			*rate = clk_get_rate(src_ck);
+	} else {
+		clk_disable(gpt_fck);
+		clk_disable(gpt_ick);
+	}
+	clk_put(src_ck);
+	clk_put(gpt_fck);
+	clk_put(gpt_ick);
+	return 1;
+}
+
 static struct irqaction omap2_gp_timer_irq = {
 	.name		= "gp timer",
 	.flags		= SA_INTERRUPT,
 	.handler	= omap2_gp_timer_interrupt,
 };
 
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in 
+ * the MHz range.  The usual source will be sys_ck divided by 2
+ */
+static void omap2_gp_timer_init(int nr)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
+	unsigned long tick_period, gprate;
 	u32 l;
 
+	/* enable timer clocks & set parent to sys_ck */
+	omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+	gprate /= 2;	/* Min timer prescale divider is 2 */
+	gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+	tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+	tick_period -= 1;
+
 	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+	l = timer_read_reg(nr, GP_TIMER_TIDR);
+	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", 
+		(l >> 4) & 0x0f, l & 0x0f);
+
+	setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+	omap2_gp_timer_start(nr, tick_period, 1);
+}
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
-	else {
-		clk_enable(sys_ck);
-		tick_period = clk_get_rate(sys_ck) / 100;
-		clk_put(sys_ck);
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+					     struct pt_regs *regs)
+{
+	ov_elapsed_ns +=  ns_per_overflow;
+	timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+	.name		= "gp timer overflow",
+	.flags		= SA_INTERRUPT,
+	.handler	= omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+	if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+		timer_write_reg(nr, GP_TIMER_TCLR, 0);
+		omap2_gp_timer_start(nr, 0xffffffff, 1);
+		setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
 	}
+}
 
-	tick_period /= 2;	/* Minimum prescale divider is 2 */
-	tick_period -= 1;
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+	return (((gpticks * 2 * 1000)/ gpticks_per_msec) + 1) >> 1;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+	u32 elapsed_cycles, elapsed_usec;
+	
+	elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);	
+	return (ov_elapsed_ns + elapsed_usec * 1000);
+}
 
-	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+	u32 now, elapsed_usec, elapsed_cycles;
 
-	setup_irq(38, &omap2_gp_timer_irq);
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_cycles = now - omap_mpu_timer_last;
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+	return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+	u32 gpticks_per_ns;
 
-	omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+	omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+	omap2_gp_timer_init(OS_TIMER_NR);
+	gpticks_per_ns = gpticks_per_msec / 1000000;
+	omap_timer.offset = omap2_gettimeoffset;
+	ns_per_overflow = 0xffffffff * gpticks_per_ns;
 }
 
 struct sys_timer omap_timer = {
-	.init	= omap2_gp_timer_init,
+	.init	= omap2_timer_init,
+	.offset = NULL,
 };
 
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
 #define INT_24XX_GPIO_BANK2	30
 #define INT_24XX_GPIO_BANK3	31
 #define INT_24XX_GPIO_BANK4	32
+#define INT_24XX_GPTIMER2	38
+#define INT_24XX_GPTIMER3	39
 #define INT_24XX_MCBSP1_IRQ_TX	59
 #define INT_24XX_MCBSP1_IRQ_RX	60
 #define INT_24XX_MCBSP2_IRQ_TX	62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
  */
 #ifdef CONFIG_OMAP_32K_TIMER
 #define CLOCK_TICK_RATE		(CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE 	(6000000)	/* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE 	(6500000)
 #else
-#define CLOCK_TICK_RATE		(HZ * 100000UL)
+#define CLOCK_TICK_RATE		(6000000)
 #endif
 
 #endif /* __ASM_ARCH_OMAP_TIMEX_H */

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: [PATCH] OMAP2: timer+irq fix up
@ 2006-03-07 15:14 Woodruff, Richard
  0 siblings, 0 replies; 6+ messages in thread
From: Woodruff, Richard @ 2006-03-07 15:14 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 1152 bytes --]

Here is one last fix for latency calculations.

Regards,
Richard W.

> > Hi Tony,
> >
> > Well, how about this than.  It pretty much does everything and seems
to
> > test ok and values look reasonable at hardware probes.
> >
> > For irq.c:
> > - add write barriers to synchronize posted ARM peripheral port
accesses
> > with ARM pipeline and ARM memory port.
> > 	-- ARM pipe and memory port are synchronized by default, p-port
> > is not.
> >
> > For timer-gp.c:
> > - add GPT enables.
> > 	-- shouldn't assume GPT I&F clk are enabled
> > - add setting of GPT2 source to sysclk.
> > 	-- shouldn't assumed sys_clk is source, 32K is default.
> > 	-- set_parent -> recalc sys_ck -> gpt rate correct.
> > - move first timer access past clock enable.
> > 	-- access will abort if clock off.
> > - sys_ck is always on, no need for enable
> > - implement sched_clock
> > - implement gettimeoffset
> > - make generic for any gptimer usage
> >
> > For clock.h
> > - fix gptimer2 enable bit.
> >
> > For timex.h
> > - fix CLOCK_TICK_RATE to behave like it does for the rest of the
ARMs.
> >
> > Regards,
> > Richard W.


[-- Attachment #2: timer3.diff --]
[-- Type: application/octet-stream, Size: 9885 bytes --]

diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
 	.parent		= &l4_ck,
 	.flags		= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
 	.enable_reg	= (void __iomem *)&CM_ICLKEN1_CORE,	/* Bit4 */
-	.enable_bit	= 0,
+	.enable_bit	= 4,
 	.recalc		= &omap2_followparent_recalc,
 };
 
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
 #define INTC_MIR_CLEAR0	0x0088
 #define INTC_MIR_SET0	0x008c
 
+#define sync_p_port_write()	\
+	({ int dv = 0;	\
+		__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv));	\
+	})
+
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
 static void omap_ack_irq(unsigned int irq)
 {
 	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+	sync_p_port_write();
 }
 
 static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..e74836d 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
  *         Juha Yrjölä <juha.yrjola@nokia.com>
  *
  * Some parts based off of TI's 24xx code:
- *
- *   Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
  *
  * Roughly modelled after the OMAP1 MPU timer code.
  *
@@ -40,7 +39,17 @@
 #define GP_TIMER_TLDR		0x2c
 #define GP_TIMER_TSICR		0x40
 
+/* system ticker */
 #define OS_TIMER_NR		1  /* GP timer 2 */
+#define OS_TIMER_NR_IRQ		INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF		2  /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ	INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK		"sys_ck"
+
+#define TIMER_ON		1
+#define TIMER_OFF		0
 
 static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +58,12 @@ static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
 };
 
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last;	/* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+unsigned long long ns_per_overflow;
+unsigned long long ov_elapsed_ns = 0;
+
 static inline unsigned int timer_read_reg(int nr, unsigned int reg)
 {
 	return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +75,7 @@ static inline void timer_write_reg(int n
 }
 
 /* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
 {
 	unsigned int tmp;
 
@@ -68,13 +83,28 @@ static inline void omap2_gp_timer_start(
 
 	timer_write_reg(nr, GP_TIMER_TLDR, tmp);
 	timer_write_reg(nr, GP_TIMER_TCRR, tmp);
-	timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+	timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
 	timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
 }
 
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 					    struct pt_regs *regs)
 {
+	u32 now, latency;
+
+	/* get current tick time stamp */
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	/* get ticks since autoreload from ffffffff to ffffxyz */
+	latency = timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR) - 
+		(0xffffffff - gpticks_per_msec * 1000/HZ);
+	/* adjust for latency, overflow works out with u32 size */
+	omap_mpu_timer_last = now - latency;
+
 	write_seqlock(&xtime_lock);
 
 	timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +115,139 @@ static irqreturn_t omap2_gp_timer_interr
 	return IRQ_HANDLED;
 }
 
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off, 
+				char *n_src)
+{
+	struct clk *src_ck, *gpt_fck, *gpt_ick;
+	char n_fck[16], n_ick[16];
+
+	++nr;
+	snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+	snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+	src_ck  = clk_get(NULL, n_src);
+	gpt_fck = clk_get(NULL, n_fck);
+	gpt_ick = clk_get(NULL, n_ick);
+
+	if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+		printk(KERN_ERR "Serious problem starting system GPT timer \n");
+		return 0;
+	}
+	if(on_off){
+		clk_set_parent(gpt_fck, src_ck);
+		clk_enable(gpt_fck);
+		clk_enable(gpt_ick);
+		if(rate != NULL)
+			*rate = clk_get_rate(src_ck);
+	} else {
+		clk_disable(gpt_fck);
+		clk_disable(gpt_ick);
+	}
+	clk_put(src_ck);
+	clk_put(gpt_fck);
+	clk_put(gpt_ick);
+	return 1;
+}
+
 static struct irqaction omap2_gp_timer_irq = {
 	.name		= "gp timer",
 	.flags		= SA_INTERRUPT,
 	.handler	= omap2_gp_timer_interrupt,
 };
 
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in 
+ * the MHz range.  The usual source will be sys_ck divided by 2
+ */
+static void omap2_gp_timer_init(int nr)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
+	unsigned long tick_period, gprate;
 	u32 l;
 
+	/* enable timer clocks & set parent to sys_ck */
+	omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+	gprate /= 2;	/* Min timer prescale divider is 2 */
+	gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+	tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+	tick_period -= 1;
+
 	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+	l = timer_read_reg(nr, GP_TIMER_TIDR);
+	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", 
+		(l >> 4) & 0x0f, l & 0x0f);
+
+	setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+	omap2_gp_timer_start(nr, tick_period, 1);
+}
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
-	else {
-		clk_enable(sys_ck);
-		tick_period = clk_get_rate(sys_ck) / 100;
-		clk_put(sys_ck);
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+					     struct pt_regs *regs)
+{
+	ov_elapsed_ns +=  ns_per_overflow;
+	timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+	.name		= "gp timer overflow",
+	.flags		= SA_INTERRUPT,
+	.handler	= omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+	if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+		timer_write_reg(nr, GP_TIMER_TCLR, 0);
+		omap2_gp_timer_start(nr, 0xffffffff, 1);
+		setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
 	}
+}
 
-	tick_period /= 2;	/* Minimum prescale divider is 2 */
-	tick_period -= 1;
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+	return (((gpticks * 2 * 1000)/ gpticks_per_msec) + 1) >> 1;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+	u32 elapsed_cycles, elapsed_usec;
+	
+	elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);	
+	return (ov_elapsed_ns + elapsed_usec * 1000);
+}
 
-	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+	u32 now, elapsed_usec, elapsed_cycles;
 
-	setup_irq(38, &omap2_gp_timer_irq);
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_cycles = now - omap_mpu_timer_last;
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+	return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+	u32 gpticks_per_ns;
 
-	omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+	omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+	omap2_gp_timer_init(OS_TIMER_NR);
+	gpticks_per_ns = gpticks_per_msec / 1000000;
+	omap_timer.offset = omap2_gettimeoffset;
+	ns_per_overflow = 0xffffffff * gpticks_per_ns;
 }
 
 struct sys_timer omap_timer = {
-	.init	= omap2_gp_timer_init,
+	.init	= omap2_timer_init,
+	.offset = NULL,
 };
 
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
 #define INT_24XX_GPIO_BANK2	30
 #define INT_24XX_GPIO_BANK3	31
 #define INT_24XX_GPIO_BANK4	32
+#define INT_24XX_GPTIMER2	38
+#define INT_24XX_GPTIMER3	39
 #define INT_24XX_MCBSP1_IRQ_TX	59
 #define INT_24XX_MCBSP1_IRQ_RX	60
 #define INT_24XX_MCBSP2_IRQ_TX	62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
  */
 #ifdef CONFIG_OMAP_32K_TIMER
 #define CLOCK_TICK_RATE		(CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE 	(6000000)	/* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE 	(6500000)
 #else
-#define CLOCK_TICK_RATE		(HZ * 100000UL)
+#define CLOCK_TICK_RATE		(6000000)
 #endif
 
 #endif /* __ASM_ARCH_OMAP_TIMEX_H */

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: [PATCH] OMAP2: timer+irq fix up
@ 2006-03-08  3:02 Woodruff, Richard
  0 siblings, 0 replies; 6+ messages in thread
From: Woodruff, Richard @ 2006-03-08  3:02 UTC (permalink / raw)
  To: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 1164 bytes --]

Hopefully, fix sched_clock ns value.

> > > Well, how about this than.  It pretty much does everything and
seems
> to
> > > test ok and values look reasonable at hardware probes.
> > >
> > > For irq.c:
> > > - add write barriers to synchronize posted ARM peripheral port
> accesses
> > > with ARM pipeline and ARM memory port.
> > > 	-- ARM pipe and memory port are synchronized by default, p-port
> > > is not.
> > >
> > > For timer-gp.c:
> > > - add GPT enables.
> > > 	-- shouldn't assume GPT I&F clk are enabled
> > > - add setting of GPT2 source to sysclk.
> > > 	-- shouldn't assumed sys_clk is source, 32K is default.
> > > 	-- set_parent -> recalc sys_ck -> gpt rate correct.
> > > - move first timer access past clock enable.
> > > 	-- access will abort if clock off.
> > > - sys_ck is always on, no need for enable
> > > - implement sched_clock
> > > - implement gettimeoffset
> > > - make generic for any gptimer usage
> > >
> > > For clock.h
> > > - fix gptimer2 enable bit.
> > >
> > > For timex.h
> > > - fix CLOCK_TICK_RATE to behave like it does for the rest of the
ARMs.
> > >
> > > Regards,
> > > Richard W.


[-- Attachment #2: timer5.diff --]
[-- Type: application/octet-stream, Size: 10357 bytes --]

diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
 	.parent		= &l4_ck,
 	.flags		= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
 	.enable_reg	= (void __iomem *)&CM_ICLKEN1_CORE,	/* Bit4 */
-	.enable_bit	= 0,
+	.enable_bit	= 4,
 	.recalc		= &omap2_followparent_recalc,
 };
 
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
 #define INTC_MIR_CLEAR0	0x0088
 #define INTC_MIR_SET0	0x008c
 
+#define sync_p_port_write()	\
+	({ int dv = 0;	\
+		__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv));	\
+	})
+
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
 static void omap_ack_irq(unsigned int irq)
 {
 	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+	sync_p_port_write();
 }
 
 static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
 	}
 
 	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+	sync_p_port_write();
 }
 
 static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..2a2b550 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
  *         Juha Yrjölä <juha.yrjola@nokia.com>
  *
  * Some parts based off of TI's 24xx code:
- *
- *   Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
  *
  * Roughly modelled after the OMAP1 MPU timer code.
  *
@@ -40,7 +39,17 @@
 #define GP_TIMER_TLDR		0x2c
 #define GP_TIMER_TSICR		0x40
 
+/* system ticker */
 #define OS_TIMER_NR		1  /* GP timer 2 */
+#define OS_TIMER_NR_IRQ		INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF		2  /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ	INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK		"sys_ck"
+
+#define TIMER_ON		1
+#define TIMER_OFF		0
 
 static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +58,12 @@ static unsigned long timer_base[] = {
 	IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
 };
 
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last;	/* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+unsigned long long num_jiffies_in_ov_in_ns; /* # jiffies in 0xffffffff ticks */
+unsigned long long tot_jiffies_in_ns = 0;  /* total jiffies in ns */
+
 static inline unsigned int timer_read_reg(int nr, unsigned int reg)
 {
 	return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +75,7 @@ static inline void timer_write_reg(int n
 }
 
 /* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
 {
 	unsigned int tmp;
 
@@ -68,13 +83,28 @@ static inline void omap2_gp_timer_start(
 
 	timer_write_reg(nr, GP_TIMER_TLDR, tmp);
 	timer_write_reg(nr, GP_TIMER_TCRR, tmp);
-	timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+	timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
 	timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
 }
 
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
 					    struct pt_regs *regs)
 {
+	u32 now, latency;
+
+	/* get current tick time stamp */
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	/* get ticks since autoreload from ffffffff to ffffxyz */
+	latency = timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR) - 
+		(0xffffffff - gpticks_per_msec * 1000/HZ);
+	/* adjust for latency, overflow works out with u32 size */
+	omap_mpu_timer_last = now - latency;
+
 	write_seqlock(&xtime_lock);
 
 	timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +115,149 @@ static irqreturn_t omap2_gp_timer_interr
 	return IRQ_HANDLED;
 }
 
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off, 
+				char *n_src)
+{
+	struct clk *src_ck, *gpt_fck, *gpt_ick;
+	char n_fck[16], n_ick[16];
+
+	++nr;
+	snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+	snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+	src_ck  = clk_get(NULL, n_src);
+	gpt_fck = clk_get(NULL, n_fck);
+	gpt_ick = clk_get(NULL, n_ick);
+
+	if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+		printk(KERN_ERR "Serious problem starting system GPT timer \n");
+		return 0;
+	}
+	if(on_off){
+		clk_set_parent(gpt_fck, src_ck);
+		clk_enable(gpt_fck);
+		clk_enable(gpt_ick);
+		if(rate != NULL)
+			*rate = clk_get_rate(src_ck);
+	} else {
+		clk_disable(gpt_fck);
+		clk_disable(gpt_ick);
+	}
+	clk_put(src_ck);
+	clk_put(gpt_fck);
+	clk_put(gpt_ick);
+	return 1;
+}
+
 static struct irqaction omap2_gp_timer_irq = {
 	.name		= "gp timer",
 	.flags		= SA_INTERRUPT,
 	.handler	= omap2_gp_timer_interrupt,
 };
 
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in 
+ * the MHz range.  The usual source will be sys_ck divided by 2
+ */
+static u32 omap2_gp_timer_init(int nr)
 {
-	struct clk * sys_ck;
-	u32 tick_period = 120000;
+	unsigned long tick_period, gprate;
 	u32 l;
 
+	/* enable timer clocks & set parent to sys_ck */
+	omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+	gprate /= 2;	/* Min timer prescale divider is 2 */
+	gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+	tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+	tick_period -= 1;
+
 	/* Reset clock and prescale value */
-	timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+	timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+	l = timer_read_reg(nr, GP_TIMER_TIDR);
+	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n", 
+		(l >> 4) & 0x0f, l & 0x0f);
+
+	setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+	omap2_gp_timer_start(nr, tick_period, 1);
+
+	return gprate;
+}
 
-	sys_ck = clk_get(NULL, "sys_ck");
-	if (IS_ERR(sys_ck))
-		printk(KERN_ERR "Could not get sys_ck\n");
-	else {
-		clk_enable(sys_ck);
-		tick_period = clk_get_rate(sys_ck) / 100;
-		clk_put(sys_ck);
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+					     struct pt_regs *regs)
+{
+	tot_jiffies_in_ns +=  num_jiffies_in_ov_in_ns;
+	timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+	.name		= "gp timer overflow",
+	.flags		= SA_INTERRUPT,
+	.handler	= omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+	if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+		timer_write_reg(nr, GP_TIMER_TCLR, 0);
+		omap2_gp_timer_start(nr, 0xffffffff, 1);
+		setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
 	}
+}
 
-	tick_period /= 2;	/* Minimum prescale divider is 2 */
-	tick_period -= 1;
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+	return (((gpticks * 2 * 1000)/ gpticks_per_msec) + 1) >> 1;
+}
+
+/*
+ * Scheduler clock - returns total jiffies expressed in nanosec.
+ */
+unsigned long long sched_clock(void)
+{
+	u32 elapsed_cycles, elapsed_ns;
+
+	elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_ns = 1000 * machinecycles_to_usecs(elapsed_cycles);		
+	return (tot_jiffies_in_ns + elapsed_ns);
+}
 
-	l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
-	printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
-	       (l >> 4) & 0x0f, l & 0x0f);
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+	u32 now, elapsed_usec, elapsed_cycles;
+
+	now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+	elapsed_cycles = now - omap_mpu_timer_last;
+	elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+	return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+	u32 gprate;
+	u32 num_ns_in_jiffy;
 
-	setup_irq(38, &omap2_gp_timer_irq);
+	omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+	gprate = omap2_gp_timer_init(OS_TIMER_NR);
 
-	omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+	/* 0xffffffff * 1/gprate = seconds in overflow = siof
+	 * siof * HZ (jiffies in a second) = num of jiffies in overflow = njiof
+	 * njiof * ns in a jiffy (1000000000/HZ) = num_jiffies_in_ov_in_ns
+	 */
+	num_jiffies_in_ov_in_ns = 0xffffffff * HZ;
+	do_div(num_jiffies_in_ov_in_ns, gprate); /* jiffies in 0xffffffff ticks */
+	num_ns_in_jiffy = 1000000000 / HZ; /* num ns in a jiffy */
+	num_jiffies_in_ov_in_ns *= num_ns_in_jiffy;
+	omap_timer.offset = omap2_gettimeoffset;
 }
 
 struct sys_timer omap_timer = {
-	.init	= omap2_gp_timer_init,
+	.init	= omap2_timer_init,
+	.offset = NULL,
 };
 
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
 #define INT_24XX_GPIO_BANK2	30
 #define INT_24XX_GPIO_BANK3	31
 #define INT_24XX_GPIO_BANK4	32
+#define INT_24XX_GPTIMER2	38
+#define INT_24XX_GPTIMER3	39
 #define INT_24XX_MCBSP1_IRQ_TX	59
 #define INT_24XX_MCBSP1_IRQ_RX	60
 #define INT_24XX_MCBSP2_IRQ_TX	62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
  */
 #ifdef CONFIG_OMAP_32K_TIMER
 #define CLOCK_TICK_RATE		(CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE 	(6000000)	/* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE 	(6500000)
 #else
-#define CLOCK_TICK_RATE		(HZ * 100000UL)
+#define CLOCK_TICK_RATE		(6000000)
 #endif
 
 #endif /* __ASM_ARCH_OMAP_TIMEX_H */

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2006-03-08  3:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-03-06 16:10 [PATCH] OMAP2: timer+irq fix up Woodruff, Richard
2006-03-06 21:59 ` Tony Lindgren
  -- strict thread matches above, loose matches on Subject: below --
2006-03-07  1:08 Woodruff, Richard
2006-03-07  1:39 Woodruff, Richard
2006-03-07 15:14 Woodruff, Richard
2006-03-08  3:02 Woodruff, Richard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox