CS5536中MFGPT时钟的实现

来源:互联网 发布:手办淘宝店推荐贴吧 编辑:程序博客网 时间:2024/03/28 17:19

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()
来初始化这个时钟。

原创粉丝点击