看門狗 又叫 watchdog timer 是為了能夠防止程序跑飛而使用的一種機制
若系統正常 喂狗的程序會正常的進行 一旦系統出現死機、異常等現象 喂狗會停止 系統在一定的時間內沒有收到喂狗 就自動重啟 恢復到正常狀態
其實就是一個定時器電路 一般有一個輸入,叫喂狗,一個輸出到MCU的RST端,MCU正常工作的時候,每隔一段時間輸出一個信號到喂狗端,給 WDT 清零,
如果超過規定時間不喂狗(一般在程序跑飛時),WDT 定時超過,就會給出一個復位信號到MCU,使MCU復位. 防止MCU死機.
Platform: RK3288
OS: Android 5.1.1
Kernel: 3.10.0
1.1.設備樹 看門狗配置
dtsi片段並不能說明全部-設備樹語法是分層的,因此屬性(尤其是status)可能仍會被包含基本SoC的板級.dts覆蓋. dtsi文件
kernel\arch\arm\boot\dts\rk3288-tb_zk_r322_V3.dts
&watchdog { status = "okay"; //rockchip,irq = <0>; rockchip,atboot = <0>; };
kernel\arch\arm\boot\dts\rk3288.dtsi
watchdog: wdt@2004c000 { compatible = "rockchip,watch dog"; reg = <0xff800000 0x100>; clocks = <&pclk_pd_alive>; clock-names = "pclk_wdt"; interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>; rockchip,irq = <1>; rockchip,timeout = <60>; rockchip,atboot = <1>; rockchip,debug = <0>; status = "disabled"; };
1.2.kernel\drivers\watchdog\rk29_wdt.c
a.rk29_wdt_set_heartbeat 定時器設定一個中斷的時間間隔maxtime 22s
wdt_clock = devm_clk_get(&pdev->dev, "pclk_wdt");
/* timeout unit second */ int rk29_wdt_set_heartbeat(int timeout) { unsigned int count = 0; unsigned int torr = 0, acc = 1, maxtime = 0; unsigned int freq = clk_get_rate(wdt_clock); if (timeout < 1) return -EINVAL; //0x80000000 is the max count of watch dog maxtime = 0x80000000 / freq + 1; if(timeout > maxtime) timeout = maxtime; count = timeout * freq; count /= 0x10000; while(acc < count){ acc *= 2; torr++; } if(torr > 15){ torr = 15; } DBG("%s:torr:%d, count:%d, maxtime:%d s\n", __func__, torr, count, maxtime); wdt_writel(torr, RK29_WDT_TORR); return 0; }
b.rk29_wdt_keepalive 喂狗
/* functions */ void rk29_wdt_keepalive(void) { if (wdt_base) wdt_writel(0x76, RK29_WDT_CRR); }
c. rk29_wdt_probe 中斷申請函數request_irq詳解
rk29_wdt_proberk29_wdt_probe 中斷申請 系統回調函數
ret = request_irq(wdt_irq->start, rk29_wdt_irq_handler, 0, pdev->name, pdev); /* interrupt handler code */ static irqreturn_t rk29_wdt_irq_handler(int irqno, void *param) { DBG("RK29_wdt:watchdog timer expired (irq)\n"); rk29_wdt_keepalive(); return IRQ_HANDLED; }
d.rk29_wdt_ioctl
WDIOC_KEEPALIVE :喂狗
WDIOC_SETTIMEOUT :設置超時值
WDIOC_GETTIMEOUT :獲取超時值
static long rk29_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int new_margin; DBG("%s\n", __func__); switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &rk29_wdt_ident, sizeof(rk29_wdt_ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: DBG("%s:rk29_wdt_keepalive\n", __func__); rk29_wdt_keepalive(); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_margin, p)) return -EFAULT; if (rk29_wdt_set_heartbeat(new_margin)) return -EINVAL; rk29_wdt_keepalive(); return put_user(tmr_margin, p); case WDIOC_GETTIMEOUT: return put_user(tmr_margin, p); default: return -ENOTTY; } }
1.3.JNI ioctl系統調用來控制設備
#define XH_WATCHDOG "/dev/watchdog" #define WDIOC_APP_WDT_CTL 100 static void watchdog_close(JNIEnv *env, jobject obj, jint fd) { write(fd, "V", 1); ioctl(fd, WDIOC_APP_WDT_CTL, NULL); close(fd); } static void watchdog_feed(JNIEnv *env, jobject obj, jint fd) { write(fd, "a", 1); } static jint watchdog_open(JNIEnv *env, jobject obj) { int fd=0; int timeout=0; timeout=40; fd=open(XH_WATCHDOG, O_RDWR,0); if(fd>0) { ioctl(fd, WDIOC_SETTIMEOUT, &timeout); ioctl(fd, WDIOC_APP_WDT_CTL, &timeout); } return fd; }