[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>
This commit is contained in:
parent
c0da085ad2
commit
e4fe19819e
3 changed files with 33 additions and 9 deletions
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue