浏览代码

[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>
Lennert Buytenhek 20 年之前
父节点
当前提交
e4fe19819e
共有 3 个文件被更改,包括 33 次插入9 次删除
  1. 26 8
      arch/arm/mach-ixp2000/core.c
  2. 6 1
      drivers/char/watchdog/ixp2000_wdt.c
  3. 1 0
      include/asm-arm/arch-ixp2000/ixp2000-regs.h

+ 26 - 8
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 */

+ 6 - 1
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);
 }

+ 1 - 0
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)