diff options
author | Lennert Buytenhek <buytenh@org.rmk.(none)> | 2005-06-20 13:51:07 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2005-06-20 13:51:07 -0400 |
commit | e4fe19819ef32950541503042f32e71b67edffc7 (patch) | |
tree | 57c68008ba8c3b3a9f6ab8950c620175e9405569 /arch/arm/mach-ixp2000 | |
parent | c0da085ad2e6b1419b8a7439538f7f15eb5c4777 (diff) |
[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>
Diffstat (limited to 'arch/arm/mach-ixp2000')
-rw-r--r-- | arch/arm/mach-ixp2000/core.c | 34 |
1 files changed, 26 insertions, 8 deletions
diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c index 4f3c3d5c781c..fc0555596d6d 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) | |||
162 | static unsigned ticks_per_jiffy; | 162 | static unsigned ticks_per_jiffy; |
163 | static unsigned ticks_per_usec; | 163 | static unsigned ticks_per_usec; |
164 | static unsigned next_jiffy_time; | 164 | static unsigned next_jiffy_time; |
165 | static volatile unsigned long *missing_jiffy_timer_csr; | ||
165 | 166 | ||
166 | unsigned long ixp2000_gettimeoffset (void) | 167 | unsigned long ixp2000_gettimeoffset (void) |
167 | { | 168 | { |
168 | unsigned long offset; | 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 | return offset / ticks_per_usec; | 173 | return offset / ticks_per_usec; |
173 | } | 174 | } |
@@ -179,7 +180,7 @@ static int ixp2000_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
179 | /* clear timer 1 */ | 180 | /* clear timer 1 */ |
180 | ixp2000_reg_write(IXP2000_T1_CLR, 1); | 181 | ixp2000_reg_write(IXP2000_T1_CLR, 1); |
181 | 182 | ||
182 | while ((next_jiffy_time - *IXP2000_T4_CSR) > ticks_per_jiffy) { | 183 | while ((next_jiffy_time - *missing_jiffy_timer_csr) > ticks_per_jiffy) { |
183 | timer_tick(regs); | 184 | timer_tick(regs); |
184 | next_jiffy_time -= ticks_per_jiffy; | 185 | next_jiffy_time -= ticks_per_jiffy; |
185 | } | 186 | } |
@@ -197,20 +198,37 @@ static struct irqaction ixp2000_timer_irq = { | |||
197 | 198 | ||
198 | void __init ixp2000_init_time(unsigned long tick_rate) | 199 | void __init ixp2000_init_time(unsigned long tick_rate) |
199 | { | 200 | { |
200 | ixp2000_reg_write(IXP2000_T1_CLR, 0); | ||
201 | ixp2000_reg_write(IXP2000_T4_CLR, 0); | ||
202 | |||
203 | ticks_per_jiffy = (tick_rate + HZ/2) / HZ; | 201 | ticks_per_jiffy = (tick_rate + HZ/2) / HZ; |
204 | ticks_per_usec = tick_rate / 1000000; | 202 | ticks_per_usec = tick_rate / 1000000; |
205 | 203 | ||
204 | /* | ||
205 | * We use timer 1 as our timer interrupt. | ||
206 | */ | ||
207 | ixp2000_reg_write(IXP2000_T1_CLR, 0); | ||
206 | ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); | 208 | ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); |
207 | ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); | 209 | ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); |
208 | 210 | ||
209 | /* | 211 | /* |
210 | * We use T4 as a monotonic counter to track missed jiffies | 212 | * We use a second timer as a monotonic counter for tracking |
213 | * missed jiffies. The IXP2000 has four timers, but if we're | ||
214 | * on an A-step IXP2800, timer 2 and 3 don't work, so on those | ||
215 | * chips we use timer 4. Timer 4 is the only timer that can | ||
216 | * be used for the watchdog, so we use timer 2 if we're on a | ||
217 | * non-buggy chip. | ||
211 | */ | 218 | */ |
212 | ixp2000_reg_write(IXP2000_T4_CLD, -1); | 219 | if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { |
213 | ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); | 220 | printk(KERN_INFO "Enabling IXP2800 erratum #25 workaround\n"); |
221 | |||
222 | ixp2000_reg_write(IXP2000_T4_CLR, 0); | ||
223 | ixp2000_reg_write(IXP2000_T4_CLD, -1); | ||
224 | ixp2000_reg_write(IXP2000_T4_CTL, (1 << 7)); | ||
225 | missing_jiffy_timer_csr = IXP2000_T4_CSR; | ||
226 | } else { | ||
227 | ixp2000_reg_write(IXP2000_T2_CLR, 0); | ||
228 | ixp2000_reg_write(IXP2000_T2_CLD, -1); | ||
229 | ixp2000_reg_write(IXP2000_T2_CTL, (1 << 7)); | ||
230 | missing_jiffy_timer_csr = IXP2000_T2_CSR; | ||
231 | } | ||
214 | next_jiffy_time = 0xffffffff; | 232 | next_jiffy_time = 0xffffffff; |
215 | 233 | ||
216 | /* register for interrupt */ | 234 | /* register for interrupt */ |