[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 <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c
index 4f3c3d5c..fc05555 100644
--- a/arch/arm/mach-ixp2000/core.c
+++ b/arch/arm/mach-ixp2000/core.c
@@ -162,12 +162,13 @@
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 @@
/* 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 @@
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 */