Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Lennert Buytenhek and committed by
Russell King
e4fe1981 c0da085a

+33 -9
+26 -8
arch/arm/mach-ixp2000/core.c
··· 162 162 static unsigned ticks_per_jiffy; 163 163 static unsigned ticks_per_usec; 164 164 static unsigned next_jiffy_time; 165 + static volatile unsigned long *missing_jiffy_timer_csr; 165 166 166 167 unsigned long ixp2000_gettimeoffset (void) 167 168 { 168 169 unsigned long offset; 169 170 170 - offset = next_jiffy_time - *IXP2000_T4_CSR; 171 + offset = next_jiffy_time - *missing_jiffy_timer_csr; 171 172 172 173 return offset / ticks_per_usec; 173 174 } ··· 180 179 /* clear timer 1 */ 181 180 ixp2000_reg_write(IXP2000_T1_CLR, 1); 182 181 183 - while ((next_jiffy_time - *IXP2000_T4_CSR) > ticks_per_jiffy) { 182 + while ((next_jiffy_time - *missing_jiffy_timer_csr) > ticks_per_jiffy) { 184 183 timer_tick(regs); 185 184 next_jiffy_time -= ticks_per_jiffy; 186 185 } ··· 198 197 199 198 void __init ixp2000_init_time(unsigned long tick_rate) 200 199 { 201 - ixp2000_reg_write(IXP2000_T1_CLR, 0); 202 - ixp2000_reg_write(IXP2000_T4_CLR, 0); 203 - 204 200 ticks_per_jiffy = (tick_rate + HZ/2) / HZ; 205 201 ticks_per_usec = tick_rate / 1000000; 206 202 203 + /* 204 + * We use timer 1 as our timer interrupt. 205 + */ 206 + ixp2000_reg_write(IXP2000_T1_CLR, 0); 207 207 ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); 208 208 ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); 209 209 210 210 /* 211 - * We use T4 as a monotonic counter to track missed jiffies 211 + * We use a second timer as a monotonic counter for tracking 212 + * missed jiffies. The IXP2000 has four timers, but if we're 213 + * on an A-step IXP2800, timer 2 and 3 don't work, so on those 214 + * chips we use timer 4. Timer 4 is the only timer that can 215 + * be used for the watchdog, so we use timer 2 if we're on a 216 + * non-buggy chip. 212 217 */ 213 - ixp2000_reg_write(IXP2000_T4_CLD, -1); 214 - ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); 218 + if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { 219 + printk(KERN_INFO "Enabling IXP2800 erratum #25 workaround\n"); 220 + 221 + ixp2000_reg_write(IXP2000_T4_CLR, 0); 222 + ixp2000_reg_write(IXP2000_T4_CLD, -1); 223 + ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); 224 + missing_jiffy_timer_csr = IXP2000_T4_CSR; 225 + } else { 226 + ixp2000_reg_write(IXP2000_T2_CLR, 0); 227 + ixp2000_reg_write(IXP2000_T2_CLD, -1); 228 + ixp2000_reg_write(IXP2000_T2_CTL, (1 << 7)); 229 + missing_jiffy_timer_csr = IXP2000_T2_CSR; 230 + } 215 231 next_jiffy_time = 0xffffffff; 216 232 217 233 /* register for interrupt */
+6 -1
drivers/char/watchdog/ixp2000_wdt.c
··· 192 192 193 193 static int __init ixp2000_wdt_init(void) 194 194 { 195 - wdt_tick_rate = (*IXP2000_T1_CLD * HZ)/ 256;; 195 + if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { 196 + printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n"); 197 + return -EIO; 198 + } 199 + 200 + wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256; 196 201 197 202 return misc_register(&ixp2000_wdt_miscdev); 198 203 }
+1
include/asm-arm/arch-ixp2000/ixp2000-regs.h
··· 363 363 #define IXP2000_MIN_REV_MASK 0x0000000F 364 364 #define IXP2000_PROD_ID_MASK 0xFFFFFFFF 365 365 366 + #define IXP2000_PRODUCT_ID GLOBAL_REG(0x00) 366 367 #define IXP2000_MISC_CONTROL GLOBAL_REG(0x04) 367 368 #define IXP2000_MSF_CLK_CNTRL GLOBAL_REG(0x08) 368 369 #define IXP2000_RESET0 GLOBAL_REG(0x0c)