Hi I have discovered that the accuracy of the wallclock (xtime) on ppc is not very good. I am running a custom telco board based on a 8266, and the main busclock is derived off of a T1 reference clock. I was noticing a huge number of logentries fron OpenNTPD about adjustiing the clock, so I started to check. The drift of the walltime was a little over 7 seconds in 15 hours (7 seconds slow) (equals about 130us per s)
When I checked around, I found that xtime is updated in kernel/time.c and is updated by time_nsec nanoseconds per timer interrupt. This value is hardcoded (through some obscure macros) to be 999848 ns/timertick. Tis is 152ns slow per timertick. This works out to be 152us per second, almos the value I found. I have tried a few workarounds, and made one that modifies time_nsec before update_wall_time_one_tick() is called. With this fix (which is 8260 specific at the time) I have less than 1 ms drift after 5 days. The fix works by calculating how many ns and fractions of ns there is in one timer-tick (one jiffie). Then, at each timer interrupt, I accumulate the ns fractions, and add whole ns to the timer when needed. This is tested on 8266 under 80.00MHz and 82.575360MHz main busclock. It also works correct if HZ=1024 instead of 1000 It is not meant as a patch to be added to the kernel right now, but more of a possible solution to a problem that it doesn't seem anybody has noticed before. Index: arch/ppc/kernel/time.c =================================================================== --- arch/ppc/kernel/time.c (revision 20) +++ arch/ppc/kernel/time.c (working copy) @@ -119,6 +119,11 @@ EXPORT_SYMBOL(profile_pc); #endif + +#ifdef CONFIG_8260 +extern unsigned long get_ns_delta(void); /* arch/ppc/syslib/m8260_setup.c */ +extern unsigned long tick_nsec; /* kernel/time.c */ +#endif /* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. @@ -148,6 +153,10 @@ /* We are in an interrupt, no need to save/restore flags */ write_seqlock(&xtime_lock); tb_last_stamp = jiffy_stamp; + +#ifdef CONFIG_8260 + tick_nsec = get_ns_delta(); +#endif do_timer(regs); /* @@ -179,7 +188,7 @@ write_sequnlock(&xtime_lock); } if ( !disarm_decr[smp_processor_id()] ) - set_dec(next_dec); + set_dec(next_dec-1); last_jiffy_stamp(cpu) = jiffy_stamp; if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) Index: arch/ppc/syslib/m8260_setup.c =================================================================== --- arch/ppc/syslib/m8260_setup.c (revision 20) +++ arch/ppc/syslib/m8260_setup.c (working copy) @@ -31,6 +31,8 @@ #include "cpm2_pic.h" +#include <linux/module.h> + unsigned char __res[sizeof(bd_t)]; extern void cpm2_reset(void); @@ -82,16 +84,63 @@ /* The decrementer counts at the system (internal) clock frequency * divided by four. */ +unsigned long tb_time_nsec; +unsigned long tb_frac_nsec; +unsigned long tb_frac_limit; + +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); + static void __init m8260_calibrate_decr(void) +{ + bd_t *binfo = (bd_t *)__res; + int freq, divisor, tick_freq; + uint64_t tmp; + printk("Calibrating Decrementer\n"); + + freq = binfo->bi_busfreq; + divisor = 4; + + /* get rounded off decrementer frequency */ + tick_freq = (freq + 3) / 4; + + /* get number of timebase ticks per jiffy (timerint) */ + tb_ticks_per_jiffy = (tick_freq + (HZ/2)) / HZ; + tb_to_us = mulhwu_scale_factor(tick_freq, 1000000); + + /* calculate timer interrupt interval in nano seconds */ + tmp = 1000000000ULL * (u64)tb_ticks_per_jiffy; + __div64_32(&tmp, tick_freq); + tb_time_nsec = tmp; + + /* calculate fractions of nanosecons per timer interrupt */ + tb_frac_limit = 100000UL; + tmp = 1000000000ULL * (u64)tb_ticks_per_jiffy * tb_frac_limit; + __div64_32(&tmp, tick_freq); + tb_frac_nsec = (u32)(tmp - (u64)tb_time_nsec * (u64)tb_frac_limit); + + printk("busfreq = %u\n", freq); + printk("tb_ticks_per_jiffy = %u\ntb_to_us = %u\n", tb_ticks_per_jiffy, tb_to_us); + printk("tick_freq = %u\ntime_nsec = %u\ntb_frac_nsec = %u\n", tick_freq, tb_time_nsec, tb_frac_nsec); +} + +unsigned long get_ns_delta(void) { - bd_t *binfo = (bd_t *)__res; - int freq, divisor; + static uint32_t frac = 0; - freq = binfo->bi_busfreq; - divisor = 4; - tb_ticks_per_jiffy = freq / HZ / divisor; - tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); + unsigned long delta_ns; + + delta_ns = tb_time_nsec; + + /* adjust for inaccuracy in calculation */ + frac += tb_frac_nsec; + if (frac >= tb_frac_limit) + { + delta_ns += frac / tb_frac_limit; + frac %= tb_frac_limit; + } + + return delta_ns; } /* The 8260 has an internal 1-second timer update register that Rune Torgersen System Developer Innovative Systems LLC 1000 Innovative Drive Mitchell, SD 57301 Ph: 605-995-6120 www.innovsys.com -------------- next part -------------- A non-text attachment was scrubbed... Name: WallClock-fix Type: application/octet-stream Size: 3451 bytes Desc: WallClock-fix Url : http://ozlabs.org/pipermail/linuxppc-embedded/attachments/20050809/a63c7e89/attachment.obj