Submitted as http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2006-June/034635.html The basis for this patch was discussed last year on l-a-k on a thread with subject line "IXP4xx timer interupt bug" (see http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2005-September/031410.html), then later on nslu2-developers@nslu2-linux.org on a thread "ixp4xx variable timer base issue" (see http://thread.gmane.org/gmane.comp.misc.nslu2.devel/137/focus=137). This new patch addresses all the valid concerns raised previously and achieves the desired outcome with minimal changes. The problem is that the NSLU2 deviates from the Intel IXDP425 reference system by using a cystal with frequency 66.0000MHz as opposed to the Intel stated (but not quite mandated) value of 66.6666MHz. This results in serious errors in the RTC (1%). Previous versions of this patch (which hard-wired the NSLU2 crystal frequency with a compile-time definition) had the problem that a multi-config kernel which included NSLU2 support would use the NSLU2 frequency, and were not pushed upstream for that good reason. This new IXP4xx timer patch allows variable crystal frequencies for boards which do not have a true 66.6666MHz crystal (for example, the Linksys NSLU2 which has a 66.0000MHz crystal). The patch provides an API ixp4xx_set_board_tick_rate which may be used to correct the actual clock frequency from the board level code, and has been developed specifically so the NSLU2 board can be enabled along with other IXP4xx-based boards in the same multi-config kernel. If a particular board does not use the API to set a non-standard value for the crystal frequency, then the code defaults to the standard 66.6666MHz crystal behaviour (i.e. no change in functionality). Signed-off-by: John Bowler Signed-off-by: Rod Whitby PATCH FOLLOWS KernelVersion: 2.6.17 arch/arm/mach-ixp4xx/common.c | 165 +++++++++++++++++++++++++++++++++--- include/asm-arm/arch-ixp4xx/timex.h | 23 +++-- 2 files changed, 171 insertions(+), 17 deletions(-) Index: linux-2.6.17/arch/arm/mach-ixp4xx/common.c =================================================================== --- linux-2.6.17.orig/arch/arm/mach-ixp4xx/common.c +++ linux-2.6.17/arch/arm/mach-ixp4xx/common.c @@ -250,36 +250,165 @@ * IXP4xx timer tick * We use OS timer1 on the CPU for the timer tick and the timestamp * counter as a source of real clock ticks to account for missed jiffies. + * + * 'CLOCK_TICK_RATE' is the nominal number of internal ticks per second, + * this is significantly greater than the actual number on any ixp4xx + * board. Neither this nor 'LATCH' are required by this code because + * the only requirement is to generate HZ timer_tick calls per second. *************************************************************************/ +#if TICK_NSEC * HZ != 1000000000 + /* This will cause the jiffies to drift unnecessarily. */ +# error CLOCK_TICK_RATE should be a multiple of HZ for this code +#endif + +/* These are the control registers for the interrupt handler, they must + * only be read and written by the interrupt handler and by the init + * method (which sets them to 0). + */ +static volatile u32 last_timer_time; +static volatile int accumulated_error; + +/* Most ixp4xx boards have 33.3333MHz crystals, so default to this, reset + * this from the board level code if required. The following variables + * must be *written* only by set_board_tick_rate + */ +static u32 board_tick_rate; +static u32 board_tick_per_1000; /* board_tick_rate/1000 */ +static u32 timer_count; + +/* The following symbol may be written to change the current tick rate, + * it is read by the interrupt handler and used to reload the timer. + * The 'real' value (the one in use) is 'board_tick_rate' above. + * NOTE: this can be tweaked to match the actual crystal on a particular + * machine. + */ +volatile u32 ixp4xx_board_tick_rate = 66666600; +EXPORT_SYMBOL(ixp4xx_board_tick_rate); + +/* The set API may run asynchronously in the presence of interrupts, + * everything it does it is both atomic and complete (notice that it + * doesn't change any of the 'volatile' values). The mathematics in + * here require the following values. Changing the board tick rate + * implies an unknown error in the current timestamp tick count. + */ +#if IXP4XX_OST_RELOAD_MASK != 3 || IXP4XX_OST_ENABLE != 1 +# error unexpected value for timer reload mask +#endif +static void set_board_tick_rate(u32 rate) { + u32 reload; + + /* Store the two effectively informational rate values, the + * error calculation is (rate - count*HZ) (above), and rate + * is changed first, this can cause a temporary error which + * will be corrected on the next interrupt. + */ + board_tick_rate = rate; + board_tick_per_1000 = (rate+500)/1000; + + /* Calculate the correct value to load into the timer countdown + * register, the low two bits must be b01 (to enable the timer). + * Select the top bits to be as close to the desired value as + * possible. + * + * First find the best value, regardless of the low two bits - + * this is the value used in the interrupt calculation even though + * it cannot necessarily be set into the register. + */ + timer_count = (rate + (HZ/2))/HZ; + + /* Now the timer_ticks are being generated at this rate, calculate + * an appropriate value for the register. This stores a 30 bit + * value which gives a period of 4*x+1, we want: + * + * 4*x+1 = board_tick_rate/HZ + * + * This needs to be rounded to the closest 4*HZ value: + * + * x = ((board_tick_rate-HZ) + (4*HZ)/2) / 4*HZ + * x = (board_tick_rate+HZ) / (4*HZ); + */ + reload = (board_tick_rate + HZ) / HZ; + reload = (reload & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; + *IXP4XX_OSRT1 = reload; -static unsigned volatile last_jiffy_time; + /* If the clock is drifing, look in syslog: */ + printk(KERN_INFO "IXP4xx: FREQ=%d COUNT=%d\n", rate, reload); +} -#define CLOCK_TICKS_PER_USEC ((CLOCK_TICK_RATE + USEC_PER_SEC/2) / USEC_PER_SEC) +/* This returns the time in timer ticks since the 'last_timer_time' + * recorded above. Use this to avoid arithmetic errors because of + * the overflow when the timer wraps. + */ +static inline u32 ixp4xx_timer_delta(void) +{ + return *IXP4XX_OSTS - last_timer_time; +} /* IRQs are disabled before entering here from do_gettimeofday() */ static unsigned long ixp4xx_gettimeoffset(void) { - u32 elapsed; - - elapsed = *IXP4XX_OSTS - last_jiffy_time; + /* Return the offset of the current time from the last time + * timer tick in microseconds. This is only used for the + * gettimeofday call. + * + * The result of this API is at most about 20000 (for a 50Hz + * HZ - 20000 uS/tick), the input delta is at most about + * 1.3M - 21 bits. + */ + u32 delta = ixp4xx_timer_delta(); /* About 21 bits max */ + /* return delta * 1000000 / board_tick_rate; */ + return (delta * 1000 + board_tick_per_1000/2) / board_tick_per_1000; +} - return elapsed / CLOCK_TICKS_PER_USEC; +/* This is the correct adjustment to the counter to compensate for an + * error iff timer_count-1 <= exact_count <= timer_count+1 + */ +static inline int adjustment(int error) { + if (error >= HZ) + return 1; + else if (error <= -HZ) + return -1; + return 0; } static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + u32 rate; + u32 count; + int error; + write_seqlock(&xtime_lock); /* Clear Pending Interrupt by writing '1' to it */ *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; + /* If the board tick rate has been changed update the cached + * value. + */ + if (ixp4xx_board_tick_rate != board_tick_rate) { + set_board_tick_rate(ixp4xx_board_tick_rate); + accumulated_error = 0; + } + /* * Catch up with the real idea of time + * + * board_tick_rate: actual ixp4xx ticks/second, read-only + * accumulated_error: aggregate error/tick * HZ, read/write + * timer_count: best ixp4xx ticks per timer_tick, read-only */ - while ((*IXP4XX_OSTS - last_jiffy_time) > LATCH) { + rate = board_tick_rate; + error = accumulated_error; + count = timer_count; + do { + u32 adjusted_count = count + adjustment(error); + if (ixp4xx_timer_delta() < adjusted_count) + break; timer_tick(regs); - last_jiffy_time += LATCH; - } + last_timer_time += adjusted_count; + error += rate - adjusted_count*HZ; + } while (1); + accumulated_error = error; write_sequnlock(&xtime_lock); @@ -292,17 +421,30 @@ .handler = ixp4xx_timer_interrupt, }; +u32 ixp4xx_get_board_tick_rate(void) { + return board_tick_rate; +} + +EXPORT_SYMBOL(ixp4xx_get_board_tick_rate); + +void ixp4xx_set_board_tick_rate(u32 rate) { + ixp4xx_board_tick_rate = rate; +} + +EXPORT_SYMBOL(ixp4xx_set_board_tick_rate); + static void __init ixp4xx_timer_init(void) { /* Clear Pending Interrupt by writing '1' to it */ *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; /* Setup the Timer counter value */ - *IXP4XX_OSRT1 = (LATCH & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; + set_board_tick_rate(ixp4xx_board_tick_rate); /* Reset time-stamp counter */ *IXP4XX_OSTS = 0; - last_jiffy_time = 0; + last_timer_time = 0; + accumulated_error = 0; /* Connect the interrupt handler and enable the interrupt */ setup_irq(IRQ_IXP4XX_TIMER1, &ixp4xx_timer_irq); @@ -365,4 +507,3 @@ printk("IXP4xx: Using %luMiB expansion bus window size\n", ixp4xx_exp_bus_size >> 20); } - Index: linux-2.6.17/include/asm-arm/arch-ixp4xx/timex.h =================================================================== --- linux-2.6.17.orig/include/asm-arm/arch-ixp4xx/timex.h +++ linux-2.6.17/include/asm-arm/arch-ixp4xx/timex.h @@ -6,10 +6,23 @@ #include /* - * We use IXP425 General purpose timer for our timer needs, it runs at - * 66.66... MHz. We do a convulted calculation of CLOCK_TICK_RATE b/c the - * timer register ignores the bottom 2 bits of the LATCH value. + * In linux/timex.h 'LATCH' is defined as CLOCK_TICK_RATE/HZ and + * is the number of internal counts per timer interrupt. Thus + * CLOCK_TICK_RATE is LATCH*HZ. + * + * The actual values of these numbers do not matter, because they + * are only used to calculate ACTHZ (rate/latch as a 24.8 fixed + * point number), so the value here gives a LATCH of 1 and pretty + * much guarantees to flush out any off-by-one errors. + * + * ACTHZ is equal to HZ, because CLOCK_TICK_RATE is a multiple of + * HZ, this is checked in the ixp4xx/common.c code. */ -#define FREQ 66666666 -#define CLOCK_TICK_RATE (((FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ) +#define CLOCK_TICK_RATE HZ +/* The following allow the exact board tick rate to be set and + * discovered. The value should be exactly twice the frequency + * (in Hz) of the onboard crystal. + */ +extern u32 ixp4xx_get_board_tick_rate(void); +extern void ixp4xx_set_board_tick_rate(u32 new_rate);