| ÂÛ̳ע²á| ¼ÓÈëÊÕ²Ø | ÉèΪÊ×Ò³| RSS
Google
Äúµ±Ç°µÄλÖãºÊ×Ò³ > LinuxƵµÀ > Linux¿ª·¢Çø > ÄÚºËÑо¿

¶¨Ê±Æ÷

ʱ¼ä£º2005-12-01 12:54:54  À´Ô´£º  ×÷Õߣº
£ºêRTC_IRQ
ÊÇÖ¸RTCоƬËùÁ¬½ÓµÄÖжÏÇëÇóÊäÈëÏߺţ¬Í¨³£ÊÇ8¡£

7£®2£®2 ¶ÔRTC¼Ä´æÆ÷µÄ¶¨Òå
LinuxÔÚinclude/linux/mc146818rtc.hÕâ¸öÍ·ÎļþÖж¨ÒåÁËRTC¸÷¼Ä´æÆ÷µÄº¬Òå¡£

£¨1£©¼Ä´æÆ÷ÄÚ²¿µØÖ·Ë÷ÒýµÄ¶¨Òå
LinuxÄں˽öʹÓÃRTCоƬµÄʱ¼äÓëÈÕÆÚ¼Ä´æÆ÷×éºÍ¿ØÖƼĴæÆ÷×飬µØÖ·Îª0x00~0x09Ö®¼äµÄ10¸öʱ¼äÓëÈÕÆÚ¼Ä´æÆ÷µÄ¶¨ÒåÈçÏ£º
#define RTC_SECONDS 0
#define RTC_SECONDS_ALARM 1
#define RTC_MINUTES 2
#define RTC_MINUTES_ALARM 3
#define RTC_HOURS 4
#define RTC_HOURS_ALARM 5
/* RTC_*_alarm is always true if 2 MSBs are set */
# define RTC_ALARM_DONT_CARE 0xC0

#define RTC_DAY_OF_WEEK 6
#define RTC_DAY_OF_MONTH 7
#define RTC_MONTH 8
#define RTC_YEAR 9

Ëĸö¿ØÖƼĴæÆ÷µÄµØÖ·¶¨ÒåÈçÏ£º
#define RTC_REG_A 10
#define RTC_REG_B 11
#define RTC_REG_C 12
#define RTC_REG_D 13

£¨2£©¸÷¿ØÖƼĴæÆ÷µÄ״̬λµÄÏêϸ¶¨Òå
¿ØÖƼĴæÆ÷A£¨0x0A£©Ö÷ÒªÓÃÓÚÑ¡ÔñRTCоƬµÄ¹¤×÷ƵÂÊ£¬Òò´ËÒ²³ÆÎªRTCƵÂÊÑ¡Ôñ¼Ä´æÆ÷¡£Òò´ËLinuxÓÃÒ»¸öºê±ðÃû
RTC_FREQ_SELECTÀ´±íʾ¿ØÖƼĴæÆ÷A£¬ÈçÏ£º
#define RTC_FREQ_SELECT RTC_REG_A
RTCƵÂʼĴæÆ÷ÖеÄλ±»·ÖΪÈý×飺¢Ùbit£Û7£Ý±íʾUIP±êÖ¾£»¢Úbit£Û6£º4£ÝÓÃÓÚ³ý·¨Æ÷µÄƵÂÊÑ¡Ôñ£»¢Ûbit£Û3£º0£ÝÓÃÓÚËÙÂÊÑ¡
Ôñ¡£ËüÃǵ͍ÒåÈçÏ£º
# define RTC_UIP 0x80
# define RTC_DIV_CTL 0x70
/* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
# define RTC_RATE_SELECT 0x0F
ÕýÈç7.1.1.1½ÚËù½éÉܵÄÄÇÑù£¬bit£Û6£º4£ÝÓÐ5ÖпÉÄܵÄȡֵ£¬·Ö±ðΪ³ý·¨Æ÷Ñ¡Ôñ²»Í¬µÄ¹¤×÷ƵÂÊ»òÓÃÓÚÖØÖóý·¨Æ÷£¬¸÷ÖÖ¿ÉÄܵÄ
ȡֵÈç϶¨ÒåËùʾ£º
/* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
# define RTC_REF_CLCK_4MHZ 0x00
# define RTC_REF_CLCK_1MHZ 0x10
# define RTC_REF_CLCK_32KHZ 0x20
/* 2 values for divider stage reset, others for "testing purposes only" */
# define RTC_DIV_RESET1 0x60
# define RTC_DIV_RESET2 0x70

¼Ä´æÆ÷BÖеĸ÷λÓÃÓÚʹÄÜ£¯½ûÖ¹RTCµÄ¸÷ÖÖÌØÐÔ£¬Òò´Ë¿ØÖƼĴæÆ÷B£¨0x0B£©Ò²³ÆÎª¡°¿ØÖƼĴæÆ÷¡±£¬LinuxÓúê±ðÃûRTC_CONTROL
À´±íʾ¿ØÖƼĴæÆ÷B£¬ËüÓëÆäÖеĸ÷±ê־λµÄ¶¨ÒåÈçÏÂËùʾ£º
#define RTC_CONTROL RTC_REG_B
# define RTC_SET 0x80 /* disable updates for clock setting */
# define RTC_PIE 0x40 /* periodic interrupt enable */
# define RTC_AIE 0x20 /* alarm interrupt enable */
# define RTC_UIE 0x10 /* update-finished interrupt enable */
# define RTC_SQWE 0x08 /* enable square-wave output */
# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */

¼Ä´æÆ÷CÊÇRTCоƬµÄÖжÏÇëÇó״̬¼Ä´æÆ÷£¬LinuxÓúê±ðÃûRTC_INTR_FLAGSÀ´±íʾ¼Ä´æÆ÷C£¬ËüÓëÆäÖеĸ÷±ê־λµÄ¶¨ÒåÈçÏÂËù
ʾ£º
#define RTC_INTR_FLAGS RTC_REG_C
/* caution - cleared by read */
# define RTC_IRQF 0x80 /* any of the following 3 is active */
# define RTC_PF 0x40
# define RTC_AF 0x20
# define RTC_UF 0x10

¼Ä´æÆ÷D½ö¶¨ÒåÁËÆä×î¸ßλbit£Û7£Ý£¬ÒÔ±íʾRTCоƬÊÇ·ñÓÐЧ¡£Òò´Ë¼Ä´æÆ÷DÒ²³ÆÎªRTCµÄÓÐЧ¼Ä´æÆ÷¡£LinuxÓúê±ðÃûRTC_VALID
À´±íʾ¼Ä´æÆ÷D£¬ÈçÏ£º
#define RTC_VALID RTC_REG_D
# define RTC_VRT 0x80 /* valid RAM and time */

£¨3£©¶þ½øÖƸñʽÓëBCD¸ñʽµÄÏ໥ת»»
ÓÉÓÚʱ¼äÓëÈÕÆÚ¼Ä´æÆ÷ÖеÄÖµ¿ÉÄÜÒÔBCD¸ñʽ´æ´¢£¬Ò²¿ÉÄÜÒÔ¶þ½øÖƸñʽ´æ´¢£¬Òò´ËÐèÒª¶¨Òå¶þ½øÖƸñʽÓëBCD¸ñʽ֮¼äµÄÏ໥ת»»
ºê£¬ÒÔ·½±ã±à³Ì¡£ÈçÏ£º
#ifndef BCD_TO_BIN
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
#endif

#ifndef BIN_TO_BCD
#define BIN_TO_BCD(val) ((val)=(((val)/10)= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}

return (((
(unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}

£¨3£©set_rtc_mmss()º¯Êý
¸Ãº¯ÊýÓÃÀ´¸üÐÂRTCÖеÄʱ¼ä£¬Ëü½öÓÐÒ»¸ö²ÎÊýnowtime£¬ÊÇÒÔÃëÊý±íʾµÄµ±Ç°Ê±¼ä£¬ÆäÔ´ÂëÈçÏ£º
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) tv_usec>£½
0¡£
LinuxÄÚºËͨ¹ýtimeval½á¹¹ÀàÐ͵ÄÈ«¾Ö±äÁ¿xtimeÀ´Î¬³Öµ±Ç°Ê±¼ä£¬¸Ã±äÁ¿¶¨ÒåÔÚkernel/timer.cÎļþÖУ¬ÈçÏÂËùʾ£º
/* The current time */
volatile struct timeval xtime __attribute__ ((aligned (16)));
µ«ÊÇ£¬È«¾Ö±äÁ¿xtimeËùά³ÖµÄµ±Ç°Ê±¼äͨ³£Êǹ©Óû§À´¼ìË÷ºÍÉèÖõ쬶øÆäËûÄÚºËÄ£¿éͨ³£ºÜÉÙʹÓÃËü£¨ÆäËûÄÚºËÄ£¿éÓõÃ×î¶àµÄ
ÊÇjiffies£©£¬Òò´Ë¶ÔxtimeµÄ¸üв¢²»ÊÇÒ»Ïî½ôÆÈµÄÈÎÎñ£¬ËùÒÔÕâÒ»¹¤×÷ͨ³£±»ÑÓ³Ùµ½Ê±ÖÓÖжϵĵװ벿·Ö£¨bottom half£©ÖÐ
À´½øÐС£ÓÉÓÚbottom halfµÄÖ´ÐÐʱ¼ä´øÓв»È·¶¨ÐÔ£¬Òò´ËΪÁ˼ÇסÄÚºËÉÏÒ»´Î¸üÐÂxtimeÊÇʲôʱºò£¬LinuxÄں˶¨ÒåÁËÒ»¸öÀàËÆ
ÓÚjiffiesµÄÈ«¾Ö±äÁ¿wall_jiffies£¬À´±£´æÄÚºËÉÏÒ»´Î¸üÐÂxtimeʱµÄjiffiesÖµ¡£Ê±ÖÓÖжϵĵװ벿·Öÿһ´Î¸üÐÂxtimeµÄʱ
ºî¶¼»á½«wall_jiffies¸üÐÂΪµ±Ê±µÄjiffiesÖµ¡£È«¾Ö±äÁ¿wall_jiffies¶¨ÒåÔÚkernel/timer.cÎļþÖУº
/* jiffies at the most recent update of wall time */
unsigned long wall_jiffies;
¢ÛÈ«¾Ö±äÁ¿sys_tz£ºËüÊÇÒ»¸ötimezone½á¹¹ÀàÐ͵ÄÈ«¾Ö±äÁ¿£¬±íʾϵͳµ±Ç°µÄÊ±ÇøÐÅÏ¢¡£½á¹¹ÀàÐÍtimezone¶¨ÒåÔÚinclude/
linux/time.hÍ·ÎļþÖУ¬ÈçÏÂËùʾ£º
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
»ùÓÚÉÏÊö½á¹¹£¬LinuxÔÚkernel/time.cÎļþÖж¨ÒåÁËÈ«¾Ö±äÁ¿sys_tz±íʾϵͳµ±Ç°Ëù´¦µÄÊ±ÇøÐÅÏ¢£¬ÈçÏÂËùʾ£º
struct timezone sys_tz;

7£®3£®3 Linux¶ÔTSCµÄ±à³ÌʵÏÖ
LinuxÓö¨ÒåÔÚarch/i386/kernel/time.cÎļþÖеÄÈ«¾Ö±äÁ¿use_tscÀ´±íʾÄÚºËÊÇ·ñʹÓÃCPUµÄTSC¼Ä´æÆ÷£¬use_tsc£½1±íʾ
ʹÓÃTSC£¬use_tsc£½0±íʾ²»Ê¹ÓÃTSC¡£¸Ã±äÁ¿µÄÖµÊÇÔÚtime_init()³õʼ»¯º¯ÊýÖб»³õʼ»¯µÄ£¨Ïê¼ûÏÂÒ»½Ú£©¡£¸Ã±äÁ¿µÄ¶¨ÒåÈç
주
static int use_tsc;
ºêcpu_has_tsc¿ÉÒÔÈ·¶¨µ±Ç°ÏµÍ³µÄCPUÊÇ·ñÅäÖÃÓÐTSC¼Ä´æÆ÷¡£´ËÍ⣬ºêCONFIG_X86_TSCÒ²±íʾÊÇ·ñ´æÔÚTSC¼Ä´æÆ÷¡£

7£®3£®3£®1 ¶ÁTSC¼Ä´æÆ÷µÄºê²Ù×÷
x86 CPUµÄrdtscÖ¸ÁTSC¼Ä´æÆ÷µÄ¸ß32λֵ¶Áµ½EDX¼Ä´æÆ÷ÖС¢µÍ32λ¶Áµ½EAX¼Ä´æÆ÷ÖС£Linux¸ù¾Ý²»Í¬µÄÐèÒª£¬ÔÚrdtscÖ¸
ÁîµÄ»ù´¡ÉÏ·â×°¼¸¸ö¸ß²ãºê²Ù×÷£¬ÒÔ¶ÁÈ¡TSC¼Ä´æÆ÷µÄÖµ¡£ËüÃǾù¶¨ÒåÔÚinclude/asm-i386/msr.hÍ·ÎļþÖУ¬ÈçÏ£º
#define rdtsc(low,high)
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

#define rdtscl(low)
__asm__ __volatile__ ("rdtsc" : "=a" (low) : : "edx")

#define rdtscll(val)
__asm__ __volatile__ ("rdtsc" : "=A" (val))
ºêrdtsc£¨£©Í¬Ê±¶ÁÈ¡TSCµÄLSBÓëMSB£¬²¢·Ö±ð±£´æµ½ºê²ÎÊýlowºÍhighÖС£ºêrdtsclÔòÖ»¶ÁÈ¡TSC¼Ä´æÆ÷µÄLSB£¬²¢±£´æµ½ºê²ÎÊý
lowÖС£ºêrdtscll¶ÁÈ¡TSCµÄµ±Ç°64λֵ£¬²¢½«Æä±£´æµ½ºê²ÎÊývalÕâ¸ö64λ±äÁ¿ÖС£

7£®3£®3£®2 У׼TSC
Óë¿É±à³Ì¶¨Ê±Æ÷PITÏà±È£¬ÓÃTSC¼Ä´æÆ÷¿ÉÒÔ»ñµÃ¸ü¾«È·µÄʱ¼ä¶ÈÁ¿¡£µ«ÊÇÔÚ¿ÉÒÔʹÓÃTSC֮ǰ£¬Ëü±ØÐ뾫ȷµØÈ·¶¨1¸öTSC¼ÆÊýÖµµ½
µ×´ú±í¶à³¤µÄʱ¼ä¼ä¸ô£¬Ò²¼´µ½µ×Òª¹ý¶à³¤Ê±¼ä¼ä¸ôTSC¼Ä´æÆ÷²Å»á¼Ó1¡£LinuxÄÚºËÓÃÈ«¾Ö±äÁ¿fast_gettimeoffset_quotient
À´±íʾÕâ¸öÖµ£¬Æä¶¨ÒåÈçÏ£¨arch/i386/kernel/time.c£©£º
/* Cached *multiplier* to convert TSC counts to microseconds.
* (see the equation below).
* Equal to 2^32 * (1 / (clocks per usec) ).
* Initialized in time_init.
*/
unsigned long fast_gettimeoffset_quotient;
¸ù¾ÝÉÏÊö¶¨ÒåµÄ×¢ÊÍÎÒÃÇ¿ÉÒÔ¿´³ö£¬Õâ¸ö±äÁ¿µÄÖµÊÇͨ¹ýÏÂÊö¹«Ê½À´¼ÆËãµÄ£º
fast_gettimeoffset_quotient £½ (2^32) / (ÿ΢ÃëÄÚµÄʱÖÓÖÜÆÚ¸öÊý)
¶¨ÒåÔÚarch/i386/kernel/time.cÎļþÖеĺ¯Êýcalibrate_tsc()¾ÍÊǸù¾ÝÉÏÊö¹«Ê½À´¼ÆËãfast_gettimeoffset_quotient
µÄÖµµÄ¡£ÏÔÈ»Õâ¸ö¼ÆËã¹ý³Ì±ØÐëÔÚÄÚºËÆô¶¯Ê±Íê³É£¬Òò´Ë£¬º¯Êýcalibrate_tsc()Ö»±»³õʼ»¯º¯Êýtime_init()Ëùµ÷Óá£

ÓÃTSCʵÏָ߾«¶ÈµÄʱ¼ä·þÎñ
ÔÚÓµÓÐTSC£¨TimeStamp Counter£©µÄx86 CPUÉÏ£¬LinuxÄں˿ÉÒÔʵÏÖ΢Ãë¼¶µÄ¸ß¾«¶È¶¨Ê±·þÎñ£¬Ò²¼´¿ÉÒÔÈ·¶¨Á½´ÎʱÖÓÖжÏÖ®
¼äµÄij¸öʱ¿ÌµÄ΢Ã뼶ʱ¼äÖµ¡£ÈçÏÂͼËùʾ£º
ͼ7£­7 TSCʱ¼ä¹ØÏµ

´ÓÉÏͼÖпÉÒÔ¿´³ö£¬ÒªÈ·¶¨Ê±¿ÌxµÄ΢Ã뼶ʱ¼äÖµ£¬¾Í±ØÐëÈ·¶¨Ê±¿Ìx¾àÉÏÒ»´ÎʱÖÓÖжϲúÉúʱ¿ÌµÄʱ¼ä¼ä¸ôÆ«ÒÆoffset_usecµÄÖµ
£¨ÒÔ΢ÃëΪµ¥Î»£©¡£Îª´Ë£¬Äں˶¨ÒåÁËÒÔÏÂÁ½¸ö±äÁ¿£º
£¨1£©ÖжϷþÎñÖ´ÐÐÑÓ³Ùdelay_at_last_interrupt£ºÓÉÓÚ´Ó²úÉúʱÖÓÖжϵÄÄǸöʱ¿Ìµ½ÄÚºËʱÖÓÖжϷþÎñº¯Êý
timer_interruptÕæÕýÔÚCPUÉÏÖ´ÐеÄÄǸöʱ¿ÌÖ®¼äÊÇÓÐÒ»¶ÎÑÓ³Ù¼ä¸ôµÄ£¬Òò´Ë£¬LinuxÄÚºËÓñäÁ¿delay_at_last_interrupt
À´±íʾÕâÒ»¶Îʱ¼äÑÓ³Ù¼ä¸ô£¬Æä¶¨ÒåÈçÏ£¨arch/i386/kernel/time.c£©£º
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
¹ØÓÚdelay_at_last_interruptµÄ¼ÆËã²½ÖèÎÒÃǽ«ÔÚ·ÖÎötimer_interrupt£¨£©º¯ÊýʱÌÖÂÛ¡£
£¨2£©È«¾Ö±äÁ¿last_tsc_low£ºËü±íʾÖжϷþÎñtimer_interruptÕæÕýÔÚCPUÉÏÖ´ÐÐʱ¿ÌµÄTSC¼Ä´æÆ÷ÖµµÄµÍ32루LSB£©¡£
ÏÔÈ»£¬Í¨¹ýdelay_at_last_interrupt¡¢last_tsc_lowºÍʱ¿Ìx´¦µÄTSC¼Ä´æÆ÷Öµ£¬ÎÒÃǾͿÉÒÔÍêȫȷ¶¨Ê±¿Ìx¾àÉÏÒ»´ÎʱÖÓÖÐ
¶Ï²úÉúʱ¿ÌµÄʱ¼ä¼ä¸ôÆ«ÒÆoffset_usecµÄÖµ¡£ÊµÏÖÔÚarch/i386/kernel/time.cÖеĺ¯Êýdo_fast_gettimeoffset()¾ÍÊÇÕâ
Ñù¼ÆËãʱ¼ä¼ä¸ôÆ«ÒÆµÄ£¬µ±È»Ëü½öÔÚCPUÅäÖÃÓÐTSC¼Ä´æÆ÷ʱ²Å±»Ê¹Ó㬺óÃæÎÒÃÇ»áÏêϸ·ÖÎöÕâ¸öº¯Êý¡£

7£®4 ʱÖÓÖжϵÄÇý¶¯
ÈçǰËùÊö£¬8253£¯8254 PITµÄͨµÀ0ͨ³£±»ÓÃÀ´ÔÚIRQ0ÉϲúÉúÖÜÆÚÐÔµÄʱÖÓÖжϡ£¶ÔʱÖÓÖжϵÄÇý¶¯ÊǾø´óÊý²Ù×÷ϵͳÄÚºËʵÏÖ
time-keepingµÄ¹Ø¼üËùÔÚ¡£²»Í¬µÄOS¶ÔʱÖÓÇý¶¯µÄÒªÇóÒ²²»Í¬£¬µ«ÊÇÒ»°ã¶¼°üº¬ÏÂÁÐÒªÇóÄÚÈÝ£º
1. ά»¤ÏµÍ³µÄµ±Ç°Ê±¼äÓëÈÕÆÚ¡£
2. ·ÀÖ¹½ø³ÌÔËÐÐʱ¼ä³¬³öÆäÔÊÐíµÄʱ¼ä¡£
3. ¶ÔCPUµÄʹÓÃÇé¿ö½øÐмÇÕÊͳ¼Æ¡
 2/7   |‹ ‹‹ 1 2 3 4 5 6 ›› ›|

À´¶¥Ò»ÏÂ
½ü»ØÊ×Ò³
·µ»ØÊ×Ò³
·¢±íÆÀÂÛ ¹²ÓÐÌõÆÀÂÛ
Óû§Ãû: ÃÜÂë:
ÑéÖ¤Âë: ÄäÃû·¢±í
Ïà¹ØÎÄÕÂ
    ÎÞÏà¹ØÐÅÏ¢
À¸Ä¿¸üÐÂ
À¸Ä¿ÈÈÃÅ