1. 硬體原理圖
這裡使用了ADM706晶片,wdi喂狗引腳與CPUGPIO3相連。
wdo看門狗逾時複位信号輸出引腳,與/MR手動複位輸入相連,也就是會産生一個RESET信号。
原理圖如下所示:
2. uboot中添加watchdog喂狗功能
首先需要在include/configs/xxx.h檔案中,添加一個宏定義:
#defineCONFIG_HW_WATCHDOG
啟動HW_WATCHDOG功能。
因為在include/watchdog.h中有WATCHDOG宏定義如下:
#ifdefCONFIG_HW_WATCHDOG
#if defined(__ASSEMBLY__)
#define WATCHDOG_RESET blhw_watchdog_reset
#else
extern voidhw_watchdog_reset(void);
#define WATCHDOG_RESEThw_watchdog_reset
#endif
#else
#if defined(CONFIG_WATCHDOG)
#if defined(__ASSEMBLY__)
#define WATCHDOG_RESETbl watchdog_reset
#else
extern voidwatchdog_reset(void);
#define WATCHDOG_RESETwatchdog_reset
#endif
#else
#if defined(__ASSEMBLY__)
#define WATCHDOG_RESET
#else
#defineWATCHDOG_RESET() {}
#endif
#endif
#endif
在定義了CONFIG_HW_WATCHDOG宏之後,系統就會使用hw_watchdog_reset()函數,二者個函數就定義在arch/powerpc/cpu/mpc85xx/cpu.c檔案中,這裡使用的是powerpc的P2020晶片,也就是MPC85xx系列晶片。
添加watchdog功能代碼實作如下:
#ifdefCONFIG_HW_WATCHDOG
#defineGPIO_REG_BASE 0xFFE0F000
#defineGPIO_REG_GPDIR (GPIO_REG_BASE + 0x00)
#defineGPIO_REG_GPODR (GPIO_REG_BASE + 0x04)
#defineGPIO_REG_GPDAT (GPIO_REG_BASE + 0x08)
#defineGPIO_3_OFFSET 0x10000000
void
watchdog_adm_reset(void)
{
*(volatile unsigned int*)GPIO_REG_GPDIR |= GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPODR &= ~GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPDAT &= ~GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPDAT |= GPIO_3_OFFSET;
}
void
hw_watchdog_reset(void)
{
watchdog_adm_reset();
}
#endif
Uboot中基本是到處都使用了一個WATCHDOG_RESET宏,基本上每運作一段時間就會調用這個宏來喂狗,這樣實作的一個看門狗僞狗功能。
3. linux驅動中添加一個看門狗喂狗程式
這個驅動很簡單,就是添加一個定時器,每隔一段時間就喂狗一次。
代碼如下:
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/types.h>
#include<linux/errno.h>
#include<linux/miscdevice.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/ioport.h>
#include<linux/timer.h>
#include<linux/completion.h>
#include<linux/jiffies.h>
#include<linux/watchdog.h>
#include<linux/platform_device.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/gpio.h>
#defineGPIO_REG_BASE 0xFFE0F000
#defineGPIO_REG_GPDIR (gpio_mem + 0x00)
#defineGPIO_REG_GPODR (gpio_mem + 0x04)
#defineGPIO_REG_GPDAT (gpio_mem + 0x08)
#defineGPIO_3_OFFSET 0x10000000
staticvoid *gpio_mem;
void
watchdog_adm_reset(void)
{
*(volatile unsigned int *)GPIO_REG_GPDIR|= GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPODR &= ~GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPDAT &= ~GPIO_3_OFFSET;
*(volatile unsigned int*)GPIO_REG_GPDAT |= GPIO_3_OFFSET;
}
staticstruct {
spinlock_t lock;
struct timer_list timer;
int interval;
int first_interval;
}adm_wdt_device;
staticvoid adm_wdt_trigger(unsigned long unused)
{
spin_lock(&adm_wdt_device.lock);
watchdog_adm_reset();
mod_timer(&adm_wdt_device.timer, jiffies+ adm_wdt_device.interval);
spin_unlock(&adm_wdt_device.lock);
}
staticvoid adm_wdt_start(void)
{
unsigned long flags;
spin_lock_irqsave(&adm_wdt_device.lock, flags);
mod_timer(&adm_wdt_device.timer,jiffies + adm_wdt_device.first_interval);
spin_unlock_irqrestore(&adm_wdt_device.lock, flags);
}
staticint __init adm_wdt_init(void)
{
printk(KERN_INFO "ADM706 watchdogstart feeding\n");
spin_lock_init(&adm_wdt_device.lock);
setup_timer(&adm_wdt_device.timer,adm_wdt_trigger, 0L);
adm_wdt_device.first_interval = 0;
adm_wdt_device.interval =msecs_to_jiffies(1000);
gpio_mem = ioremap(GPIO_REG_BASE, 100);
adm_wdt_start();
return 0;
}
arch_initcall(adm_wdt_init);
staticvoid __exit adm_wdt_exit(void)
{
printk(KERN_INFO "ADM706 watchdogstop feeding\n");
iounmap(gpio_mem);
del_timer(&adm_wdt_device.timer);
}
module_exit(adm_wdt_exit);
MODULE_AUTHOR("XiaohaiXu");
MODULE_DESCRIPTION("Driverfor GPIO controlled ADM706 watchdogs");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("adm706gpio-controlled watchdog");