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-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