MFGPT简介:
全称:Multi-Function General Purpose Timer.
它包括了8个timers,其中的6个工作在 working domain,频率为32 KHZ 和 14318 KHZ
另外2个工作在 standy domain,频率为32 KHZ.
对于输入时钟频率,都有15位可以用来分频,从而分别产生15种时钟频率.
MFGPT1
I/O Reg
Clock Switch
Timer
对于每一个MFGPT,都有4个 software accessible I/O寄存器:
Up Counter, Comparator 1 Value, Comparator 2 Value, Setup register
这4个寄存器初始化顺序:
如果先初始化setup寄存器,那么在comparator 1 or 2 和 counter 都未初始化,为0时,
时钟event 将会立即触发。
所以应该:
1.先初始化 comparator 1 or 2 ,
2.然后 counter 清0 ,
3.最后设置setup。
寄存器的重新初始化顺序:
1.清理 counter enable 为 0;
2.清理中断使能: NMI Enable Reset Enable bits in MSRs,disable GPIO的输入输出
3.更新 counter compare 1 or 2;
4.清除任何已经设置的 event bits;
5.set up 中断
MFGPT寄存器描述符:
MFGPT的寄存器分为3部分:
1. Standard GeodeLink Device MSRs
2. MFGPT Specific MSRs
3. MFGPT Native Registers
MSRs都是通过 __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()
来初始化这个时钟。