From e4fe19819ef32950541503042f32e71b67edffc7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 20 Jun 2005 18:51:07 +0100 Subject: [PATCH] [PATCH] ARM: 2701/1: free up ixp2000 timer 4 for the watchdog Patch from Lennert Buytenhek The IXP2000 has four timers, but if we're on an A-step IXP2800, timer 2 and 3 don't work. We need two timers for timekeeping (one for the timer interrupt and one for tracking missed jiffies), so on early IXP2800s we have no other choice but to use timer 1 and 4 for that, but on all other IXP2000s we'd rather leave timer 4 free since that's the only timer we can use for the watchdog. So, on buggy IXP2000s (i.e. the A-step IXP2800) we use timer 4 for tracking missed jiffies, and on all all non-buggy IXP2000s (i.e. everything but the A-step IXP2800) we use timer 2. On a pre-production IXP2800, this patch should print these messages on boot: Enabling IXP2800 erratum #25 workaround Unable to use IXP2000 watchdog due to IXP2800 erratum #25 On any non-buggy IXP2800 (as well as on IXP2400s) you shouldn't see anything at all, and the watchdog should be usable again. Signed-off-by: Lennert Buytenhek Signed-off-by: Deepak Saxena Signed-off-by: Russell King --- arch/arm/mach-ixp2000/core.c | 34 ++++++++++++++++----- drivers/char/watchdog/ixp2000_wdt.c | 7 ++++- include/asm-arm/arch-ixp2000/ixp2000-regs.h | 1 + 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c index 4f3c3d5c78..fc0555596d 100644 --- a/arch/arm/mach-ixp2000/core.c +++ b/arch/arm/mach-ixp2000/core.c @@ -162,12 +162,13 @@ void __init ixp2000_map_io(void) static unsigned ticks_per_jiffy; static unsigned ticks_per_usec; static unsigned next_jiffy_time; +static volatile unsigned long *missing_jiffy_timer_csr; unsigned long ixp2000_gettimeoffset (void) { unsigned long offset; - offset = next_jiffy_time - *IXP2000_T4_CSR; + offset = next_jiffy_time - *missing_jiffy_timer_csr; return offset / ticks_per_usec; } @@ -179,7 +180,7 @@ static int ixp2000_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* clear timer 1 */ ixp2000_reg_write(IXP2000_T1_CLR, 1); - while ((next_jiffy_time - *IXP2000_T4_CSR) > ticks_per_jiffy) { + while ((next_jiffy_time - *missing_jiffy_timer_csr) > ticks_per_jiffy) { timer_tick(regs); next_jiffy_time -= ticks_per_jiffy; } @@ -197,20 +198,37 @@ static struct irqaction ixp2000_timer_irq = { void __init ixp2000_init_time(unsigned long tick_rate) { - ixp2000_reg_write(IXP2000_T1_CLR, 0); - ixp2000_reg_write(IXP2000_T4_CLR, 0); - ticks_per_jiffy = (tick_rate + HZ/2) / HZ; ticks_per_usec = tick_rate / 1000000; + /* + * We use timer 1 as our timer interrupt. + */ + ixp2000_reg_write(IXP2000_T1_CLR, 0); ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); /* - * We use T4 as a monotonic counter to track missed jiffies + * We use a second timer as a monotonic counter for tracking + * missed jiffies. The IXP2000 has four timers, but if we're + * on an A-step IXP2800, timer 2 and 3 don't work, so on those + * chips we use timer 4. Timer 4 is the only timer that can + * be used for the watchdog, so we use timer 2 if we're on a + * non-buggy chip. */ - ixp2000_reg_write(IXP2000_T4_CLD, -1); - ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); + if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { + printk(KERN_INFO "Enabling IXP2800 erratum #25 workaround\n"); + + ixp2000_reg_write(IXP2000_T4_CLR, 0); + ixp2000_reg_write(IXP2000_T4_CLD, -1); + ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); + missing_jiffy_timer_csr = IXP2000_T4_CSR; + } else { + ixp2000_reg_write(IXP2000_T2_CLR, 0); + ixp2000_reg_write(IXP2000_T2_CLD, -1); + ixp2000_reg_write(IXP2000_T2_CTL, (1 << 7)); + missing_jiffy_timer_csr = IXP2000_T2_CSR; + } next_jiffy_time = 0xffffffff; /* register for interrupt */ diff --git a/drivers/char/watchdog/ixp2000_wdt.c b/drivers/char/watchdog/ixp2000_wdt.c index ab659d37b4..4e98c215e5 100644 --- a/drivers/char/watchdog/ixp2000_wdt.c +++ b/drivers/char/watchdog/ixp2000_wdt.c @@ -192,7 +192,12 @@ static struct miscdevice ixp2000_wdt_miscdev = static int __init ixp2000_wdt_init(void) { - wdt_tick_rate = (*IXP2000_T1_CLD * HZ)/ 256;; + if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { + printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n"); + return -EIO; + } + + wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256; return misc_register(&ixp2000_wdt_miscdev); } diff --git a/include/asm-arm/arch-ixp2000/ixp2000-regs.h b/include/asm-arm/arch-ixp2000/ixp2000-regs.h index 6c56708d0f..a1d9e181b1 100644 --- a/include/asm-arm/arch-ixp2000/ixp2000-regs.h +++ b/include/asm-arm/arch-ixp2000/ixp2000-regs.h @@ -363,6 +363,7 @@ #define IXP2000_MIN_REV_MASK 0x0000000F #define IXP2000_PROD_ID_MASK 0xFFFFFFFF +#define IXP2000_PRODUCT_ID GLOBAL_REG(0x00) #define IXP2000_MISC_CONTROL GLOBAL_REG(0x04) #define IXP2000_MSF_CLK_CNTRL GLOBAL_REG(0x08) #define IXP2000_RESET0 GLOBAL_REG(0x0c) -- 2.39.5