📄 time.c
字号:
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_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; delta_xsec = mulhdu( (tb_last_stamp-naca->tb_orig_stamp), naca->tb_to_xs ); new_xsec = (new_usec * XSEC_PER_SEC) / USEC_PER_SEC; new_xsec += new_sec * XSEC_PER_SEC; if ( new_xsec > delta_xsec ) { naca->stamp_xsec = new_xsec - delta_xsec; } else { /* This is only for the case where the user is setting the time * way back to a time such that the boot time would have been * before 1970 ... eg. we booted ten days ago, and we are * setting the time to Jan 5, 1970 */ naca->stamp_xsec = new_xsec; naca->tb_orig_stamp = tb_last_stamp; } naca->tz_minuteswest = sys_tz.tz_minuteswest; naca->tz_dsttime = sys_tz.tz_dsttime; write_unlock_irqrestore(&xtime_lock, flags);}/* * This function is a copy of the architecture independent function * but which calls do_settimeofday rather than setting the xtime * fields itself. This way, the fields which are used for * do_settimeofday get updated too. */long ppc64_sys32_stime(int* tptr){ int value; struct timeval myTimeval; if (!capable(CAP_SYS_TIME)) return -EPERM; if (get_user(value, tptr)) return -EFAULT; myTimeval.tv_sec = value; myTimeval.tv_usec = 0; do_settimeofday(&myTimeval); return 0;}/* * This function is a copy of the architecture independent function * but which calls do_settimeofday rather than setting the xtime * fields itself. This way, the fields which are used for * do_settimeofday get updated too. */long ppc64_sys_stime(long* tptr){ long value; struct timeval myTimeval; if (!capable(CAP_SYS_TIME)) return -EPERM; if (get_user(value, tptr)) return -EFAULT; myTimeval.tv_sec = value; myTimeval.tv_usec = 0; do_settimeofday(&myTimeval); return 0;}void __init time_init(void){ /* This function is only called on the boot processor */ unsigned long flags; struct rtc_time tm; ppc_md.calibrate_decr(); if ( ! piranha_simulator ) { ppc_md.get_boot_time(&tm); } write_lock_irqsave(&xtime_lock, flags); xtime.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); tb_last_stamp = get_tb(); naca->tb_orig_stamp = tb_last_stamp; naca->tb_update_count = 0; naca->tb_ticks_per_sec = tb_ticks_per_sec; naca->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC; naca->tb_to_xs = tb_to_xs; xtime_sync_interval = tb_ticks_per_sec - (tb_ticks_per_sec/8); next_xtime_sync_tb = tb_last_stamp + xtime_sync_interval; time_freq = 0; xtime.tv_usec = 0; 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); /* This horrible hack gives setup a hook just before console_init */ setup_before_console_init();}/* * After adjtimex is called, adjust the conversion of tb ticks * to microseconds to keep do_gettimeofday synchronized * with ntpd. * * Use the time_adjust, time_freq and time_offset computed by adjtimex to * adjust the frequency. *//* #define DEBUG_PPC_ADJTIMEX 1 */void ppc_adjtimex(void){ unsigned long den, new_tb_ticks_per_sec, tb_ticks, old_xsec, new_tb_to_xs, new_xsec, new_stamp_xsec; unsigned long tb_ticks_per_sec_delta; long delta_freq, ltemp; struct div_result divres; unsigned long flags; long singleshot_ppm = 0; /* Compute parts per million frequency adjustment to accomplish the time adjustment implied by time_offset to be applied over the elapsed time indicated by time_constant. Use SHIFT_USEC to get it into the same units as time_freq. */ if ( time_offset < 0 ) { ltemp = -time_offset; ltemp <<= SHIFT_USEC - SHIFT_UPDATE; ltemp >>= SHIFT_KG + time_constant; ltemp = -ltemp; } else { ltemp = time_offset; ltemp <<= SHIFT_USEC - SHIFT_UPDATE; ltemp >>= SHIFT_KG + time_constant; } /* If there is a single shot time adjustment in progress */ if ( time_adjust ) {#ifdef DEBUG_PPC_ADJTIMEX printk("ppc_adjtimex: "); if ( adjusting_time == 0 ) printk("starting "); printk("single shot time_adjust = %ld\n", time_adjust);#endif adjusting_time = 1; /* Compute parts per million frequency adjustment to match time_adjust */ singleshot_ppm = tickadj * HZ; /* * The adjustment should be tickadj*HZ to match the code in * linux/kernel/timer.c, but experiments show that this is too * large. 3/4 of tickadj*HZ seems about right */ singleshot_ppm -= singleshot_ppm / 4; /* Use SHIFT_USEC to get it into the same units as time_freq */ singleshot_ppm <<= SHIFT_USEC; if ( time_adjust < 0 ) singleshot_ppm = -singleshot_ppm; } else {#ifdef DEBUG_PPC_ADJTIMEX if ( adjusting_time ) printk("ppc_adjtimex: ending single shot time_adjust\n");#endif adjusting_time = 0; } /* Add up all of the frequency adjustments */ delta_freq = time_freq + ltemp + singleshot_ppm; /* Compute a new value for tb_ticks_per_sec based on the frequency adjustment */ den = 1000000 * (1 << (SHIFT_USEC - 8)); if ( delta_freq < 0 ) { tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( (-delta_freq) >> (SHIFT_USEC - 8))) / den; new_tb_ticks_per_sec = tb_ticks_per_sec + tb_ticks_per_sec_delta; } else { tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( delta_freq >> (SHIFT_USEC - 8))) / den; new_tb_ticks_per_sec = tb_ticks_per_sec - tb_ticks_per_sec_delta; } #ifdef DEBUG_PPC_ADJTIMEX printk("ppc_adjtimex: ltemp = %ld, time_freq = %ld, singleshot_ppm = %ld\n", ltemp, time_freq, singleshot_ppm); printk("ppc_adjtimex: tb_ticks_per_sec - base = %ld new = %ld\n", tb_ticks_per_sec, new_tb_ticks_per_sec);#endif /* * Compute a new value of tb_to_xs (used to convert tb to microseconds * and a new value of stamp_xsec which is the time (in 1/2^20 second * units) corresponding to tb_orig_stamp. This new value of stamp_xsec * compensates for the change in frequency (implied by the new * tb_to_xs) and so guarantees that the current time remains the same * */ tb_ticks = get_tb() - naca->tb_orig_stamp; div128_by_32( 1024*1024, 0, new_tb_ticks_per_sec, &divres ); new_tb_to_xs = divres.result_low; new_xsec = mulhdu( tb_ticks, new_tb_to_xs ); write_lock_irqsave( &xtime_lock, flags ); old_xsec = mulhdu( tb_ticks, naca->tb_to_xs ); new_stamp_xsec = naca->stamp_xsec + old_xsec - new_xsec; /* * tb_update_count is used to allow the problem state gettimeofday code * to assure itself that it sees a consistent view of the tb_to_xs and * stamp_xsec variables. It reads the tb_update_count, then reads * tb_to_xs and stamp_xsec and then reads tb_update_count again. If * the two values of tb_update_count match and are even then the * tb_to_xs and stamp_xsec values are consistent. If not, then it * loops back and reads them again until this criteria is met. */ ++(naca->tb_update_count); wmb(); naca->tb_to_xs = new_tb_to_xs; naca->stamp_xsec = new_stamp_xsec; wmb(); ++(naca->tb_update_count); write_unlock_irqrestore( &xtime_lock, flags );}#define TICK_SIZE tick#define FEBRUARY 2#define STARTOFTIME 1970#define SECDAY 86400L#define SECYR (SECDAY * 365)#define leapyear(year) ((year) % 4 == 0)#define days_in_year(a) (leapyear(a) ? 366 : 365)#define days_in_month(a) (month_days[(a) - 1])static int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};/* * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) */void GregorianDay(struct rtc_time * tm){ int leapsToDate; int lastYear; int day; int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; lastYear=tm->tm_year-1; /* * Number of leap corrections to apply up to end of last year */ leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; /* * This year is a leap year if it is divisible by 4 except when it is * divisible by 100 unless it is divisible by 400 * * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be */ if((tm->tm_year%4==0) && ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && (tm->tm_mon>2)) { /* * We are past Feb. 29 in a leap year */ day=1; } else { day=0; } day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; tm->tm_wday=day%7;}void to_tm(int tim, struct rtc_time * tm){ register int i; register long hms, day; day = tim / SECDAY; hms = tim % SECDAY; /* Hours, minutes, seconds are easy */ tm->tm_hour = hms / 3600; tm->tm_min = (hms % 3600) / 60; tm->tm_sec = (hms % 3600) % 60; /* Number of years in days */ for (i = STARTOFTIME; day >= days_in_year(i); i++) day -= days_in_year(i); tm->tm_year = i; /* Number of months in days left */ if (leapyear(tm->tm_year)) days_in_month(FEBRUARY) = 29; for (i = 1; day >= days_in_month(i); i++) day -= days_in_month(i); days_in_month(FEBRUARY) = 28; tm->tm_mon = i; /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; /* * Determine the day of week */ GregorianDay(tm);}#if 0/* 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; }#endif/* * Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit * result. */void div128_by_32( unsigned long dividend_high, unsigned long dividend_low, unsigned divisor, struct div_result *dr ){ unsigned long a,b,c,d, w,x,y,z, ra,rb,rc; a = dividend_high >> 32; b = dividend_high & 0xffffffff; c = dividend_low >> 32; d = dividend_low & 0xffffffff; w = a/divisor; ra = (a - (w * divisor)) << 32; x = (ra + b)/divisor; rb = ((ra + b) - (x * divisor)) << 32; y = (rb + c)/divisor; rc = ((rb + b) - (y * divisor)) << 32; z = (rc + d)/divisor; dr->result_high = (w << 32) + x; dr->result_low = (y << 32) + z;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -