Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1585385
  • 博文数量: 92
  • 博客积分: 2002
  • 博客等级: 大尉
  • 技术积分: 4717
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-01 17:09
文章分类

全部博文(92)

文章存档

2013年(1)

2012年(6)

2011年(85)

分类: LINUX

2011-01-17 19:27:17

项目中需要添加硬件看门狗。
板子上的看门狗芯片居然没法设置time out时间,晕,当初怎么选型的,估计是为了图便宜或者...你懂的。

好,使用 MPC8560 内部看门狗来计时。

1  Watchdog for E500


1.1  Watchdog状态机 

MPC8560
coreE500,下图是E500 Watchdog Timer的状态机:



    整个状态机是由TSRTimer State Register)寄存器的两个field bit[ENWWIS ]来控制的。




Bit field

解释



ENW

bit若置1,代表Watchdog Timer当前是使能状态



WIS

bit1,代表Watchdog Interrupt发生过


硬件会不断地周期地对[ENW,WIS]进行set操作,软件可以对[ENW,WIS]进行clear操作,软硬件结合起来维护此状态机。

简单来说就是,WDT的超时分为两种情况:

状态[1,0]时发生time out,是第一次超时,将状态置为[1,1],并触发watchdog 中断(可配)

状态[1,1]时发生time out,是第二次超时,会发出HRESET_REQ,请求CPU硬复位(可配)

事实上,WDT的两次超时触发的动作都是可配置的,通过TCR(Timer Control Register)MSRMachine State Register

第一次超时,会更新状态为[1,1]TCR[WIE]MSR[CE]若被set则触发中断,否则不触发中断

第二次超时,会根据TCR[WRC]来选择进行何种操作。


TCR[WRC]

解释

00

Do nothing

01

触发Machine Check中断(前提是MSR[ME]使能)

10

发出HRESET_REQ信号,请求硬复位HRESET

11

保留

 

一个参考流程如下:

1.为产生watchdog interrupt,对TCR[WIE]MSR[CE]进行set;为使CPU硬复位,使TCR[WRC][1,0]

2. 当前状态[0,0],此为CPU上电后的初始状态

3.一个周期后状态变为[1,0],注意 [0,0] ->[1,0]什么都不做

4.一个周期后状态变为[1,1],触发watchdog interrupt

5.watchdog interrupt hanlder对状态进行clear操作(“喂狗”),状态变为[0,0],接下来又从步骤2开始

 

6.用户敲重启命令(reboot),对TCR[WIE]进行clear,使watchdoginterrupt不触发,停止“喂狗”

7.一个周期后超时[1,0],又一个周期后超时[1,1],又一个周期超时,CPU发出HRESET_REQ

8.外部芯片(如CPLD)收到HRESET_REQ,发HRESETCPU

9.CPU收到HRESET,进行硬复位


1.2   Watchdog计时器

E500内部有个计数器Time Base Register。这个计数器由HID0(Hardware Implementation-Dependent Register 0)TCR控制。

Time Base Register是由TBUTime Base Upper)和TBLTime Base Lower)组成,即一个64bit的值。

寄存器的相关配置bit


Bit field

解释

SEL_TBCLK

0Time base is based on the processor clockerery 8 clock1

1Time base is based on the TBCLK (RTC) inputRTC上升沿加1

TBEN

1,计数器使能,Time Base Register开始累加。

0,计时器停止累加。


Time Base Register的某位bit01,则代表发生一次time out

TCR[WPEXT, WP]就是来定义这个bit的。如

WPTXT  WP

1001      00                  0b1001 00 = 0x24 = 36

这个36就是所谓的period值,用于触发time outbit的位置就是 63-36+1 = 28

HID0[SEL_TBCLK]1时,即选用CCB(假设为266MHz)作为计时基准时,有如下:

 

·在0.5timeout时间后,第28bit将被由01,使状态由[0,0]变为[1,0],什么事都不发生

·在1timeout时间后,第28bit又由01,使状态由[1,0][1,1],第一次超时

·在[1,1]状态下,1timeout时间后,第28bit01,第二次超时

 

第一次超时所花时间:

       1.5 X (2^28)   / (CCB /8)      = 12.11

第二次超时所处时间:

       2.5 X (2^28)   / (CCB /8)      = 20.18


2  Uboot添加硬件看门狗

uboot中默认看门狗策略由两个宏CONFIG_HW_WATCHDOGCONFIG_WATCHDOG来使能。

此策略是在代码执行中的不同阶段,添加喂狗代码。

这种喂狗方法会使代码很乱,uboot中到处都充斥的喂狗代码。另外这种方法对代码执行时间是敏感的,如果有段代码执行时间很长(如搬运code),则需要添加喂狗代码,很繁。

uboot的默认策略比较适合外部看门狗。

我们现在用的是CPU内部看门狗,直接无视上述两宏。我们要在watchdog interrupt中喂狗,即只在watchdog interrupt handler中喂狗,比较简便。


2.1  Watchdog Interrupt Handler

修改文件uboot/cpu/mpc85xx/start.S,修改默认的handler

  1. /* STD_EXCEPTION(0x0c00, WatchdogTimer, UnknownException) */

  2. STD_EXCEPTION(0x0c00, WatchdogTimer, WatchdogHandler)

 

修改文件uboot/cpu/mpc85xx/traps.c,实现自己的handler

  1. void WatchdogHandle(struct pt_regs *regs){

  2.     unsigned long msr; //turn off all interrpt, may need it, not sure

  3.     msr = get_msr();

  4.     set_msr(0);

  5.     mtspr(SPRN_TSR, mfspr(SPRN_TSR) | TSR_WIS); //喂狗

  6.     set_msr(msr);

  7. }

 

2.2 Watchdog 配置及开启

修改uboot/lib_ppc/board.c,添加watchdog的配置及开启例程

  1. init_fnc_t *init_sequence[] = 
  2. {
  3.        ………
  4.        WatchdogCreate, //配置例程 这两个例程越靠前越好,没有强制要求
  5.        WatchdogStart, //开启例程
  6.        ……
  7. };

 

修改uboot/common/cmd_bootm.c,添加watchdog的配置例程,开启例程和暂停例程

  1. void WatchdogCreate(void)
  2. {
  3.     unsigned long register val;

  4.     //01.no time base counting now
  5.     mtspr(SPRN_HID0, mfspr(SPRN_HID0) & ~HID0_TBEN);

  6.     //02.set timer period
  7.     val = mfspr(SPRN_TCR);
  8.     val &= ~(0xc0000000 | 0x001e0000); // clear WP and WP_EXT
  9.     val |= (0x00120000); // 此处可修改为宏实现
  10.     mtspr(SPRN_TCR, val);

  11.     //03.set period unit
  12.     val = mfspr(SPRN_HID0);
  13.     val &= ~(0x00040000);
  14.     mtspr(SPRN_HID0, val);

  15.     //04. enable Watchdog timer interrupt
  16.     set_msr (get_msr() | MSR_CE);
  17.  
  18.     //05: enable watchdog timer
  19.     mtspr(SPRN_TCR, mfspr(SPRN_TCR) | TCR_WIE);

  20.     //06: deal with sec timer out
  21.     val = mfspr(SPRN_TCR);
  22.     val &= ~30000000;
  23.     val |= 0x20000000; //reboot
  24.     mtspr(SPRN_TCR, val);

  25.     asm("isync");

  26. }
  1. void WatchdogStart(void)
  2. {
  3.     //01. clear time base
  4.     mtspr(SPRN_TBWL, 0);
  5.     mtspr(SPRN_TBWU, 0);

  6.     //02. now time base counting...
  7.     mtspr(SPRN_HID0, mfspr(SPRN_HID0) | HID0_TBEN);
  8.     asm("isync");
  9. }

 

  1. void WatchdogStop(void){ // just for test,need to modify and more test

  2.     //01. clear tsr
  3.     mtspr( SPRN_TSR, 0xf0000000);

  4.     //02.clear watchdog timer period
  5.     mtspr( SPRN_TCR, mfspr(SPRN_TCR) & ~(0xc0000000 | 0x001e0000) );

  6.     //03.disable watchdog timer
  7.     mtspr(SPRN_TCR, mfspr(SPRN_TCR) & ~(TCR_WIE) );

  8. }


2.3  reset命令的实现

现在我们可以用wathdog来实现重启命令reset

修改文件 uboot/cpu/mpc85xx/cpu.c

  1. int do_reset (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
  2. {
  3. #if 0 // leon del for HRESET
  4.     /*
  5.      * Initiate hard reset in debug control register DBCR0
  6.      * Make sure MSR[DE] = 1
  7.      */
  8.     unsigned long val;
  9.     val = mfspr(DBCR0);
  10.     val |= 0x70000000;
  11.     mtspr(DBCR0,val);
  12. #endif

  13.     //leon add: just disable first time out,second time out will reset send HRESET_REQ
  14.     mtspr(SPRN_TCR, mfspr(SPRN_TCR) & ~TCR_WIE);
  15.     return 1;
  16. }

2.4  FLASH操作关闭看门狗 (??)


打开看门狗的话,对Flash进行写操作时,会使uboot崩溃。具体原因还没找到,一个比较靠谱的猜想是watchdog interrpt handler代码执行还会去flash中读指令。可是当你敲erase bank 1命令的时候,代码早就搬运到RAM中了啊,无解。。。望牛人指点,感激不尽。

所以,当对flash进行写操作指令前,首先要敲自己加的命令,如timer_stop,来关闭watchdog。
写操作完成后,敲命令timer_start,来启动watchdog。

命令timer_stop中再去调函数WatchdogStop().命令timer_start中调用WatchdogCreate()和WatchdogStart()
具体命令如何添加,参考uboot添加命令,最快最简山寨法


3  Linux添加硬件看门狗

Linuxwatchdog机制是靠uboot传来的wdtwdt_period启动参数来使能的。

wdt1就使能watchdog机制,0就禁止;

wdt_perid就是上面所说的period值了。

WDT的配置和开启是在wdt驱动里实现的,入口函数是booke_wdt_init()

Linuxwatchdog interrupt handler函数WatchdogException()会自旋直至CPU硬复位。

那我们在Decrementer Interrupt handler中喂狗,即在do_timer()中喂狗。

修改文件linux-2.6.24/kernel/timer.c

  1. extern int stop_feed;

  2. void do_timer(unsigned long ticks)
  3. {
  4.     if( likely(stop_feed == 0) )
  5.         mtspr(SPRN_TSR, mfspr(SPRN_TSR) | TSR_WIS); //喂狗
  6.  
  7.     jiffies_64 += ticks;
  8.     update_times(ticks);
  9. }

 

相应的reboot命令的实现就简单多了,文件linux-2.6.24/arch/ppc/syslib/ppc85xx_setup.c

  1. void mpc85xx_restart(char *cmd){
  2.     local_irq_disable();
  3.     myabort();
  4. }

  5. int stop_feed = 0;
  6. void myabort (void){
  7.    stop_feed = 1;
  8. }
修改到此结束。 

当我们敲reboot命令后,do_timer()中停止喂狗,watchdog timer first time out后,会执行Watchdog interrupt handler函数 WatchdogException()then spin until second timeout occurthen reboot


//文件linux-2.6.24/arch/ppc/kernel/head_fsl_booke.S

  1. CRITICAL_EXCEPTION(0x3200, WatchdogTimer, WatchdogException)

 

//文件linux-2.6.24/arch/ppc/kernel/traps.c


  1. void WatchdogException(struct pt_regs *regs)
  2. {
  3.     printk (KERN_EMERG "PowerPC Book-E Watchdog Exception\n");
  4.     WatchdogHandler(regs);
  5. }

  6.  
  7. /*
  8.  * Default handler for a Watchdog exception,
  9.  * spins until a reboot occurs 这句和CRITICAL_EXCEPTION宏有关
  10.  */
  11. void __attribute__ ((weak)) WatchdogHandler(struct pt_regs *regs)
  12. {
  13.     /* Generic WatchdogHandler, implement your own */
  14.     mtspr(SPRN_TCR, mfspr(SPRN_TCR)&(~TCR_WIE)); //
  15.     return;
  16. }


阅读(10215) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~