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

📄 time.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge * Paul Mackerras' version and mine for PReP and Pmac. * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). * Converted for 64-bit by Mike Corrigan (mikejc@us.ibm.com) * * 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). *  * Speeded up do_gettimeofday by getting rid of references to * xtime (which required locks for consistency). (mikejc@us.ibm.com) * * TODO (not necessarily in this file): * - improve precision and reproducibility of timebase frequency * measurement at boot time. (for iSeries, we calibrate the timebase * against the Titan chip's clock.) * - 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. * * 1997-09-10  Updated NTP code according to technical memorandum Jan '96 *             "A Kernel Model for Precision Timekeeping" by Dave Mills * *      This program is free software; you can redistribute it and/or *      modify it under the terms of the GNU General Public License *      as published by the Free Software Foundation; either version *      2 of the License, or (at your option) any later version. */#include <linux/config.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/param.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/timex.h>#include <linux/kernel_stat.h>#include <linux/mc146818rtc.h>#include <linux/time.h>#include <linux/init.h>#include <asm/naca.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/processor.h>#include <asm/nvram.h>#include <asm/cache.h>#include <asm/machdep.h>#include <asm/init.h>#ifdef CONFIG_PPC_ISERIES#include <asm/iSeries/HvCallXm.h>#endif#include <asm/uaccess.h>#include <asm/time.h>#include <asm/ppcdebug.h>void smp_local_timer_interrupt(struct pt_regs *);extern void setup_before_console_init();/* keep track of when we need to update the rtc */time_t last_rtc_update;extern rwlock_t xtime_lock;extern int piranha_simulator;#ifdef CONFIG_PPC_ISERIESunsigned long iSeries_recal_titan = 0;unsigned long iSeries_recal_tb = 0; static unsigned long first_settimeofday = 1;#endif#define XSEC_PER_SEC (1024*1024)#define USEC_PER_SEC (1000000)unsigned long tb_ticks_per_jiffy;unsigned long tb_ticks_per_usec;unsigned long tb_ticks_per_sec;unsigned long next_xtime_sync_tb;unsigned long xtime_sync_interval;unsigned long tb_to_xs;unsigned long processor_freq;spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;extern unsigned long wall_jiffies;extern unsigned long lpEvent_count;extern int smp_tb_synchronized;extern unsigned long prof_cpu_mask;extern unsigned int * prof_buffer;extern unsigned long prof_len;extern unsigned long prof_shift;extern char _stext;extern struct timezone sys_tz;void ppc_adjtimex(void);static unsigned adjusting_time = 0;static void ppc_do_profile (unsigned long nip){	/*	 * Only measure the CPUs specified by /proc/irq/prof_cpu_mask.	 * (default is all CPUs.)	 */	if (!((1<<smp_processor_id()) & prof_cpu_mask))		return;	nip -= (unsigned long) &_stext;	nip >>= prof_shift;	/*	 * Don't ignore out-of-bounds EIP values silently,	 * put them into the last histogram slot, so if	 * present, they will show up as a sharp peak.	 */	if (nip > prof_len-1)		nip = prof_len-1;	atomic_inc((atomic_t *)&prof_buffer[nip]);}static __inline__ void timer_check_rtc(void){        /*         * 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.         */        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) {	    struct rtc_time tm;	    to_tm(xtime.tv_sec+1, &tm);	    tm.tm_year -= 1900;	    tm.tm_mon -= 1;            if (ppc_md.set_rtc_time(&tm) == 0)                last_rtc_update = xtime.tv_sec+1;            else                /* Try again one minute later */                last_rtc_update += 60;        }}/* Synchronize xtime with do_gettimeofday */ static __inline__ void timer_sync_xtime( unsigned long cur_tb ){	struct timeval my_tv;	if ( cur_tb > next_xtime_sync_tb ) {		next_xtime_sync_tb = cur_tb + xtime_sync_interval;		do_gettimeofday( &my_tv );		if ( xtime.tv_sec <= my_tv.tv_sec ) {			xtime.tv_sec = my_tv.tv_sec;			xtime.tv_usec = my_tv.tv_usec;		}	}}#ifdef CONFIG_PPC_ISERIES/*  * This function recalibrates the timebase based on the 49-bit time-of-day * value in the Titan chip.  The Titan is much more accurate than the value * returned by the service processor for the timebase frequency.   */static void iSeries_tb_recal(void){	struct div_result divres;	unsigned long titan, tb;	tb = get_tb();	titan = HvCallXm_loadTod();	if ( iSeries_recal_titan ) {		unsigned long tb_ticks = tb - iSeries_recal_tb;		unsigned long titan_usec = (titan - iSeries_recal_titan) >> 12;		unsigned long new_tb_ticks_per_sec   = (tb_ticks * USEC_PER_SEC)/titan_usec;		unsigned long new_tb_ticks_per_jiffy = (new_tb_ticks_per_sec+(HZ/2))/HZ;		long tick_diff = new_tb_ticks_per_jiffy - tb_ticks_per_jiffy;		char sign = '+';				/* make sure tb_ticks_per_sec and tb_ticks_per_jiffy are consistent */		new_tb_ticks_per_sec = new_tb_ticks_per_jiffy * HZ;		if ( tick_diff < 0 ) {			tick_diff = -tick_diff;			sign = '-';		}		if ( tick_diff ) {			if ( tick_diff < tb_ticks_per_jiffy/25 ) {				printk( "Titan recalibrate: new tb_ticks_per_jiffy = %lu (%c%ld)\n",						new_tb_ticks_per_jiffy, sign, tick_diff );				tb_ticks_per_jiffy = new_tb_ticks_per_jiffy;				tb_ticks_per_sec   = new_tb_ticks_per_sec;				div128_by_32( XSEC_PER_SEC, 0, tb_ticks_per_sec, &divres );				systemcfg->tb_ticks_per_sec = tb_ticks_per_sec;				tb_to_xs = divres.result_low;				systemcfg->tb_to_xs = tb_to_xs;			}			else {				printk( "Titan recalibrate: FAILED (difference > 4 percent)\n"					"                   new tb_ticks_per_jiffy = %lu\n"					"                   old tb_ticks_per_jiffy = %lu\n",					new_tb_ticks_per_jiffy, tb_ticks_per_jiffy );			}		}	}	iSeries_recal_titan = titan;	iSeries_recal_tb = tb;}#endif/* * For iSeries shared processors, we have to let the hypervisor * set the hardware decrementer.  We set a virtual decrementer * in the ItLpPaca and call the hypervisor if the virtual * decrementer is less than the current value in the hardware * decrementer. (almost always the new decrementer value will * be greater than the current hardware decementer so the hypervisor * call will not be needed) */unsigned long tb_last_stamp=0;/* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. */int timer_interrupt(struct pt_regs * regs){	int next_dec;	unsigned long cur_tb;	struct paca_struct *lpaca = get_paca();	unsigned long cpu = lpaca->xPacaIndex;	struct ItLpQueue * lpq;	irq_enter(cpu);	if ((!user_mode(regs)) && (prof_buffer))		ppc_do_profile(instruction_pointer(regs));	pmc_timeslice_tick(); /* Hack this in for now */	lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 0;	while (lpaca->next_jiffy_update_tb <= (cur_tb = get_tb())) {#ifdef CONFIG_SMP		smp_local_timer_interrupt(regs);#endif		if (cpu == 0) {			write_lock(&xtime_lock);			tb_last_stamp = lpaca->next_jiffy_update_tb;			do_timer(regs);			timer_sync_xtime( cur_tb );			timer_check_rtc();			write_unlock(&xtime_lock);			if ( adjusting_time && (time_adjust == 0) )				ppc_adjtimex();		}		lpaca->next_jiffy_update_tb += tb_ticks_per_jiffy;	}		next_dec = lpaca->next_jiffy_update_tb - cur_tb;	if (next_dec > lpaca->default_decr)        	next_dec = lpaca->default_decr;	set_dec(next_dec);	lpq = lpaca->lpQueuePtr;	if (lpq && ItLpQueue_isLpIntPending(lpq))		lpEvent_count += ItLpQueue_process(lpq, regs); 	irq_exit(cpu);	if (softirq_pending(cpu))		do_softirq();		return 1;}/* * This version of gettimeofday has microsecond resolution. */void do_gettimeofday(struct timeval *tv){        unsigned long sec, usec, tb_ticks;	unsigned long xsec, tb_xsec;	unsigned long temp_tb_to_xs, temp_stamp_xsec;	unsigned long tb_count_1, tb_count_2;	unsigned long always_zero;	struct systemcfg *gtdp;		gtdp = systemcfg;	/* 	 * The following loop guarantees that we see a consistent view of the	 * tb_to_xs and stamp_xsec variables.  These two variables can change	 * (eg. when xntpd adjusts the clock frequency) and an inconsistent	 * view (one variable changed, the other not) could result in a wildly	 * wrong result for do_gettimeofday. 	 *	 * The code which updates these variables (ppc_adjtimex below)	 * increments tb_update_count, then updates the two variables and then	 * increments tb_update_count again.  This code reads tb_update_count,	 * reads the two variables and then reads tb_update_count again.  It	 * loops doing this until the two reads of tb_update_count yield the	 * same value and that value is even.  This ensures a consistent view	 * of the two variables.	 *	 * The strange looking assembler code below causes the hardware to	 * think that reading the two variables is dependent on the first read	 * of tb_update_count and that the second reading of tb_update_count is	 * dependent on reading the two variables.  This assures ordering	 * without the need for a lwsync, which is much more expensive.	 */	do {		tb_ticks = get_tb() - gtdp->tb_orig_stamp;		tb_count_1 = gtdp->tb_update_count;		__asm__ __volatile__ ("		andc 	%0,%2,%2\n\		add	%1,%3,%0\n\"		: "=&r"(always_zero), "=r"(gtdp)		: "r"(tb_count_1), "r"(gtdp) );		temp_tb_to_xs = gtdp->tb_to_xs;		temp_stamp_xsec = gtdp->stamp_xsec;		__asm__ __volatile__ ("		add	%0,%2,%3\n\		andc	%0,%0,%0\n\		add	%1,%4,%0\n\"		: "=&r"(always_zero), "=r"(gtdp)		: "r"(temp_stamp_xsec), "r"(temp_tb_to_xs), "r"(gtdp) );		tb_count_2 = gtdp->tb_update_count;	} while ( tb_count_2 - ( tb_count_1 & 0xfffffffffffffffe ) ); 	/* These calculations are faster (gets rid of divides)	 * if done in units of 1/2^20 rather than microseconds.	 * The conversion to microseconds at the end is done	 * without a divide (and in fact, without a multiply) */	tb_xsec = mulhdu( tb_ticks, temp_tb_to_xs );	xsec = temp_stamp_xsec + tb_xsec;	sec = xsec / XSEC_PER_SEC;	xsec -= sec * XSEC_PER_SEC;	usec = (xsec * USEC_PER_SEC)/XSEC_PER_SEC;        tv->tv_sec = sec;        tv->tv_usec = usec;}void do_settimeofday(struct timeval *tv){	unsigned long flags;	unsigned long delta_xsec;	long int tb_delta, new_usec, new_sec;	unsigned long new_xsec;	write_lock_irqsave(&xtime_lock, flags);	/* 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.	 */#ifdef CONFIG_PPC_ISERIES	if ( first_settimeofday ) {		iSeries_tb_recal();		first_settimeofday = 0;	}#endif	tb_delta = tb_ticks_since(tb_last_stamp);	tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy;	new_sec = tv->tv_sec;	new_usec = tv->tv_usec - tb_delta / tb_ticks_per_usec;	while (new_usec <0) {		new_sec--; 		new_usec += USEC_PER_SEC;

⌨️ 快捷键说明

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