diff -urN linux/arch/ppc/kernel/apus_setup.c bk/arch/ppc/kernel/apus_setup.c --- linux/arch/ppc/kernel/apus_setup.c Wed Apr 26 10:58:46 2000 +++ bk/arch/ppc/kernel/apus_setup.c Wed Aug 16 12:50:39 2000 @@ -304,7 +304,7 @@ void apus_calibrate_decr(void) { #ifdef CONFIG_APUS - int freq, divisor; + unsigned long freq; /* This algorithm for determining the bus speed was contributed by Ralph Schmidt. */ @@ -335,8 +335,8 @@ bus_speed = 60; freq = 15000000; } else if ((bus_speed >= 63) && (bus_speed < 69)) { - bus_speed = 66; - freq = 16500000; + bus_speed = 67; + freq = 16666667; } else { printk ("APUS: Unable to determine bus speed (%d). " "Defaulting to 50MHz", bus_speed); @@ -375,12 +375,10 @@ } - freq *= 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); __bus_speed = bus_speed; __speed_test_failed = speed_test_failed; diff -urN linux/arch/ppc/kernel/chrp_setup.c bk/arch/ppc/kernel/chrp_setup.c --- linux/arch/ppc/kernel/chrp_setup.c Fri Jul 14 14:41:35 2000 +++ bk/arch/ppc/kernel/chrp_setup.c Tue Aug 22 09:45:16 2000 @@ -62,6 +62,6 @@ unsigned long chrp_get_rtc_time(void); int chrp_set_rtc_time(unsigned long nowtime); void chrp_calibrate_decr(void); -void chrp_time_init(void); +long chrp_time_init(void); void chrp_setup_pci_ptrs(void); diff -urN linux/arch/ppc/kernel/chrp_time.c bk/arch/ppc/kernel/chrp_time.c --- linux/arch/ppc/kernel/chrp_time.c Fri Jul 14 14:41:35 2000 +++ bk/arch/ppc/kernel/chrp_time.c Tue Aug 22 09:45:16 2000 @@ -32,18 +32,20 @@ static int nvram_as0 = NVRAM_AS0; static int nvram_data = NVRAM_DATA; -void __init chrp_time_init(void) +long __init chrp_time_init(void) { struct device_node *rtcs; int base; rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); if (rtcs == NULL || rtcs->addrs == NULL) - return; + return 0; base = rtcs->addrs[0].address; nvram_as1 = 0; nvram_as0 = base; nvram_data = base + 1; + + return 0; } int __chrp chrp_cmos_clock_read(int addr) @@ -115,28 +117,34 @@ unsigned long __chrp chrp_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; - int i; + int uip, i; /* The Linux interpretation of the CMOS clock register contents: * When the Update-In-Progress (UIP) flag goes from 1 to 0, the * RTC registers show the second which has precisely just started. * Let's hope other operating systems interpret the RTC the same way. */ - /* read RTC exactly on falling edge of update flag */ - for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) - break; - for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) - break; - do { /* Isn't this overkill ? UIP above should guarantee consistency */ + + /* Since the UIP flag is set for about 2.2 ms and the clock + * is typically written with a precision of 1 jiffy, trying + * to obtain a precision better than a few milliseconds is + * an illusion. Only consistency is interesting, this also + * allows to use the routine for /dev/rtc without a potential + * 1 second kernel busy loop triggered by any reader of /dev/rtc. + */ + + for ( i = 0; i<1000000; i++) { + uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); sec = chrp_cmos_clock_read(RTC_SECONDS); min = chrp_cmos_clock_read(RTC_MINUTES); hour = chrp_cmos_clock_read(RTC_HOURS); day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); mon = chrp_cmos_clock_read(RTC_MONTH); year = chrp_cmos_clock_read(RTC_YEAR); - } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); + uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); + if ((uip & RTC_UIP)==0) break; + } + if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(sec); @@ -155,8 +163,7 @@ void __init chrp_calibrate_decr(void) { struct device_node *cpu; - int *fp, divisor; - unsigned long freq; + unsigned int freq, *fp; if (via_calibrate_decr()) return; @@ -168,15 +175,13 @@ freq = 16666000; /* hardcoded default */ cpu = find_type_devices("cpu"); if (cpu != 0) { - fp = (int *) get_property(cpu, "timebase-frequency", NULL); + fp = (unsigned int *) + get_property(cpu, "timebase-frequency", NULL); if (fp != 0) freq = *fp; } - freq *= 30; - divisor = 30; - printk("time_init: decrementer frequency = %lu/%d (%ld MHz)\n", freq, - divisor, (freq/divisor)>>20); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); } diff -urN linux/arch/ppc/kernel/gemini_setup.c bk/arch/ppc/kernel/gemini_setup.c --- linux/arch/ppc/kernel/gemini_setup.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/gemini_setup.c Tue Aug 22 09:45:16 2000 @@ -335,7 +335,7 @@ #define gemini_rtc_write(val,x) (writeb((val),(GEMINI_RTC+(x)))) /* ensure that the RTC is up and running */ -void __init gemini_time_init(void) +long __init gemini_time_init(void) { unsigned char reg; @@ -346,6 +346,7 @@ gemini_rtc_write((reg & ~(M48T35_RTC_STOPPED)), M48T35_RTC_CONTROL); gemini_rtc_write((reg | M48T35_RTC_SET), M48T35_RTC_CONTROL); } + return 0; } #undef DEBUG_RTC diff -urN linux/arch/ppc/kernel/m8260_setup.c bk/arch/ppc/kernel/m8260_setup.c --- linux/arch/ppc/kernel/m8260_setup.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/m8260_setup.c Sat Jul 29 17:59:38 2000 @@ -112,8 +112,8 @@ bd_t *binfo = (bd_t *)__res; int freq, divisor; - freq = (binfo->bi_intfreq * 1000000); - divisor = 16; + freq = (binfo->bi_busfreq * 1000000); + divisor = 4; decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff -urN linux/arch/ppc/kernel/oak_setup.c bk/arch/ppc/kernel/oak_setup.c --- linux/arch/ppc/kernel/oak_setup.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/oak_setup.c Tue Aug 22 09:45:16 2000 @@ -231,10 +231,11 @@ /* * Document me. */ -void __init +long __init oak_time_init(void) { /* XXX - Implement me */ + return 0; } /* diff -urN linux/arch/ppc/kernel/pmac_setup.c bk/arch/ppc/kernel/pmac_setup.c --- linux/arch/ppc/kernel/pmac_setup.c Wed Aug 2 15:20:18 2000 +++ bk/arch/ppc/kernel/pmac_setup.c Tue Aug 29 09:48:33 2000 @@ -68,7 +68,7 @@ #undef SHOW_GATWICK_IRQS -extern void pmac_time_init(void); +extern long pmac_time_init(void); extern unsigned long pmac_get_rtc_time(void); extern int pmac_set_rtc_time(unsigned long nowtime); extern void pmac_read_rtc_time(void); diff -urN linux/arch/ppc/kernel/prep_setup.c bk/arch/ppc/kernel/prep_setup.c --- linux/arch/ppc/kernel/prep_setup.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/prep_setup.c Fri Aug 25 17:10:27 2000 @@ -365,14 +365,13 @@ */ void __init prep_res_calibrate_decr(void) { - int freq, divisor; + unsigned long freq, divisor=4; freq = res->VitalProductData.ProcessorBusHz; - divisor = 4; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + (freq/divisor)/1000000, (freq/divisor)%1000000); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); } /* @@ -381,32 +380,30 @@ * but on prep we have to figure it out. * -- Cort */ -int calibrate_done = 0; -volatile int *done_ptr = &calibrate_done; +/* Done with 3 interrupts: the first one primes the cache and the + * 2 following ones measure the interval. The precision of the method + * is still doubtful due to the short interval sampled. + */ +static __initdata volatile int calibrate_steps = 3; +static __initdata unsigned tbstamp; void __init prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs *regs) { - unsigned long freq, divisor; - static unsigned long t1 = 0, t2 = 0; - - if ( !t1 ) - t1 = get_dec(); - else if (!t2) - { - t2 = get_dec(); - t2 = t1-t2; /* decr's in 1/HZ */ - t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ - freq = t2 * 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", - freq, divisor,t2>>20); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; - *done_ptr = 1; + unsigned long t, freq; + int step=--calibrate_steps; + + t = get_tbl(); + if (step > 0) { + tbstamp = t; + } else { + freq = (t - tbstamp)*HZ; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); } } @@ -428,17 +425,43 @@ if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) panic("Could not allocate timer IRQ!"); __sti(); - while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ + while ( calibrate_steps ) /* nothing */; /* wait for calibrate */ restore_flags(flags); free_irq( 0, NULL); } -/* We use the NVRAM RTC to time a second to calibrate the decrementer. */ +static long __init mk48t59_init(void) { + unsigned char tmp; + + tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + if (tmp & MK48T59_RTC_CB_STOP) { + printk("Warning: RTC was stopped, date will be wrong.\n"); + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLB, + tmp & ~MK48T59_RTC_CB_STOP); + /* Low frequency crystal oscillators may take a very long + * time to startup and stabilize. For now just ignore the + * the issue, but attempting to calibrate the decrementer + * from the RTC just after this wakeup is likely to be very + * inaccurate. Firmware should not allow to load + * the OS with the clock stopped anyway... + */ + } + /* Ensure that the clock registers are updated */ + tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + tmp &= ~(MK48T59_RTC_CA_READ | MK48T59_RTC_CA_WRITE); + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, tmp); + return 0; +} + +/* We use the NVRAM RTC to time a second to calibrate the decrementer, + * the RTC registers have just been set up in the right state by the + * preceding routine. + */ void __init mk48t59_calibrate_decr(void) { - unsigned long freq, divisor; - unsigned long t1, t2; + unsigned long freq; + unsigned long t1; unsigned char save_control; long i; unsigned char sec; @@ -458,29 +481,31 @@ /* Read the seconds value to see when it changes. */ sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + /* Actually this is bad for precision, we should have a loop in + * which we only read the seconds counter. nvram_read_val writes + * the address bytes on every call and this takes a lot of time. + * Perhaps an nvram_wait_change method returning a time + * stamp with a loop count as parameter would be the solution. + */ for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + t1 = get_tbl(); if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { break; } } - t1 = get_dec(); sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); for (i = 0 ; i < 1000000 ; i++) { /* Should take up 1 second... */ + freq = get_tbl()-t1; if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { break; } } - t2 = t1 - get_dec(); - - freq = t2 * 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", - freq, divisor,t2>>20); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); } void __prep @@ -788,6 +813,7 @@ { ppc_md.set_rtc_time = mk48t59_set_rtc_time; ppc_md.get_rtc_time = mk48t59_get_rtc_time; + ppc_md.time_init = mk48t59_init; } else { @@ -808,6 +834,7 @@ ppc_md.set_rtc_time = mk48t59_set_rtc_time; ppc_md.get_rtc_time = mk48t59_get_rtc_time; ppc_md.calibrate_decr = mk48t59_calibrate_decr; + ppc_md.time_init = mk48t59_init; } #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) diff -urN linux/arch/ppc/kernel/prep_time.c bk/arch/ppc/kernel/prep_time.c --- linux/arch/ppc/kernel/prep_time.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/prep_time.c Fri Aug 18 19:14:11 2000 @@ -99,28 +99,34 @@ unsigned long mc146818_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; - int i; + int uip, i; /* The Linux interpretation of the CMOS clock register contents: * When the Update-In-Progress (UIP) flag goes from 1 to 0, the * RTC registers show the second which has precisely just started. * Let's hope other operating systems interpret the RTC the same way. */ - /* read RTC exactly on falling edge of update flag */ - for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) - break; - for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) - break; - do { /* Isn't this overkill ? UIP above should guarantee consistency */ + + /* Since the UIP flag is set for about 2.2 ms and the clock + * is typically written with a precision of 1 jiffy, trying + * to obtain a precision better than a few milliseconds is + * an illusion. Only consistency is interesting, this also + * allows to use the routine for /dev/rtc without a potential + * 1 second kernel busy loop triggered by any reader of /dev/rtc. + */ + + for ( i = 0; i<1000000; i++) { + uip = CMOS_READ(RTC_FREQ_SELECT); sec = CMOS_READ(RTC_SECONDS); min = CMOS_READ(RTC_MINUTES); hour = CMOS_READ(RTC_HOURS); day = CMOS_READ(RTC_DAY_OF_MONTH); mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); + uip |= CMOS_READ(RTC_FREQ_SELECT); + if ((uip & RTC_UIP)==0) break; + } + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { @@ -177,28 +183,11 @@ { unsigned char save_control; unsigned int year, mon, day, hour, min, sec; - int i; - /* Make sure the time is not stopped. */ - save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); - - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, - (save_control & (~MK48T59_RTC_CB_STOP))); - - /* Now make sure the read bit is off so the value will change. */ + /* Simple: freeze the clock, read it and allow updates again */ save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); save_control &= ~MK48T59_RTC_CA_READ; ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); - - /* Read the seconds value to see when it changes. */ - sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); - - /* Wait until the seconds value changes, then read the value. */ - for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ - if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { - break; - } - } /* Set the register to read the value. */ ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, diff -urN linux/arch/ppc/kernel/time.c bk/arch/ppc/kernel/time.c --- linux/arch/ppc/kernel/time.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/time.c Fri Aug 25 17:10:27 2000 @@ -6,6 +6,27 @@ * Paul Mackerras' version and mine for PReP and Pmac. * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). * + * First round of bugfixes by Gabriel Paubert (paubert@iram.es) + * to make clock more stable (2.4.0-test5). The only thing + * that this code assumes is that the timebases have been synchronized + * by firmware on SMP and are never stopped (never do sleep + * on SMP then, nap and doze are OK). + * + * TODO (not necessarily in this file): + * - improve precision and reproducibility of timebase frequency + * measurement at boot time. + * - get rid of xtime_lock for gettimeofday (generic kernel problem + * to be implemented on all architectures for SMP scalability and + * eventually implementing gettimeofday without entering the kernel). + * - put all time/clock related variables in a single structure + * to minimize number of cache lines touched by gettimeofday() + * - for astronomical applications: add a new function to get + * non ambiguous timestamps even around leap seconds. This needs + * a new timestamp format and a good name. + * + * + * The following comment is partially obsolete (at least the long wait + * is no more a valid reason): * Since the MPC8xx has a programmable interrupt timer, I decided to * use that rather than the decrementer. Two reasons: 1.) the clock * frequency is low, causing 2.) a long wait in the timer interrupt @@ -49,18 +70,32 @@ void smp_local_timer_interrupt(struct pt_regs *); /* keep track of when we need to update the rtc */ -time_t last_rtc_update = 0; +time_t last_rtc_update; extern rwlock_t xtime_lock; /* The decrementer counts down by 128 every 128ns on a 601. */ #define DECREMENTER_COUNT_601 (1000000000 / HZ) -#define COUNT_PERIOD_NUM_601 1 -#define COUNT_PERIOD_DEN_601 1000 -unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ -unsigned count_period_num; /* 1 decrementer count equals */ -unsigned count_period_den; /* count_period_num / count_period_den us */ -unsigned long last_tb; +unsigned tb_ticks_per_jiffy; +unsigned tb_to_us; +unsigned tb_last_stamp; + +extern unsigned long wall_jiffies; + +static long time_offset; + +/* Timer interrupt helper function */ +static inline int tb_delta(unsigned *jiffy_stamp) { + int delta; + if (__USE_RTC()) { + delta = get_rtcl(); + if (delta < *jiffy_stamp) *jiffy_stamp -= 1000000000; + delta -= *jiffy_stamp; + } else { + delta = get_tbl() - *jiffy_stamp; + } + return delta; +} /* * timer_interrupt - gets called when the decrementer overflows, @@ -69,88 +104,56 @@ */ int timer_interrupt(struct pt_regs * regs) { - int dval, d; -#if 0 - unsigned long flags; -#endif + int next_dec; unsigned long cpu = smp_processor_id(); - + unsigned jiffy_stamp = last_jiffy_stamp(cpu); + hardirq_enter(cpu); -#ifdef CONFIG_SMP - { - unsigned int loops = 100000000; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* CONFIG_SMP */ - dval = get_dec(); - /* - * Wait for the decrementer to change, then jump - * in and add decrementer_count to its value - * (quickly, before it changes again!) - */ - while ((d = get_dec()) == dval) - ; - asm volatile("mftb %0" : "=r" (last_tb) ); - /* - * Don't play catchup between the call to time_init() - * and sti() in init/main.c. - * - * This also means if we're delayed for > HZ - * we lose those ticks. If we're delayed for > HZ - * then we have something wrong anyway, though. - * - * -- Cort - */ - if ( d < (-1*decrementer_count) ) - d = 0; - set_dec(d + decrementer_count); - if ( !smp_processor_id() ) - { + do { + jiffy_stamp += tb_ticks_per_jiffy; + if (smp_processor_id()) continue; + /* We are in an interrupt, no need to save/restore flags */ + write_lock(&xtime_lock); + tb_last_stamp = jiffy_stamp; do_timer(regs); -#if 0 - /* -- BenH -- I'm removing this for now since it can cause various - * troubles with local-time RTCs. Now that we have a - * /dev/rtc that uses ppc_md.set_rtc_time() on mac, it - * should be possible to program the RTC from userland - * in all cases. - */ + /* - * update the rtc when needed + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. */ - read_lock_irqsave(&xtime_lock, flags); - if ( (time_status & STA_UNSYNC) && - ((xtime.tv_sec > last_rtc_update + 60) || - (xtime.tv_sec < last_rtc_update)) ) - { - if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) + last_rtc_update = xtime.tv_sec+1; else - /* do it again in 60 s */ - last_rtc_update = xtime.tv_sec; + /* Try again one minute later */ + last_rtc_update += 60; } - read_unlock_irqrestore(&xtime_lock, flags); -#endif - } + write_unlock(&xtime_lock); + } while((next_dec = tb_ticks_per_jiffy - tb_delta(&jiffy_stamp)) < 0); + set_dec(next_dec); + last_jiffy_stamp(cpu) = jiffy_stamp; + #ifdef CONFIG_SMP smp_local_timer_interrupt(regs); #endif - if ( ppc_md.heartbeat && !ppc_md.heartbeat_count--) + if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) ppc_md.heartbeat(); hardirq_exit(cpu); @@ -162,74 +165,138 @@ */ void do_gettimeofday(struct timeval *tv) { - unsigned long flags, diff; + unsigned long flags; + unsigned delta, lost_ticks, usec, sec; - save_flags(flags); - cli(); read_lock_irqsave(&xtime_lock, flags); - *tv = xtime; + sec = xtime.tv_sec; + usec = xtime.tv_usec; + delta = tb_ticks_since(tb_last_stamp); +#ifdef CONFIG_SMP + /* As long as timebases are not in sync, gettimeofday can only + * have jiffy resolution on SMP. + */ + if (_machine != _MACH_Pmac) + delta = 0; +#endif /* CONFIG_SMP */ + lost_ticks = jiffies - wall_jiffies; read_unlock_irqrestore(&xtime_lock, flags); - /* XXX we don't seem to have the decrementers synced properly yet */ -#ifndef CONFIG_SMP - asm volatile("mftb %0" : "=r" (diff) ); - diff -= last_tb; - tv->tv_usec += diff * count_period_num / count_period_den; - tv->tv_sec += tv->tv_usec / 1000000; - tv->tv_usec = tv->tv_usec % 1000000; -#endif - - restore_flags(flags); + + usec += mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta); + while (usec > 1000000) { + sec++; + usec -= 1000000; + } + tv->tv_sec = sec; + tv->tv_usec = usec; } void do_settimeofday(struct timeval *tv) { unsigned long flags; - int frac_tick; - - last_rtc_update = 0; /* so the rtc gets updated soon */ - - frac_tick = tv->tv_usec % (1000000 / HZ); - save_flags(flags); - cli(); + int tb_delta, new_usec, new_sec; + write_lock_irqsave(&xtime_lock, flags); - xtime.tv_sec = tv->tv_sec; - xtime.tv_usec = tv->tv_usec - frac_tick; - write_unlock_irqrestore(&xtime_lock, flags); - set_dec(frac_tick * count_period_den / count_period_num); + /* Updating the RTC is not the job of this code. If the time is + * stepped under NTP, the RTC will be update after STA_UNSYNC + * is cleared. Tool like clock/hwclock either copy the RTC + * to the system time, in which case there is no point in writing + * to the RTC again, or write to the RTC but then they don't call + * settimeofday to perform this operation. Note also that + * we don't touch the decrementer since: + * a) it would lose timer interrupt synchronization on SMP + * (if it is working one day) + * b) it could make one jiffy spuriously shorter or longer + * which would introduce another source of uncertainty potentially + * harmful to relatively short timers. + */ + + /* This works perfectly on SMP only if the tb are in sync but + * guarantees an error < 1 jiffy even if they are off by eons, + * still reasonable when gettimeofday resolution is 1 jiffy. + */ + tb_delta = tb_ticks_since(last_jiffy_stamp(smp_processor_id())); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + new_sec = tv->tv_sec; + new_usec = tv->tv_usec - mulhwu(tb_to_us, tb_delta); + while (new_usec <0) { + new_sec--; + new_usec += 1000000; + } + xtime.tv_usec = new_usec; + xtime.tv_sec = new_sec; + + /* In case of a large backwards jump in time with NTP, we want the + * clock to be updated as soon as the PLL is again in lock. + */ + last_rtc_update = new_sec - 658; + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - restore_flags(flags); + write_unlock_irqrestore(&xtime_lock, flags); } void __init time_init(void) { + time_t sec, old_sec; + unsigned old_stamp, stamp, elapsed; + /* This function is only called on the boot processor */ unsigned long flags; + if (ppc_md.time_init != NULL) - { - ppc_md.time_init(); - } + time_offset = ppc_md.time_init(); - if ((_get_PVR() >> 16) == 1) { + if (__USE_RTC()) { /* 601 processor: dec counts down by 128 every 128ns */ - decrementer_count = DECREMENTER_COUNT_601; - count_period_num = COUNT_PERIOD_NUM_601; - count_period_den = COUNT_PERIOD_DEN_601; - } else if (!smp_processor_id()) { + tb_ticks_per_jiffy = DECREMENTER_COUNT_601; + /* mulhwu_scale_factor(1000000000, 1000000) is 0x418937 */ + tb_to_us = 0x418937; + } else { ppc_md.calibrate_decr(); } + /* Now that the decrementer is calibrated, it can be used in case the + * clock is stuck, but the fact that we have to handle the 601 + * makes things more complex. Repeatedly read the RTC until the + * next second boundary to try to achieve some precision... + */ + stamp = get_native_tbl(); + sec = ppc_md.get_rtc_time(); + elapsed = 0; + do { + old_stamp = stamp; + old_sec = sec; + stamp = get_native_tbl(); + if (__USE_RTC() && stamp < old_stamp) old_stamp -= 1000000000; + elapsed += stamp - old_stamp; + sec = ppc_md.get_rtc_time(); + } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy); + if (sec==old_sec) { + printk("Warning: real time clock seems stuck!\n"); + } write_lock_irqsave(&xtime_lock, flags); - xtime.tv_sec = ppc_md.get_rtc_time(); + xtime.tv_sec = sec; + last_jiffy_stamp(0) = tb_last_stamp = stamp; xtime.tv_usec = 0; + /* No update now, we just read the time from the RTC ! */ + last_rtc_update = xtime.tv_sec; write_unlock_irqrestore(&xtime_lock, flags); + /* Not exact, but the timer interrupt takes care of this */ + set_dec(tb_ticks_per_jiffy); - set_dec(decrementer_count); - /* allow setting the time right away */ - last_rtc_update = 0; + /* If platform provided a timezone (pmac), we correct the time + * using do_sys_settimeofday() which in turn calls warp_clock() + */ + if (time_offset) { + struct timezone tz; + tz.tz_minuteswest = -time_offset / 60; + tz.tz_dsttime = 0; + do_sys_settimeofday(NULL, &tz); + } } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. @@ -354,3 +421,31 @@ */ GregorianDay(tm); } + +/* Auxiliary function to compute scaling factors */ +/* Actually the choice of a timebase running at 1/4 the of the bus + * frequency giving resolution of a few tens of nanoseconds is quite nice. + * It makes this computation very precise (27-28 bits typically) which + * is optimistic considering the stability of most processor clock + * oscillators and the precision with which the timebase frequency + * is measured but does not harm. + */ +unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { + unsigned mlt=0, tmp, err; + /* No concern for performance, it's done once: use a stupid + * but safe and compact method to find the multiplier. + */ + for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { + if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; + } + /* We might still be off by 1 for the best approximation. + * A side effect of this is that if outscale is too large + * the returned value will be zero. + * Many corner cases have been checked and seem to work, + * some might have been forgotten in the test however. + */ + err = inscale*(mlt+1); + if (err <= inscale/2) mlt++; + return mlt; +} + diff -urN linux/arch/ppc/kernel/walnut_setup.c bk/arch/ppc/kernel/walnut_setup.c --- linux/arch/ppc/kernel/walnut_setup.c Fri Jul 14 14:41:36 2000 +++ bk/arch/ppc/kernel/walnut_setup.c Tue Aug 22 09:45:16 2000 @@ -226,10 +226,11 @@ /* * Document me. */ -void __init +long __init walnut_time_init(void) { /* XXX - Implement me */ + return 0; } /* diff -urN linux/include/asm-ppc/hardirq.h bk/include/asm-ppc/hardirq.h --- linux/include/asm-ppc/hardirq.h Thu Aug 10 14:07:02 2000 +++ bk/include/asm-ppc/hardirq.h Fri Aug 18 19:14:12 2000 @@ -5,16 +5,23 @@ #include /* entry.S is sensitive to the offsets of these fields */ +/* The __last_jiffy_stamp field is needed to ensure that no decrementer + * interrupt is lost on SMP machines. Since on most CPUs it is in the same + * cache line as local_irq_count, it is cheap to access and is also used on UP + * for uniformity. + */ typedef struct { unsigned int __softirq_active; unsigned int __softirq_mask; unsigned int __local_irq_count; unsigned int __local_bh_count; unsigned int __syscall_count; + unsigned int __last_jiffy_stamp; } ____cacheline_aligned irq_cpustat_t; #include /* Standard mappings for irq_cpustat_t above */ +#define last_jiffy_stamp(cpu) __IRQ_STAT((cpu), __last_jiffy_stamp) /* * Are we in an interrupt context? Either doing bottom half * or hardware interrupt processing? diff -urN linux/include/asm-ppc/machdep.h bk/include/asm-ppc/machdep.h --- linux/include/asm-ppc/machdep.h Wed Aug 2 15:20:37 2000 +++ bk/include/asm-ppc/machdep.h Tue Aug 22 09:45:16 2000 @@ -31,7 +31,7 @@ void (*power_off)(void); void (*halt)(void); - void (*time_init)(void); /* Optional, may be NULL */ + long (*time_init)(void); /* Optional, may be NULL */ int (*set_rtc_time)(unsigned long nowtime); unsigned long (*get_rtc_time)(void); void (*calibrate_decr)(void); diff -urN linux/include/asm-ppc/time.h bk/include/asm-ppc/time.h --- linux/include/asm-ppc/time.h Fri Jul 14 14:41:50 2000 +++ bk/include/asm-ppc/time.h Thu Aug 17 12:37:59 2000 @@ -12,9 +12,10 @@ #include /* time.c */ -extern unsigned decrementer_count; -extern unsigned count_period_num; -extern unsigned count_period_den; +extern unsigned tb_ticks_per_jiffy; +extern unsigned tb_to_us; +extern unsigned tb_last_stamp; + extern unsigned long mktime(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); extern void to_tm(int tim, struct rtc_time * tm); @@ -40,3 +41,73 @@ mtspr(SPRN_DEC, val); #endif } + +/* Accessor functions for the timebase (RTC on 601) registers. */ +/* If one day CONFIG_POWER is added just define __USE_RTC as 1 */ +#ifdef CONFIG_6xx +extern __inline__ int const __USE_RTC(void) { + return (mfspr(SPRN_PVR)>>16) == 1; +} +#else +#define __USE_RTC() 0 +#endif + +extern __inline__ unsigned long get_tbl(void) { + unsigned long tbl; + asm volatile("mftb %0" : "=r" (tbl)); + return tbl; +} + +extern __inline__ unsigned long get_rtcl(void) { + unsigned long rtcl; + asm volatile("mfrtcl %0" : "=r" (rtcl)); + return rtcl; +} + +extern __inline__ unsigned get_native_tbl(void) { + if (__USE_RTC()) + return get_rtcl(); + else + return get_tbl(); +} + +/* On machines with RTC, this function can only be used safely + * after the timestamp and for 1 second. It is only used by gettimeofday + * however so it should not matter. + */ +extern __inline__ unsigned tb_ticks_since(unsigned tstamp) { + if (__USE_RTC()) { + int delta = get_rtcl() - tstamp; + return delta<0 ? delta + 1000000000 : delta; + } else { + return get_tbl() - tstamp; + } +} + +#if 0 +extern __inline__ unsigned long get_bin_rtcl(void) { + unsigned long rtcl, rtcu1, rtcu2; + asm volatile("\ +1: mfrtcu %0\n\ + mfrtcl %1\n\ + mfrtcu %2\n\ + cmpw %0,%2\n\ + bne- 1b\n" + : "=r" (rtcu1), "=r" (rtcl), "=r" (rtcu2) + : : "cr0"); + return rtcu2*1000000000+rtcl; +} + +extern __inline__ unsigned binary_tbl(void) { + if (__USE_RTC()) + return get_bin_rtcl(); + else + return get_tbl(); +} +#endif + +/* Use mulhwu to scale processor timebase to timeval */ +#define mulhwu(x,y) \ +({unsigned z; asm ("mulhwu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;}) + +unsigned mulhwu_scale_factor(unsigned, unsigned); diff -urN linux/arch/ppc/kernel/ppc_ksyms.c bk/arch/ppc/kernel/ppc_ksyms.c --- linux/arch/ppc/kernel/ppc_ksyms.c Thu Aug 10 14:06:27 2000 +++ bk/arch/ppc/kernel/ppc_ksyms.c Thu Aug 31 20:15:23 2000 @@ -288,7 +288,7 @@ EXPORT_SYMBOL(irq_desc); void ppc_irq_dispatch_handler(struct pt_regs *, int); EXPORT_SYMBOL(ppc_irq_dispatch_handler); -EXPORT_SYMBOL(decrementer_count); +EXPORT_SYMBOL(tb_ticks_per_jiffy); EXPORT_SYMBOL(get_wchan); EXPORT_SYMBOL(console_drivers); EXPORT_SYMBOL(console_lock);