📄 time.c
字号:
/* $Id: time.c,v 1.17 2002/11/15 14:49:24 oskarp Exp $ * * linux/arch/cris/kernel/time.c * * Copyright (C) 1991, 1992, 1995 Linus Torvalds * Copyright (C) 1999, 2000, 2001, 2002 Axis Communications AB * * 1994-07-02 Alan Modra * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime * 1995-03-26 Markus Kuhn * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 * precision CMOS clock update * 1996-05-03 Ingo Molnar * fixed time warps in do_[slow|fast]_gettimeoffset() * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills * * Linux/CRIS specific code: * * Authors: Bjorn Wesen * Johan Adolfsson * 2002-03-04 Johan Adolfsson * Use prescale timer at 25000 Hz instead of the baudrate timer at * 19200 to get rid of the 64ppm to fast timer (and we get better * resolution within a jiffie as well. * 2002-03-05 Johan Adolfsson * Use prescaler in do_slow_gettimeoffset() to get 1 us resolution (40ns) * 2002-09-06 Johan Adolfsson * Handle lost ticks by checking wall_jiffies, more efficient code * by using local vars and not the pointer argument. * */#include <linux/errno.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/param.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/time.h>#include <linux/delay.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/delay.h>#include <asm/rtc.h>#include <linux/timex.h>#include <linux/config.h>#include <asm/svinto.h>#define CRIS_TEST_TIMERS 0static int have_rtc; /* used to remember if we have an RTC or not *//* define this if you need to use print_timestamp *//* it will make jiffies at 96 hz instead of 100 hz though */#undef USE_CASCADE_TIMERSextern int setup_etrax_irq(int, struct irqaction *);#define TICK_SIZE tickextern unsigned long wall_jiffies;/* The timers count from their initial value down to 1 * The R_TIMER0_DATA counts down when R_TIM_PRESC_STATUS reaches halv * of the divider value. */ unsigned long get_ns_in_jiffie(void){ unsigned char timer_count, t1; unsigned short presc_count; unsigned long ns; unsigned long flags; save_flags(flags); cli(); timer_count = *R_TIMER0_DATA; presc_count = *R_TIM_PRESC_STATUS; /* presc_count might be wrapped */ t1 = *R_TIMER0_DATA; if (timer_count != t1){ /* it wrapped, read prescaler again... */ presc_count = *R_TIM_PRESC_STATUS; timer_count = t1; } restore_flags(flags); if (presc_count >= PRESCALE_VALUE/2 ){ presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2; } else { presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2; } ns = ( (TIMER0_DIV - timer_count) * ((1000000000/HZ)/TIMER0_DIV )) + ( (presc_count) * (1000000000/PRESCALE_FREQ)); return ns;}#if CRIS_TEST_TIMERS #define NS_TEST_SIZE 4000static unsigned long ns_test[NS_TEST_SIZE];void cris_test_timers(void){ int i;#if 0 for (i = 0; i < NS_TEST_SIZE; i++) { ns_test[i] = *R_TIMER0_DATA | (*R_TIM_PRESC_STATUS<<16); } for (i = 1; i < NS_TEST_SIZE; i++) { printk("%4i. %lu %lu %09lu ns \n", i, ns_test[i]&0x0FFFF, (ns_test[i]>>16), get_ns_in_jiffie_from_data(ns_test[i]&0x0FFFF, ns_test[i]>>16)); }#else for (i = 0; i < NS_TEST_SIZE; i++) { ns_test[i] = get_ns_in_jiffie(); } for (i = 1; i < NS_TEST_SIZE; i++) { printk("%4i. %09lu ns diff %li ns\n", i, ns_test[i], ns_test[i]- ns_test[i-1]); }#endif}#endifstatic unsigned long do_slow_gettimeoffset(void){ unsigned long count, t1; unsigned long usec_count = 0; unsigned short presc_count; static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ static unsigned long jiffies_p = 0; /* * cache volatile jiffies temporarily; we have IRQs turned off. */ unsigned long jiffies_t; /* The timer interrupt comes from Etrax timer 0. In order to get * better precision, we check the current value. It might have * underflowed already though. */#ifndef CONFIG_SVINTO_SIM /* Not available in the xsim simulator. */ count = *R_TIMER0_DATA; presc_count = *R_TIM_PRESC_STATUS; /* presc_count might be wrapped */ t1 = *R_TIMER0_DATA; if (count != t1){ /* it wrapped, read prescaler again... */ presc_count = *R_TIM_PRESC_STATUS; count = t1; }#else count = 0; presc_count = 0;#endif jiffies_t = jiffies; /* * avoiding timer inconsistencies (they are rare, but they happen)... * there are one problem that must be avoided here: * 1. the timer counter underflows */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { /* Timer wrapped, use new count and prescale * increase the time corresponding to one jiffie */ usec_count = 1000000/HZ; } } else jiffies_p = jiffies_t; count_p = count; if (presc_count >= PRESCALE_VALUE/2 ){ presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2; } else { presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2; } /* Convert timer value to usec */ usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) + (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000); return usec_count;}#define do_gettimeoffset() do_slow_gettimeoffset()/* * This version of gettimeofday has near microsecond resolution. */void do_gettimeofday(struct timeval *tv){ unsigned long flags; unsigned long usec, sec; save_flags(flags); cli(); usec = do_gettimeoffset(); { unsigned long lost = jiffies - wall_jiffies; if (lost) usec += lost * (1000000 / HZ); } sec = xtime.tv_sec; usec += xtime.tv_usec; restore_flags(flags); while (usec >= 1000000) { usec -= 1000000; sec++; } tv->tv_sec = sec; tv->tv_usec = usec;}void do_settimeofday(struct timeval *tv){ unsigned long flags; signed long new_usec, new_sec; save_flags(flags); cli(); /* This is revolting. We need to set the xtime.tv_usec * correctly. However, the value in this location is * is value at the last tick. * Discover what correction gettimeofday * would have done, and then undo it! */ new_usec = tv->tv_usec; new_usec -= do_gettimeoffset(); new_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); new_sec = tv->tv_sec; while (new_usec < 0) { new_usec += 1000000; new_sec--; } xtime.tv_sec = new_sec; xtime.tv_usec = new_usec; 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);}/* * BUG: This routine does not handle hour overflow properly; it just * sets the minutes. Usually you'll only notice that after reboot! */static int set_rtc_mmss(unsigned long nowtime){ int retval = 0; int real_seconds, real_minutes, cmos_minutes; printk("set_rtc_mmss(%lu)\n", nowtime); if(!have_rtc) return 0; cmos_minutes = CMOS_READ(RTC_MINUTES); BCD_TO_BIN(cmos_minutes); /* * since we're only adjusting minutes and seconds, * don't interfere with hour overflow. This avoids * messing with unknown time zones but requires your * RTC not to be off by more than 15 minutes */ real_seconds = nowtime % 60; real_minutes = nowtime / 60; if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) real_minutes += 30; /* correct for half hour time zone */ real_minutes %= 60; if (abs(real_minutes - cmos_minutes) < 30) { BIN_TO_BCD(real_seconds); BIN_TO_BCD(real_minutes); CMOS_WRITE(real_seconds,RTC_SECONDS); CMOS_WRITE(real_minutes,RTC_MINUTES); } else { printk(KERN_WARNING
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -