⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 2 页
字号:
	tv->tv_usec = usec;}void do_settimeofday(struct timeval *tv){	write_lock_irq(&xtime_lock);	/*	 * This is revolting. We need to set "xtime" correctly. However, the	 * value in this location is the value at the most recent update of	 * wall time.  Discover what correction gettimeofday() would have	 * made, and then undo it!	 */	tv->tv_usec -= do_gettimeoffset();	tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);	while (tv->tv_usec < 0) {		tv->tv_usec += 1000000;		tv->tv_sec--;	}	xtime = *tv;	time_adjust = 0;		/* stop active adjtime() */	time_status |= STA_UNSYNC;	time_maxerror = NTP_PHASE_LIMIT;	time_esterror = NTP_PHASE_LIMIT;	write_unlock_irq(&xtime_lock);}/* * In order to set the CMOS clock precisely, set_rtc_mmss has to be * called 500 ms after the second nowtime has started, because when * nowtime is written into the registers of the CMOS clock, it will * jump to the next second precisely 500 ms later. Check the Motorola * MC146818A or Dallas DS12887 data sheet for details. * * 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;	unsigned char save_control, save_freq_select;	/* gets recalled with irq locally disabled */	spin_lock(&rtc_lock);	save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);	save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */	CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);	cmos_minutes = CMOS_READ(RTC_MINUTES);	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)		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) {		if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {			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		       "set_rtc_mmss: can't update from %d to %d\n",		       cmos_minutes, real_minutes);		retval = -1;	}	/* The following flags have to be released exactly in this order,	 * otherwise the DS12887 (popular MC146818A clone with integrated	 * battery and quartz) will not reset the oscillator and will not	 * update precisely 500 ms later. You won't find this mentioned in	 * the Dallas Semiconductor data sheets, but who believes data	 * sheets anyway ...                           -- Markus Kuhn	 */	CMOS_WRITE(save_control, RTC_CONTROL);	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);	spin_unlock(&rtc_lock);	return retval;}/* last time the cmos clock got updated */static long last_rtc_update;int timer_ack;/* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs){#ifdef CONFIG_X86_IO_APIC	if (timer_ack) {		/*		 * Subtle, when I/O APICs are used we have to ack timer IRQ		 * manually to reset the IRR bit for do_slow_gettimeoffset().		 * This will also deassert NMI lines for the watchdog if run		 * on an 82489DX-based system.		 */		spin_lock(&i8259A_lock);		outb(0x0c, 0x20);		/* Ack the IRQ; AEOI will end it automatically. */		inb(0x20);		spin_unlock(&i8259A_lock);	}#endif#ifdef CONFIG_VISWS	/* Clear the interrupt */	co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT) & ~CO_STAT_TIMEINTR);#endif	do_timer(regs);/* * In the SMP case we use the local APIC timer interrupt to do the * profiling, except when we simulate SMP mode on a uniprocessor * system, in that case we have to call the local interrupt handler. */#ifndef CONFIG_X86_LOCAL_APIC	if (!user_mode(regs))		x86_do_profile(regs->eip);#else	if (!using_apic_timer)		smp_local_timer_interrupt(regs);#endif	/*	 * If we have an externally synchronized Linux clock, then update	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be	 * called as close as possible to 500 ms before the new second starts.	 */	if ((time_status & STA_UNSYNC) == 0 &&	    xtime.tv_sec > last_rtc_update + 660 &&	    xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&	    xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {		if (set_rtc_mmss(xtime.tv_sec) == 0)			last_rtc_update = xtime.tv_sec;		else			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */	}	    #ifdef CONFIG_MCA	if( MCA_bus ) {		/* The PS/2 uses level-triggered interrupts.  You can't		turn them off, nor would you want to (any attempt to		enable edge-triggered interrupts usually gets intercepted by a		special hardware circuit).  Hence we have to acknowledge		the timer interrupt.  Through some incredibly stupid		design idea, the reset for IRQ 0 is done by setting the		high bit of the PPI port B (0x61).  Note that some PS/2s,		notably the 55SX, work fine if this is removed.  */		irq = inb_p( 0x61 );	/* read the current state */		outb_p( irq|0x80, 0x61 );	/* reset the IRQ */	}#endif}static int use_tsc;/* * This is the same as the above, except we _also_ save the current * Time Stamp Counter value at the time of the timer interrupt, so that * we later on can estimate the time of day more exactly. */static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs){	int count;	/*	 * Here we are in the timer irq handler. We just have irqs locally	 * disabled but we don't know if the timer_bh is running on the other	 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need	 * the irq version of write_lock because as just said we have irq	 * locally disabled. -arca	 */	write_lock(&xtime_lock);	if(use_cyclone)		mark_timeoffset_cyclone();	else if (use_tsc) {		/*		 * It is important that these two operations happen almost at		 * the same time. We do the RDTSC stuff first, since it's		 * faster. To avoid any inconsistencies, we need interrupts		 * disabled locally.		 */		/*		 * Interrupts are just disabled locally since the timer irq		 * has the SA_INTERRUPT flag set. -arca		 */			/* read Pentium cycle counter */		rdtscl(last_tsc_low);		spin_lock(&i8253_lock);		outb_p(0x00, 0x43);     /* latch the count ASAP */		count = inb_p(0x40);    /* read the latched count */		count |= inb(0x40) << 8;		/* Any unpaired read will cause the above to swap MSB/LSB		   forever.  Try to detect this and reset the counter. 		   		   This happens very occasionally with buggy SMM bios		   code at least */		   		if (count > LATCH) {			printk(KERN_WARNING 			       "i8253 count too high! resetting..\n");			outb_p(0x34, 0x43);			outb_p(LATCH & 0xff, 0x40);			outb(LATCH >> 8, 0x40);			count = LATCH - 1;		}		spin_unlock(&i8253_lock);		/* Some i8253 clones hold the LATCH value visible		   momentarily as they flip back to zero */		if (count == LATCH) {			count--;		}		count = ((LATCH-1) - count) * TICK_SIZE;		delay_at_last_interrupt = (count + LATCH/2) / LATCH;	}	do_timer_interrupt(irq, NULL, regs);	write_unlock(&xtime_lock);}/* not static: needed by APM */unsigned long get_cmos_time(void){	unsigned int year, mon, day, hour, min, sec;	int i;	spin_lock(&rtc_lock);	/* 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 */		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));	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)	  {	    BCD_TO_BIN(sec);	    BCD_TO_BIN(min);	    BCD_TO_BIN(hour);	    BCD_TO_BIN(day);	    BCD_TO_BIN(mon);	    BCD_TO_BIN(year);	  }	spin_unlock(&rtc_lock);	if ((year += 1900) < 1970)		year += 100;	return mktime(year, mon, day, hour, min, sec);}static struct irqaction irq0  = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};/* ------ Calibrate the TSC -------  * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). * Too much 64-bit arithmetic here to do this cleanly in C, and for * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) * output busy loop as low as possible. We avoid reading the CTC registers * directly because of the awkward 8-bit access mechanism of the 82C54 * device. */#define CALIBRATE_LATCH	(5 * LATCH)#define CALIBRATE_TIME	(5 * 1000020/HZ)static unsigned long __init calibrate_tsc(void){       /* Set the Gate high, disable speaker */	outb((inb(0x61) & ~0x02) | 0x01, 0x61);	/*	 * Now let's take care of CTC channel 2	 *	 * Set the Gate high, program CTC channel 2 for mode 0,	 * (interrupt on terminal count mode), binary count,	 * load 5 * LATCH count, (LSB and MSB) to begin countdown.	 */	outb(0xb0, 0x43);			/* binary, mode 0, LSB/MSB, Ch 2 */	outb(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */	outb(CALIBRATE_LATCH >> 8, 0x42);	/* MSB of count */	{		unsigned long startlow, starthigh;		unsigned long endlow, endhigh;		unsigned long count;		rdtsc(startlow,starthigh);		count = 0;		do {			count++;		} while ((inb(0x61) & 0x20) == 0);		rdtsc(endlow,endhigh);		last_tsc_low = endlow;		/* Error: ECTCNEVERSET */		if (count <= 1)			goto bad_ctc;		/* 64-bit subtract - gcc just messes up with long longs */		__asm__("subl %2,%0\n\t"			"sbbl %3,%1"			:"=a" (endlow), "=d" (endhigh)			:"g" (startlow), "g" (starthigh),			 "0" (endlow), "1" (endhigh));		/* Error: ECPUTOOFAST */		if (endhigh)			goto bad_ctc;		/* Error: ECPUTOOSLOW */		if (endlow <= CALIBRATE_TIME)			goto bad_ctc;		__asm__("divl %2"			:"=a" (endlow), "=d" (endhigh)			:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));		return endlow;	}	/*	 * The CTC wasn't reliable: we got a hit on the very first read,	 * or the CPU was so fast/slow that the quotient wouldn't fit in	 * 32 bits..	 */bad_ctc:	return 0;}void __init time_init(void){	extern int x86_udelay_tsc;		xtime.tv_sec = get_cmos_time();	xtime.tv_usec = 0;/* * If we have APM enabled or the CPU clock speed is variable * (CPU stops clock on HLT or slows clock to save power) * then the TSC timestamps may diverge by up to 1 jiffy from * 'real time' but nothing will break. * The most frequent case is that the CPU is "woken" from a halt * state by the timer interrupt itself, so we get 0 error. In the * rare cases where a driver would "wake" the CPU and request a * timestamp, the maximum error is < 1 jiffy. But timestamps are * still perfectly ordered. * Note that the TSC counter will be reset if APM suspends * to disk; this won't break the kernel, though, 'cuz we're * smart.  See arch/i386/kernel/apm.c. */ 	/* 	 *	Firstly we have to do a CPU check for chips with 	 * 	a potentially buggy TSC. At this point we haven't run 	 *	the ident/bugs checks so we must run this hook as it 	 *	may turn off the TSC flag. 	 * 	 *	NOTE: this doesnt yet handle SMP 486 machines where only 	 *	some CPU's have a TSC. Thats never worked and nobody has 	 *	moaned if you have the only one in the world - you fix it! 	 */  	dodgy_tsc(); 	if(use_cyclone)		init_cyclone_clock();			if (cpu_has_tsc) {		unsigned long tsc_quotient = calibrate_tsc();		if (tsc_quotient) {			fast_gettimeoffset_quotient = tsc_quotient;			/* XXX: This is messy			 * However, we want to allow for the cyclone timer 			 * to work w/ or w/o the TSCs being avaliable			 *      -johnstul@us.ibm.com			 */			if(!use_cyclone){				/*				 *	We could be more selective here I suspect				 *	and just enable this for the next intel chips ?			 	 */				use_tsc = 1;				x86_udelay_tsc = 1;#ifndef do_gettimeoffset				do_gettimeoffset = do_fast_gettimeoffset;#endif			}			/* report CPU clock rate in Hz.			 * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =			 * clock/second. Our precision is about 100 ppm.			 */			{	unsigned long eax=0, edx=1000;				__asm__("divl %2"		       		:"=a" (cpu_khz), "=d" (edx)        	       		:"r" (tsc_quotient),	                	"0" (eax), "1" (edx));				printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);			}		}	}#ifdef CONFIG_VISWS	printk("Starting Cobalt Timer system clock\n");	/* Set the countdown value */	co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ);	/* Start the timer */	co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN);	/* Enable (unmask) the timer interrupt */	co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK);	/* Wire cpu IDT entry to s/w handler (and Cobalt APIC to IDT) */	setup_irq(CO_IRQ_TIMER, &irq0);#else	setup_irq(0, &irq0);#endif}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -