分类: LINUX
2008-12-17 16:30:26
MFGPT简介:
I/O Reg Clock Switch Timer
MFGPT的寄存器分为3部分:1. Standard GeodeLink Device MSRs2. MFGPT Specific MSRs3. MFGPT Native RegistersMSRs都是通过 __rdmsr() __wrmsr() 函数访问的,所有的MSRs都是64bits的,但是MFGPT Spcific MSRs是32bits的,对于高32bits的写会补齐,而读出来的高32bits是无效的。A. MFGPT Specific MSRs Summary:51400028h r/w MFGPT IRQ Mask(MFGPT_IRQ)51400029h r/w MFGPT NMI AND Reset Mask(MFGPT_NR)5140002Ah r/w MFGPT Reserved(MFGPT_RSVD)5140002Bh r/w MFGPT Clear Setup test(MFGPT_SETUP)B. MFGPT Native Registers Summary:这个寄存器组是通过 base + offsets访问的,base is MSR_LBAR_MFGPT(5140000Dh)如果我们只是创建一个时钟,即MFGPT0,则用到的offsets只有:00h R/W MFGPT0 Comparator 1(MFGPT0_CMP1)02h R/W MFGPT0 Comparator 2(MFGPT0_CMP2)04h R/W MFGPT0 Up Counter (MFGPT0_CNT)06h R/W MFGPT0 Setup (MFGPT0_SETUP)
时钟工作原理:简单的说,就是给一个comparator寄存器设置一个初值,把counter寄存器置0,然后在setup寄存器中设置好工作模式,使能等位。counter寄存器就会在每一个到来的时钟脉冲后+1, 当counter = comparator 预设值时,发出一个时钟中断,通知系统一段时间过去了,系统时间应该增加了要使用MFGPT,只需在内核中注册mfgpt的 clock_event_device, clocksource.clock_event_device的初始化:重要的是.set_mode方法的初始化,它其实就是MFGPT的初始化函数:
inti_mfgpt_timer(enum clock_event_mode mode, struct clock_event_device *evt)
{
...
switch(mode){
case CLOCK_EVT_MODE_PERIODIC:
/**给comparator2 设置初值/
outw(COMPARE, base + 2);
/*counter 清 0*/
outw(0, base + 4);
/*设置setup*/
outw(0xe310, base + 6);
break;
}
...
}其中 base 是从0x8000000d中读出来的native msr基地址_rdmsr(0x8000000d, &basehi,&base),上面说了,读出来的高32位会被丢弃。base + 2, 4, 6刚好是 MFGPT0_CMP2, MFGPT0_CNT, MFGPT0_SETUP的地址。#define COMPARE ((14318000 + HZ/2) / HZ)当然,注册clock_event_device之前,还要对此结构体中的cpumask, min/max_delta_ns,等初始化,然后:
/* connect multifunction timer0 comparator 2 to irq mapper*/
_wrmsr(0x80000028, 0, 0x100);
/* map unrestricted interrupt source Z4 to IG5 */
_wrmsr(0x80000022, 0, 0x50000);接着就可以 clockevents_register_device(mfgpt_clockevent) 了;最后不要忘了,setup_irq(5, &irq5); 当然这个5号中断,必须要在之前定义.其中这个irq5的中断处理函数 timer_interrupt要做的是:/*MFGPT_CNT_EN MFGPT_CMP2 2bits 置 1*/outw(0xc000, base + 6);mfgpt_clockevent.event_handler(&mfgpt_clockevent);return IRQ_HANDLED;注册完clockevent, 就该注册 clocksource 了:这里需要注意的是 .read 方法。 这个函数是内核,应用程序读取时间的接口这个函数返回的是:return (cycle_t)(jiffies * COMPARE) + count;显然,返回的是时钟晶振震荡的次数,由于时钟晶振振动的频率是可知的,所以从返回值可以推算出当前时间。在这个函数里,需要注意的是:由于延迟,或者中断等原因:count的值有可能出现回退,溢出,所以要对count寄存器进行校正:
if (count < old_count && jifs == old_jifs) {
count = old_count;
}
old_count = count;
old_jifs = jifs;就此,一个MFGPT时钟源已经注册完成。在内核初始化进程 time_init() --> plat_time_init() --> init_mfgpt_clocksource()来初始化这个时钟。