diff mbox

[U-Boot,IXP42x,series,v4,04/17] Fix IXP code to work after relocation was added

Message ID 1302126558-1318-5-git-send-email-michael@schwingen.org
State Superseded
Delegated to: Marek Vasut
Headers show

Commit Message

Michael Schwingen April 6, 2011, 9:49 p.m. UTC
- jump to real flash location after reset before turning off flash mirror
 - fix timer system to use HZ == 1000, remove broken interrupt-based code

Signed-off-by: Michael Schwingen <michael@schwingen.org>
---
Changes for V2:
 - fix patch description
Changes for V3:
 - use I/O accessors
 - move timestamp variable from BSS to global data
 - coding style fixes
Changes for V4:
 - add changelog

 arch/arm/cpu/ixp/cpu.c                 |    5 --
 arch/arm/cpu/ixp/start.S               |   59 ++--------------
 arch/arm/cpu/ixp/timer.c               |  124 +++++++++++++++-----------------
 arch/arm/include/asm/arch-ixp/ixp425.h |    5 +-
 arch/arm/include/asm/global_data.h     |    3 +
 5 files changed, 68 insertions(+), 128 deletions(-)

Comments

Marek Vasut May 20, 2011, 11:58 p.m. UTC | #1
On Wednesday, April 06, 2011 11:49:05 PM Michael Schwingen wrote:
>  - jump to real flash location after reset before turning off flash mirror
>  - fix timer system to use HZ == 1000, remove broken interrupt-based code
> 
> Signed-off-by: Michael Schwingen <michael@schwingen.org>
> ---
> Changes for V2:
>  - fix patch description
> Changes for V3:
>  - use I/O accessors
>  - move timestamp variable from BSS to global data
>  - coding style fixes
> Changes for V4:
>  - add changelog
> 
>  arch/arm/cpu/ixp/cpu.c                 |    5 --
>  arch/arm/cpu/ixp/start.S               |   59 ++--------------
>  arch/arm/cpu/ixp/timer.c               |  124
> +++++++++++++++----------------- arch/arm/include/asm/arch-ixp/ixp425.h | 
>   5 +-
>  arch/arm/include/asm/global_data.h     |    3 +
>  5 files changed, 68 insertions(+), 128 deletions(-)
> 
> diff --git a/arch/arm/cpu/ixp/cpu.c b/arch/arm/cpu/ixp/cpu.c
> index ce275e5..942845d 100644
> --- a/arch/arm/cpu/ixp/cpu.c
> +++ b/arch/arm/cpu/ixp/cpu.c
> @@ -36,8 +36,6 @@
>  #include <asm/arch/ixp425.h>
>  #include <asm/system.h>
> 
> -ulong loops_per_jiffy;
> -
>  static void cache_flush(void);
> 
>  #if defined(CONFIG_DISPLAY_CPUINFO)
> @@ -51,17 +49,14 @@ int print_cpuinfo (void)
>  	puts("CPU:   Intel IXP425 at ");
>  	switch ((id & 0x000003f0) >> 4) {
>  	case 0x1c:
> -		loops_per_jiffy = 887467;
>  		speed = 533;
>  		break;
> 
>  	case 0x1d:
> -		loops_per_jiffy = 666016;
>  		speed = 400;
>  		break;
> 
>  	case 0x1f:
> -		loops_per_jiffy = 442901;
>  		speed = 266;
>  		break;
>  	}
> diff --git a/arch/arm/cpu/ixp/start.S b/arch/arm/cpu/ixp/start.S
> index 561c1f4..faa9a8f 100644
> --- a/arch/arm/cpu/ixp/start.S
> +++ b/arch/arm/cpu/ixp/start.S
> @@ -65,7 +65,8 @@
>  	.endm
> 
>  .globl _start
> -_start: b	reset
> +_start:
> +	ldr	pc, _reset
>  	ldr	pc, _undefined_instruction
>  	ldr	pc, _software_interrupt
>  	ldr	pc, _prefetch_abort
> @@ -74,6 +75,7 @@ _start: b	reset
>  	ldr	pc, _irq
>  	ldr	pc, _fiq
> 
> +_reset:                 .word reset
>  _undefined_instruction: .word undefined_instruction
>  _software_interrupt:	.word software_interrupt
>  _prefetch_abort:	.word prefetch_abort
> @@ -167,12 +169,6 @@ reset:
>  	str     r1, [r2]
> 
>  	/* make sure flash is visible at 0 */
> -#if 0
> -	ldr	r2, =IXP425_EXP_CFG0
> -	ldr     r1, [r2]
> -	orr     r1, r1, #0x80000000
> -	str     r1, [r2]
> -#endif
>  	mov	r1, #CONFIG_SYS_SDR_CONFIG
>  	ldr     r2, =IXP425_SDR_CONFIG
>  	str     r1, [r2]
> @@ -216,19 +212,6 @@ reset:
>  	str	r1, [r4]
>  	DELAY_FOR 0x4000, r0
> 
> -	/* copy */
> -	mov     r0, #0
> -	mov     r4, r0
> -	add     r2, r0, #CONFIG_SYS_MONITOR_LEN
> -	mov     r1, #0x10000000
> -	mov     r5, r1
> -
> -    30:
> -	ldr     r3, [r0], #4
> -	str     r3, [r1], #4
> -	cmp     r0, r2
> -	bne     30b
> -
>  	/* invalidate I & D caches & BTB */
>  	mcr	p15, 0, r0, c7, c7, 0
>  	CPWAIT	r0
> @@ -241,19 +224,12 @@ reset:
>  	mcr	p15, 0, r0, c7, c10, 4
>  	CPWAIT	r0
> 
> -	/* move flash to 0x50000000 */
> +	/* remove flash mirror at 0x00000000 */
>  	ldr	r2, =IXP425_EXP_CFG0
>  	ldr     r1, [r2]
>  	bic     r1, r1, #0x80000000
>  	str     r1, [r2]
> 
> -	nop
> -	nop
> -	nop
> -	nop
> -	nop
> -	nop
> -
>  	/* invalidate I & Data TLB */
>  	mcr	p15, 0, r0, c8, c7, 0
>  	CPWAIT r0
> @@ -269,7 +245,7 @@ reset:
>  	orr	r0,r0,#0x13
>  	msr	cpsr,r0
> 
> -/* Set stackpointer in internal RAM to call board_init_f */
> +/* Set initial stackpointer in SDRAM to call board_init_f */
>  call_board_init_f:
>  	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
>  	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
> @@ -580,28 +556,3 @@ reset_endless:
> 
>  	b	reset_endless
> 
> -#ifdef CONFIG_USE_IRQ
> -
> -.LC0:		.word	loops_per_jiffy
> -
> -/*
> - * 0 <= r0 <= 2000
> - */
> -.globl __udelay
> -__udelay:
> -	mov	r2,     #0x6800
> -	orr	r2, r2, #0x00db
> -	mul	r0, r2, r0
> -	ldr	r2, .LC0
> -	ldr	r2, [r2]		@ max = 0x0fffffff
> -	mov	r0, r0, lsr #11		@ max = 0x00003fff
> -	mov	r2, r2, lsr #11		@ max = 0x0003ffff
> -	mul	r0, r2, r0		@ max = 2^32-1
> -	movs	r0, r0, lsr #6
> -
> -delay_loop:
> -	subs	r0, r0, #1
> -	bne	delay_loop
> -	mov	pc, lr
> -
> -#endif /* CONFIG_USE_IRQ */
> diff --git a/arch/arm/cpu/ixp/timer.c b/arch/arm/cpu/ixp/timer.c
> index edf341f..7a44a08 100644
> --- a/arch/arm/cpu/ixp/timer.c
> +++ b/arch/arm/cpu/ixp/timer.c
> @@ -1,4 +1,7 @@
>  /*
> + * (C) Copyright 2010
> + * Michael Schwingen, michael@schwingen.org
> + *
>   * (C) Copyright 2006
>   * Stefan Roese, DENX Software Engineering, sr@denx.de.
>   *
> @@ -31,105 +34,94 @@
> 
>  #include <common.h>
>  #include <asm/arch/ixp425.h>
> +#include <asm/io.h>
> +#include <div64.h>
> 
> -#ifdef CONFIG_TIMER_IRQ
> -
> -#define FREQ		66666666
> -#define CLOCK_TICK_RATE	(((FREQ / CONFIG_SYS_HZ & ~IXP425_OST_RELOAD_MASK)
> + 1) * CONFIG_SYS_HZ) -#define LATCH		((CLOCK_TICK_RATE + 
CONFIG_SYS_HZ/2)
> / CONFIG_SYS_HZ)	/* For divider */ +DECLARE_GLOBAL_DATA_PTR;
> 
>  /*
> - * When interrupts are enabled, use timer 2 for time/delay generation...
> + * The IXP42x time-stamp timer runs at 2*OSC_IN (66.666MHz when using a
> + * 33.333MHz crystal).
>   */
> -
> -static volatile ulong timestamp;
> -
> -static void timer_isr(void *data)
> +static inline unsigned long long tick_to_time(unsigned long long tick)
>  {
> -	unsigned int *pTime = (unsigned int *)data;
> -
> -	(*pTime)++;
> -
> -	/*
> -	 * Reset IRQ source
> -	 */
> -	*IXP425_OSST = IXP425_OSST_TIMER_2_PEND;
> +	tick *= CONFIG_SYS_HZ;
> +	do_div(tick, CONFIG_IXP425_TIMER_CLK);
> +	return tick;
>  }
> 
> -ulong get_timer (ulong base)
> +static inline unsigned long long time_to_tick(unsigned long long time)
>  {
> -	return timestamp - base;
> +	time *= CONFIG_IXP425_TIMER_CLK;
> +	do_div(time, CONFIG_SYS_HZ);
> +	return time;
>  }
> 
> -void reset_timer (void)
> +static inline unsigned long long us_to_tick(unsigned long long us)
>  {
> -	timestamp = 0;
> +	us = us * CONFIG_IXP425_TIMER_CLK + 999999;
> +	do_div(us, 1000000);
> +	return us;
>  }
> 
> -int timer_init (void)
> +unsigned long long get_ticks(void)
>  {
> -	/* install interrupt handler for timer */
> -	irq_install_handler(IXP425_TIMER_2_IRQ, timer_isr, (void *)&timestamp);
> -
> -	/* setup the Timer counter value */
> -	*IXP425_OSRT2 = (LATCH & ~IXP425_OST_RELOAD_MASK) | IXP425_OST_ENABLE;
> +	ulong now = *IXP425_OSTS_B;

Aren't you missing an IO accessor here ?

> +
> +	if (readl(IXP425_OSST) & IXP425_OSST_TIMER_TS_PEND) {
> +		/* rollover of timestamp timer register */
> +		gd->timestamp += (0xFFFFFFFF - gd->lastinc) + now + 1;
> +		writel(IXP425_OSST_TIMER_TS_PEND, IXP425_OSST);
> +	} else {
> +		/* move stamp forward with absolut diff ticks */
> +		gd->timestamp += (now - gd->lastinc);
> +	}
> +	gd->lastinc = now;
> +	return gd->timestamp;
> +}
> 
> -	/* enable timer irq */
> -	*IXP425_ICMR = (1 << IXP425_TIMER_2_IRQ);
> 
> -	return 0;
> -}
> -#else
> -ulong get_timer (ulong base)
> +void reset_timer_masked(void)
>  {
> -       return get_timer_masked () - base;
> +	/* capture current timestamp counter */
> +	gd->lastinc = readl(IXP425_OSTS_B);
> +	/* start "advancing" time stamp from 0 */
> +	gd->timestamp = 0;
>  }
> 
> -void ixp425_udelay(unsigned long usec)
> +void reset_timer(void)
>  {
> -	/*
> -	 * This function has a max usec, but since it is called from udelay
> -	 * we should not have to worry... be happy
> -	 */
> -	unsigned long usecs = CONFIG_SYS_HZ/1000000L & ~IXP425_OST_RELOAD_MASK;
> -
> -	*IXP425_OSST = IXP425_OSST_TIMER_1_PEND;
> -	usecs |= IXP425_OST_ONE_SHOT | IXP425_OST_ENABLE;
> -	*IXP425_OSRT1 = usecs;
> -	while (!(*IXP425_OSST & IXP425_OSST_TIMER_1_PEND));
> +	reset_timer_masked();
>  }
> 
> -void __udelay (unsigned long usec)
> +ulong get_timer_masked(void)
>  {
> -	while (usec--) ixp425_udelay(1);
> +	return tick_to_time(get_ticks());
>  }
> 
> -static ulong reload_constant = 0xfffffff0;
> -
> -void reset_timer_masked (void)
> +ulong get_timer(ulong base)
>  {
> -	ulong reload = reload_constant | IXP425_OST_ONE_SHOT | IXP425_OST_ENABLE;
> +	return get_timer_masked() - base;
> +}
> 
> -	*IXP425_OSST = IXP425_OSST_TIMER_1_PEND;
> -	*IXP425_OSRT1 = reload;
> +void set_timer(ulong t)
> +{
> +	gd->timestamp = time_to_tick(t);
>  }
> 
> -ulong get_timer_masked (void)
> +/* delay x useconds AND preserve advance timestamp value */
> +void __udelay(unsigned long usec)
>  {
> -	/*
> -	 * Note that it is possible for this to wrap!
> -	 * In this case we return max.
> -	 */
> -	ulong current = *IXP425_OST1;
> -	if (*IXP425_OSST & IXP425_OSST_TIMER_1_PEND)
> -	{
> -		return reload_constant;
> -	}
> -	return (reload_constant - current);
> +	unsigned long long tmp;
> +
> +	tmp = get_ticks() + us_to_tick(usec);
> +
> +	while (get_ticks() < tmp)
> +		;
>  }
> 
>  int timer_init(void)
>  {
> +	writel(IXP425_OSST_TIMER_TS_PEND, IXP425_OSST);
>  	return 0;
>  }
> -#endif
> diff --git a/arch/arm/include/asm/arch-ixp/ixp425.h
> b/arch/arm/include/asm/arch-ixp/ixp425.h index 2114437..5132607 100644
> --- a/arch/arm/include/asm/arch-ixp/ixp425.h
> +++ b/arch/arm/include/asm/arch-ixp/ixp425.h
> @@ -391,9 +391,8 @@
>  #define IXP425_TIMER_REG(x) (IXP425_TIMER_BASE_PHYS+(x))
>  #endif
> 
> -#if 0 /* test-only: also defined in npe/include/... */
> -#define IXP425_OSTS	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
> -#endif
> +/* _B to avoid collision: also defined in npe/include/... */
> +#define IXP425_OSTS_B	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
>  #define IXP425_OST1	IXP425_TIMER_REG(IXP425_OST1_OFFSET)
>  #define IXP425_OSRT1	IXP425_TIMER_REG(IXP425_OSRT1_OFFSET)
>  #define IXP425_OST2	IXP425_TIMER_REG(IXP425_OST2_OFFSET)

This will eventually enjoy being converted to struct ixp_timer_regset { ... } 
goo. If you already did it in some further patch, sorry, I didn't get there yet 
;-)

> diff --git a/arch/arm/include/asm/global_data.h
> b/arch/arm/include/asm/global_data.h index 2a84d27..c1a59f2 100644
> --- a/arch/arm/include/asm/global_data.h
> +++ b/arch/arm/include/asm/global_data.h
> @@ -64,6 +64,9 @@ typedef	struct	global_data {
>  	unsigned long long	timer_reset_value;
>  	unsigned long	lastinc;
>  #endif
> +#ifdef CONFIG_IXP425
> +	unsigned long	timestamp;
> +#endif
>  	unsigned long	relocaddr;	/* Start address of U-Boot in RAM */
>  	phys_size_t	ram_size;	/* RAM size */
>  	unsigned long	mon_len;	/* monitor len */
Michael Schwingen May 22, 2011, 7:15 p.m. UTC | #2
Am 05/21/2011 01:58 AM, schrieb Marek Vasut:
>
>> -int timer_init (void)
>> +unsigned long long get_ticks(void)
>>  {
>> -	/* install interrupt handler for timer */
>> -	irq_install_handler(IXP425_TIMER_2_IRQ, timer_isr, (void *)&timestamp);
>> -
>> -	/* setup the Timer counter value */
>> -	*IXP425_OSRT2 = (LATCH & ~IXP425_OST_RELOAD_MASK) | IXP425_OST_ENABLE;
>> +	ulong now = *IXP425_OSTS_B;
> Aren't you missing an IO accessor here ?
Oops - you are correct.


>> diff --git a/arch/arm/include/asm/arch-ixp/ixp425.h
>> b/arch/arm/include/asm/arch-ixp/ixp425.h index 2114437..5132607 100644
>> --- a/arch/arm/include/asm/arch-ixp/ixp425.h
>> +++ b/arch/arm/include/asm/arch-ixp/ixp425.h
>> @@ -391,9 +391,8 @@
>>  #define IXP425_TIMER_REG(x) (IXP425_TIMER_BASE_PHYS+(x))
>>  #endif
>>
>> -#if 0 /* test-only: also defined in npe/include/... */
>> -#define IXP425_OSTS	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
>> -#endif
>> +/* _B to avoid collision: also defined in npe/include/... */
>> +#define IXP425_OSTS_B	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
>>  #define IXP425_OST1	IXP425_TIMER_REG(IXP425_OST1_OFFSET)
>>  #define IXP425_OSRT1	IXP425_TIMER_REG(IXP425_OSRT1_OFFSET)
>>  #define IXP425_OST2	IXP425_TIMER_REG(IXP425_OST2_OFFSET)
> This will eventually enjoy being converted to struct ixp_timer_regset { ... } 
> goo. If you already did it in some further patch, sorry, I didn't get there yet 
> ;-)
That would probably require a major rework of all the Intel access/NPE
library code - this is not something I want to touch now.
Most of these defines are used inside the Intel code, so cleaning up
only outside of that area would not improve the code much.

cu
Michael
Marek Vasut May 23, 2011, 12:38 a.m. UTC | #3
On Sunday, May 22, 2011 09:15:03 PM Michael Schwingen wrote:
> Am 05/21/2011 01:58 AM, schrieb Marek Vasut:
> >> -int timer_init (void)
> >> +unsigned long long get_ticks(void)
> >> 
> >>  {
> >> 
> >> -	/* install interrupt handler for timer */
> >> -	irq_install_handler(IXP425_TIMER_2_IRQ, timer_isr, (void
> >> *)&timestamp); -
> >> -	/* setup the Timer counter value */
> >> -	*IXP425_OSRT2 = (LATCH & ~IXP425_OST_RELOAD_MASK) | IXP425_OST_ENABLE;
> >> +	ulong now = *IXP425_OSTS_B;
> > 
> > Aren't you missing an IO accessor here ?
> 
> Oops - you are correct.
> 
> >> diff --git a/arch/arm/include/asm/arch-ixp/ixp425.h
> >> b/arch/arm/include/asm/arch-ixp/ixp425.h index 2114437..5132607 100644
> >> --- a/arch/arm/include/asm/arch-ixp/ixp425.h
> >> +++ b/arch/arm/include/asm/arch-ixp/ixp425.h
> >> @@ -391,9 +391,8 @@
> >> 
> >>  #define IXP425_TIMER_REG(x) (IXP425_TIMER_BASE_PHYS+(x))
> >>  #endif
> >> 
> >> -#if 0 /* test-only: also defined in npe/include/... */
> >> -#define IXP425_OSTS	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
> >> -#endif
> >> +/* _B to avoid collision: also defined in npe/include/... */
> >> +#define IXP425_OSTS_B	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
> >> 
> >>  #define IXP425_OST1	IXP425_TIMER_REG(IXP425_OST1_OFFSET)
> >>  #define IXP425_OSRT1	IXP425_TIMER_REG(IXP425_OSRT1_OFFSET)
> >>  #define IXP425_OST2	IXP425_TIMER_REG(IXP425_OST2_OFFSET)
> > 
> > This will eventually enjoy being converted to struct ixp_timer_regset {
> > ... } goo. If you already did it in some further patch, sorry, I didn't
> > get there yet ;-)
> 
> That would probably require a major rework of all the Intel access/NPE
> library code - this is not something I want to touch now.
> Most of these defines are used inside the Intel code, so cleaning up
> only outside of that area would not improve the code much.

It'd be cool to have this done in next cycle ;-)
> 
> cu
> Michael
diff mbox

Patch

diff --git a/arch/arm/cpu/ixp/cpu.c b/arch/arm/cpu/ixp/cpu.c
index ce275e5..942845d 100644
--- a/arch/arm/cpu/ixp/cpu.c
+++ b/arch/arm/cpu/ixp/cpu.c
@@ -36,8 +36,6 @@ 
 #include <asm/arch/ixp425.h>
 #include <asm/system.h>
 
-ulong loops_per_jiffy;
-
 static void cache_flush(void);
 
 #if defined(CONFIG_DISPLAY_CPUINFO)
@@ -51,17 +49,14 @@  int print_cpuinfo (void)
 	puts("CPU:   Intel IXP425 at ");
 	switch ((id & 0x000003f0) >> 4) {
 	case 0x1c:
-		loops_per_jiffy = 887467;
 		speed = 533;
 		break;
 
 	case 0x1d:
-		loops_per_jiffy = 666016;
 		speed = 400;
 		break;
 
 	case 0x1f:
-		loops_per_jiffy = 442901;
 		speed = 266;
 		break;
 	}
diff --git a/arch/arm/cpu/ixp/start.S b/arch/arm/cpu/ixp/start.S
index 561c1f4..faa9a8f 100644
--- a/arch/arm/cpu/ixp/start.S
+++ b/arch/arm/cpu/ixp/start.S
@@ -65,7 +65,8 @@ 
 	.endm
 
 .globl _start
-_start: b	reset
+_start:
+	ldr	pc, _reset
 	ldr	pc, _undefined_instruction
 	ldr	pc, _software_interrupt
 	ldr	pc, _prefetch_abort
@@ -74,6 +75,7 @@  _start: b	reset
 	ldr	pc, _irq
 	ldr	pc, _fiq
 
+_reset:                 .word reset
 _undefined_instruction: .word undefined_instruction
 _software_interrupt:	.word software_interrupt
 _prefetch_abort:	.word prefetch_abort
@@ -167,12 +169,6 @@  reset:
 	str     r1, [r2]
 
 	/* make sure flash is visible at 0 */
-#if 0
-	ldr	r2, =IXP425_EXP_CFG0
-	ldr     r1, [r2]
-	orr     r1, r1, #0x80000000
-	str     r1, [r2]
-#endif
 	mov	r1, #CONFIG_SYS_SDR_CONFIG
 	ldr     r2, =IXP425_SDR_CONFIG
 	str     r1, [r2]
@@ -216,19 +212,6 @@  reset:
 	str	r1, [r4]
 	DELAY_FOR 0x4000, r0
 
-	/* copy */
-	mov     r0, #0
-	mov     r4, r0
-	add     r2, r0, #CONFIG_SYS_MONITOR_LEN
-	mov     r1, #0x10000000
-	mov     r5, r1
-
-    30:
-	ldr     r3, [r0], #4
-	str     r3, [r1], #4
-	cmp     r0, r2
-	bne     30b
-
 	/* invalidate I & D caches & BTB */
 	mcr	p15, 0, r0, c7, c7, 0
 	CPWAIT	r0
@@ -241,19 +224,12 @@  reset:
 	mcr	p15, 0, r0, c7, c10, 4
 	CPWAIT	r0
 
-	/* move flash to 0x50000000 */
+	/* remove flash mirror at 0x00000000 */
 	ldr	r2, =IXP425_EXP_CFG0
 	ldr     r1, [r2]
 	bic     r1, r1, #0x80000000
 	str     r1, [r2]
 
-	nop
-	nop
-	nop
-	nop
-	nop
-	nop
-
 	/* invalidate I & Data TLB */
 	mcr	p15, 0, r0, c8, c7, 0
 	CPWAIT r0
@@ -269,7 +245,7 @@  reset:
 	orr	r0,r0,#0x13
 	msr	cpsr,r0
 
-/* Set stackpointer in internal RAM to call board_init_f */
+/* Set initial stackpointer in SDRAM to call board_init_f */
 call_board_init_f:
 	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
 	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
@@ -580,28 +556,3 @@  reset_endless:
 
 	b	reset_endless
 
-#ifdef CONFIG_USE_IRQ
-
-.LC0:		.word	loops_per_jiffy
-
-/*
- * 0 <= r0 <= 2000
- */
-.globl __udelay
-__udelay:
-	mov	r2,     #0x6800
-	orr	r2, r2, #0x00db
-	mul	r0, r2, r0
-	ldr	r2, .LC0
-	ldr	r2, [r2]		@ max = 0x0fffffff
-	mov	r0, r0, lsr #11		@ max = 0x00003fff
-	mov	r2, r2, lsr #11		@ max = 0x0003ffff
-	mul	r0, r2, r0		@ max = 2^32-1
-	movs	r0, r0, lsr #6
-
-delay_loop:
-	subs	r0, r0, #1
-	bne	delay_loop
-	mov	pc, lr
-
-#endif /* CONFIG_USE_IRQ */
diff --git a/arch/arm/cpu/ixp/timer.c b/arch/arm/cpu/ixp/timer.c
index edf341f..7a44a08 100644
--- a/arch/arm/cpu/ixp/timer.c
+++ b/arch/arm/cpu/ixp/timer.c
@@ -1,4 +1,7 @@ 
 /*
+ * (C) Copyright 2010
+ * Michael Schwingen, michael@schwingen.org
+ *
  * (C) Copyright 2006
  * Stefan Roese, DENX Software Engineering, sr@denx.de.
  *
@@ -31,105 +34,94 @@ 
 
 #include <common.h>
 #include <asm/arch/ixp425.h>
+#include <asm/io.h>
+#include <div64.h>
 
-#ifdef CONFIG_TIMER_IRQ
-
-#define FREQ		66666666
-#define CLOCK_TICK_RATE	(((FREQ / CONFIG_SYS_HZ & ~IXP425_OST_RELOAD_MASK) + 1) * CONFIG_SYS_HZ)
-#define LATCH		((CLOCK_TICK_RATE + CONFIG_SYS_HZ/2) / CONFIG_SYS_HZ)	/* For divider */
+DECLARE_GLOBAL_DATA_PTR;
 
 /*
- * When interrupts are enabled, use timer 2 for time/delay generation...
+ * The IXP42x time-stamp timer runs at 2*OSC_IN (66.666MHz when using a
+ * 33.333MHz crystal).
  */
-
-static volatile ulong timestamp;
-
-static void timer_isr(void *data)
+static inline unsigned long long tick_to_time(unsigned long long tick)
 {
-	unsigned int *pTime = (unsigned int *)data;
-
-	(*pTime)++;
-
-	/*
-	 * Reset IRQ source
-	 */
-	*IXP425_OSST = IXP425_OSST_TIMER_2_PEND;
+	tick *= CONFIG_SYS_HZ;
+	do_div(tick, CONFIG_IXP425_TIMER_CLK);
+	return tick;
 }
 
-ulong get_timer (ulong base)
+static inline unsigned long long time_to_tick(unsigned long long time)
 {
-	return timestamp - base;
+	time *= CONFIG_IXP425_TIMER_CLK;
+	do_div(time, CONFIG_SYS_HZ);
+	return time;
 }
 
-void reset_timer (void)
+static inline unsigned long long us_to_tick(unsigned long long us)
 {
-	timestamp = 0;
+	us = us * CONFIG_IXP425_TIMER_CLK + 999999;
+	do_div(us, 1000000);
+	return us;
 }
 
-int timer_init (void)
+unsigned long long get_ticks(void)
 {
-	/* install interrupt handler for timer */
-	irq_install_handler(IXP425_TIMER_2_IRQ, timer_isr, (void *)&timestamp);
-
-	/* setup the Timer counter value */
-	*IXP425_OSRT2 = (LATCH & ~IXP425_OST_RELOAD_MASK) | IXP425_OST_ENABLE;
+	ulong now = *IXP425_OSTS_B;
+
+	if (readl(IXP425_OSST) & IXP425_OSST_TIMER_TS_PEND) {
+		/* rollover of timestamp timer register */
+		gd->timestamp += (0xFFFFFFFF - gd->lastinc) + now + 1;
+		writel(IXP425_OSST_TIMER_TS_PEND, IXP425_OSST);
+	} else {
+		/* move stamp forward with absolut diff ticks */
+		gd->timestamp += (now - gd->lastinc);
+	}
+	gd->lastinc = now;
+	return gd->timestamp;
+}
 
-	/* enable timer irq */
-	*IXP425_ICMR = (1 << IXP425_TIMER_2_IRQ);
 
-	return 0;
-}
-#else
-ulong get_timer (ulong base)
+void reset_timer_masked(void)
 {
-       return get_timer_masked () - base;
+	/* capture current timestamp counter */
+	gd->lastinc = readl(IXP425_OSTS_B);
+	/* start "advancing" time stamp from 0 */
+	gd->timestamp = 0;
 }
 
-void ixp425_udelay(unsigned long usec)
+void reset_timer(void)
 {
-	/*
-	 * This function has a max usec, but since it is called from udelay
-	 * we should not have to worry... be happy
-	 */
-	unsigned long usecs = CONFIG_SYS_HZ/1000000L & ~IXP425_OST_RELOAD_MASK;
-
-	*IXP425_OSST = IXP425_OSST_TIMER_1_PEND;
-	usecs |= IXP425_OST_ONE_SHOT | IXP425_OST_ENABLE;
-	*IXP425_OSRT1 = usecs;
-	while (!(*IXP425_OSST & IXP425_OSST_TIMER_1_PEND));
+	reset_timer_masked();
 }
 
-void __udelay (unsigned long usec)
+ulong get_timer_masked(void)
 {
-	while (usec--) ixp425_udelay(1);
+	return tick_to_time(get_ticks());
 }
 
-static ulong reload_constant = 0xfffffff0;
-
-void reset_timer_masked (void)
+ulong get_timer(ulong base)
 {
-	ulong reload = reload_constant | IXP425_OST_ONE_SHOT | IXP425_OST_ENABLE;
+	return get_timer_masked() - base;
+}
 
-	*IXP425_OSST = IXP425_OSST_TIMER_1_PEND;
-	*IXP425_OSRT1 = reload;
+void set_timer(ulong t)
+{
+	gd->timestamp = time_to_tick(t);
 }
 
-ulong get_timer_masked (void)
+/* delay x useconds AND preserve advance timestamp value */
+void __udelay(unsigned long usec)
 {
-	/*
-	 * Note that it is possible for this to wrap!
-	 * In this case we return max.
-	 */
-	ulong current = *IXP425_OST1;
-	if (*IXP425_OSST & IXP425_OSST_TIMER_1_PEND)
-	{
-		return reload_constant;
-	}
-	return (reload_constant - current);
+	unsigned long long tmp;
+
+	tmp = get_ticks() + us_to_tick(usec);
+
+	while (get_ticks() < tmp)
+		;
 }
 
 int timer_init(void)
 {
+	writel(IXP425_OSST_TIMER_TS_PEND, IXP425_OSST);
 	return 0;
 }
-#endif
diff --git a/arch/arm/include/asm/arch-ixp/ixp425.h b/arch/arm/include/asm/arch-ixp/ixp425.h
index 2114437..5132607 100644
--- a/arch/arm/include/asm/arch-ixp/ixp425.h
+++ b/arch/arm/include/asm/arch-ixp/ixp425.h
@@ -391,9 +391,8 @@ 
 #define IXP425_TIMER_REG(x) (IXP425_TIMER_BASE_PHYS+(x))
 #endif
 
-#if 0 /* test-only: also defined in npe/include/... */
-#define IXP425_OSTS	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
-#endif
+/* _B to avoid collision: also defined in npe/include/... */
+#define IXP425_OSTS_B	IXP425_TIMER_REG(IXP425_OSTS_OFFSET)
 #define IXP425_OST1	IXP425_TIMER_REG(IXP425_OST1_OFFSET)
 #define IXP425_OSRT1	IXP425_TIMER_REG(IXP425_OSRT1_OFFSET)
 #define IXP425_OST2	IXP425_TIMER_REG(IXP425_OST2_OFFSET)
diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
index 2a84d27..c1a59f2 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -64,6 +64,9 @@  typedef	struct	global_data {
 	unsigned long long	timer_reset_value;
 	unsigned long	lastinc;
 #endif
+#ifdef CONFIG_IXP425
+	unsigned long	timestamp;
+#endif
 	unsigned long	relocaddr;	/* Start address of U-Boot in RAM */
 	phys_size_t	ram_size;	/* RAM size */
 	unsigned long	mon_len;	/* monitor len */