新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM-Linux驅動--Watch Dog Timer(看門狗)驅動分析

        ARM-Linux驅動--Watch Dog Timer(看門狗)驅動分析

        作者: 時間:2016-11-20 來源:網絡 收藏
        硬件平臺:FL2440

        內核版本:2.6.28

        本文引用地址:http://www.104case.com/article/201611/318869.htm

        主機平臺:Ubuntu 11,04

        內核版本:2.6.39

        1、看門狗驅動的原理

        下圖是看門狗驅動的原理圖

        可以看出,PCLK是系統時鐘,經過8位的預分頻,然后再被分頻(16、32、64、128)然后產生計數脈沖,進行計數,當計數器WTCNT加到0或減到0,然后產生中斷,或引起系統復位。所以要隔一段時間,重置WTCNT的值,防止WTCNT減到0,稱之“喂狗”。

        2、驅動分析

        下面是自己的驅動分析,如有理解錯誤,請指正

        注,為了盡量是驅動容易理解,這個驅動暫時將有關電源管理的功能刪除了,等理解透徹再完善

        1. #include
        2. #include
        3. #include
        4. #include
        5. #include
        6. #include
        7. #include
        8. #include
        9. #include
        10. #includeinterrupt.h>
        11. #include
        12. #include
        13. #include
        14. #include
        15. #include
        16. #undefS3C_VA_WATCHDOG
        17. #defineS3C_VA_WATCHDOG(0)
        18. #include
        19. #definePFX"s3c2410-wdt:"
        20. #defineCONFIG_S3C2410_WATCHDOG_ATBOOT(0)
        21. #defineCONFIG_S3C2410_WATCHDOG_DEFAULT_TIME(15)
        22. staticintnowayout=WATCHDOG_NOWAYOUT;
        23. staticinttmr_margin=CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
        24. staticinttmr_atboot=CONFIG_S3C2410_WATCHDOG_ATBOOT;
        25. staticintsoft_noboot=1;//設置默認為執行中斷
        26. staticintdebug;
        27. staticintcount;//用于計數,控制LED燈的亮滅
        28. module_param(tmr_margin,int,0);
        29. module_param(tmr_atboot,int,0);
        30. module_param(nowayout,int,0);
        31. module_param(soft_noboot,int,0);
        32. module_param(debug,int,0);
        33. MODULE_PARM_DESC(tmr_margin,"Watchdogtmr_margininseconds.default="
        34. __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME)")");
        35. MODULE_PARM_DESC(tmr_atboot,
        36. "Watchdogisstartedatboottimeifsetto1,default="
        37. __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
        38. MODULE_PARM_DESC(nowayout,"Watchdogcannotbestoppedoncestarted(default="
        39. __MODULE_STRING(WATCHDOG_NOWAYOUT)")");
        40. MODULE_PARM_DESC(soft_noboot,"Watchdogaction,setto1toignorereboots,0to
        41. reboot(defaultdependsonONLY_TESTING)");
        42. MODULE_PARM_DESC(debug,"Watchdogdebug,setto>1fordebug,(default0)");
        43. typedefenumclose_state
        44. {
        45. CLOSE_STATE_NOT,
        46. CLOSE_STATE_ALLOW=0x4021
        47. }close_state_t;
        48. staticunsignedlongopen_lock;
        49. staticstructdevice*wdt_dev;/*platformdeviceattachedto*/
        50. staticstructresource*wdt_mem;//用來保存IO端口占用的內存資源
        51. staticstructresource*wdt_irq;//保存wdt中斷號
        52. staticstructclk*wdt_clock;//保存從平臺獲取的watchdog時鐘
        53. staticvoid__iomem*wdt_base;//經過ioremap后的內存基地址
        54. staticunsignedintwdt_count;//保存向WTCNT寫的計數值
        55. staticclose_state_tallow_close;
        56. staticDEFINE_SPINLOCK(wdt_lock);//定義一個自旋鎖,用于資源的互斥訪問
        57. /*watchdogcontrolroutines*/
        58. #defineDBG(msg...)do{
        59. if(debug)
        60. printk(KERN_INFOmsg);
        61. }while(0)
        62. /*functions*/
        63. //喂狗函數,實際上是將wdt_count寫入WTCNT寄存器
        64. staticvoids3c2410wdt_keepalive(void)
        65. {
        66. spin_lock(&wdt_lock);//給資源上鎖
        67. writel(wdt_count,wdt_base+S3C2410_WTCNT);//寫WTCNT寄存器
        68. spin_unlock(&wdt_lock);//解鎖資源,下同
        69. }
        70. staticvoid__s3c2410wdt_stop(void)
        71. {
        72. unsignedlongwtcon;
        73. wtcon=readl(wdt_base+S3C2410_WTCON);
        74. wtcon&=~(S3C2410_WTCON_ENABLE|S3C2410_WTCON_RSTEN);
        75. writel(wtcon,wdt_base+S3C2410_WTCON);//設備看門狗使能無效、復位功能無效
        76. }
        77. //停止watchdog計時
        78. staticvoids3c2410wdt_stop(void)
        79. {
        80. spin_lock(&wdt_lock);
        81. __s3c2410wdt_stop();
        82. spin_unlock(&wdt_lock);
        83. }
        84. //啟動watchdog計時
        85. staticvoids3c2410wdt_start(void)
        86. {
        87. unsignedlongwtcon;
        88. spin_lock(&wdt_lock);
        89. __s3c2410wdt_stop();
        90. wtcon=readl(wdt_base+S3C2410_WTCON);
        91. wtcon|=S3C2410_WTCON_ENABLE|S3C2410_WTCON_DIV128;//看門狗定時器使能、時鐘除數因子128
        92. if(soft_noboot)//使用參數soft_noboot來選擇看門狗到時是重啟還是執行中斷函數
        93. {
        94. wtcon|=S3C2410_WTCON_INTEN;//中斷使能
        95. wtcon&=~S3C2410_WTCON_RSTEN;//復位功能無效
        96. }
        97. else
        98. {
        99. wtcon&=~S3C2410_WTCON_INTEN;//中斷不使能
        100. wtcon|=S3C2410_WTCON_RSTEN;//復位功能有效
        101. }
        102. writel(wdt_count,wdt_base+S3C2410_WTDAT);//將wdt_count寫入WTDAT
        103. writel(wdt_count,wdt_base+S3C2410_WTCNT);//將wdt_count寫入WTCNT
        104. writel(wtcon,wdt_base+S3C2410_WTCON);//設置WTCON
        105. spin_unlock(&wdt_lock);
        106. }
        107. //設置WatchDog周期timeout
        108. staticints3c2410wdt_set_heartbeat(inttimeout)
        109. {
        110. unsignedintfreq=clk_get_rate(wdt_clock);
        111. unsignedintcount;
        112. unsignedintdivisor=1;
        113. unsignedlongwtcon;
        114. if(timeout<1)
        115. return-EINVAL;
        116. freq/=128;
        117. count=timeout*freq;
        118. /*ifthecountisbiggerthanthewatchdogregister,
        119. thenworkoutwhatweneedtodo(andif)wecan
        120. actuallymakethisvalue
        121. */
        122. //如果計時的時間過大,則適當加大預分頻值
        123. if(count>=0x10000)
        124. {
        125. for(divisor=1;divisor<=0x100;divisor++)
        126. {
        127. if((count/divisor)<0x10000)
        128. break;
        129. }
        130. //若預分頻最大仍不能滿足計時周期,則報錯
        131. if((count/divisor)>=0x10000)
        132. {
        133. dev_err(wdt_dev,"timeout%dtoobign",timeout);
        134. return-EINVAL;
        135. }
        136. }
        137. tmr_margin=timeout;
        138. count/=divisor;
        139. wdt_count=count;
        140. /*updatethepre-scaler*/
        141. wtcon=readl(wdt_base+S3C2410_WTCON);
        142. wtcon&=~S3C2410_WTCON_PRESCALE_MASK;
        143. wtcon|=S3C2410_WTCON_PRESCALE(divisor-1);
        144. writel(count,wdt_base+S3C2410_WTDAT);//是指WTDAT寄存器
        145. writel(wtcon,wdt_base+S3C2410_WTCON);//設置預分頻值
        146. return0;
        147. }
        148. /*
        149. */dev/watchdoghandling
        150. */
        151. staticints3c2410wdt_open(structinode*inode,structfile*file)
        152. {
        153. if(test_and_set_bit(0,&open_lock))//測試并設置open_lock第0位為1
        154. return-EBUSY;
        155. if(nowayout)
        156. __module_get(THIS_MODULE);
        157. allow_close=CLOSE_STATE_NOT;
        158. /*startthetimer*/
        159. s3c2410wdt_start();//啟動watchdog
        160. returnnonseekable_open(inode,file);
        161. }
        162. staticints3c2410wdt_release(structinode*inode,structfile*file)
        163. {
        164. /*
        165. *Shutoffthetimer.
        166. *Lockitinifitsamoduleandwesetnowayout
        167. */
        168. if(allow_close==CLOSE_STATE_ALLOW)
        169. s3c2410wdt_stop();
        170. else
        171. {
        172. dev_err(wdt_dev,"Unexpectedclose,notstoppingwatchdogn");
        173. s3c2410wdt_keepalive();
        174. }
        175. allow_close=CLOSE_STATE_NOT;
        176. clear_bit(0,&open_lock);//清楚open_lock第0位
        177. return0;
        178. }
        179. staticssize_ts3c2410wdt_write(structfile*file,constchar__user*data,
        180. size_tlen,loff_t*ppos)
        181. {
        182. /*
        183. *Refreshthetimer.
        184. */
        185. if(len)
        186. {
        187. if(!nowayout)
        188. {
        189. size_ti;
        190. /*Incaseitwassetlongago*/
        191. allow_close=CLOSE_STATE_NOT;
        192. for(i=0;i!=len;i++)
        193. {
        194. charc;
        195. if(get_user(c,data+i))//從用戶空間copy數據
        196. return-EFAULT;
        197. if(c==V)//當輸入V時,關閉watchdog
        198. allow_close=CLOSE_STATE_ALLOW;
        199. }
        200. }
        201. //注意:這里要手動喂狗一次?!
        202. s3c2410wdt_keepalive();//由于將allow_close設置成CLOSE_STATE_ALLOW后,當release時無法再次喂狗
        203. }
        204. returnlen;
        205. }
        206. #defineOPTIONSWDIOF_SETTIMEOUT|WDIOF_KEEPALIVEPING|WDIOF_MAGICCLOSE
        207. staticconststructwatchdog_infos3c2410_wdt_ident=
        208. {
        209. .options=OPTIONS,
        210. .firmware_version=0,
        211. .identity="S3C2410Watchdog",
        212. };
        213. //IO控制接口函數
        214. staticlongs3c2410wdt_ioctl(structfile*file,unsignedintcmd,
        215. unsignedlongarg)
        216. {
        217. void__user*argp=(void__user*)arg;
        218. int__user*p=argp;
        219. intnew_margin;
        220. switch(cmd){
        221. caseWDIOC_GETSUPPORT:
        222. returncopy_to_user(argp,&s3c2410_wdt_ident,
        223. sizeof(s3c2410_wdt_ident))?-EFAULT:0;
        224. caseWDIOC_GETSTATUS:
        225. caseWDIOC_GETBOOTSTATUS:
        226. returnput_user(0,p);
        227. caseWDIOC_KEEPALIVE:
        228. s3c2410wdt_keepalive();
        229. return0;
        230. caseWDIOC_SETTIMEOUT:
        231. if(get_user(new_margin,p))
        232. return-EFAULT;
        233. if(s3c2410wdt_set_heartbeat(new_margin))
        234. return-EINVAL;
        235. s3c2410wdt_keepalive();
        236. returnput_user(tmr_margin,p);
        237. caseWDIOC_GETTIMEOUT:
        238. returnput_user(tmr_margin,p);
        239. default:
        240. return-ENOTTY;
        241. }
        242. }
        243. /*kernelinterface*/
        244. //文件操作結構體
        245. staticconststructfile_operationss3c2410wdt_fops=
        246. {
        247. .owner=THIS_MODULE,
        248. .llseek=no_llseek,//屏蔽seek操作
        249. .write=s3c2410wdt_write,//寫方法
        250. .unlocked_ioctl=s3c2410wdt_ioctl,//控制方法
        251. .open=s3c2410wdt_open,//代開設備方法
        252. .release=s3c2410wdt_release,//關閉設備方法
        253. };
        254. staticstructmiscdevices3c2410wdt_miscdev=
        255. {
        256. .minor=WATCHDOG_MINOR,
        257. .name="watchdog",
        258. .fops=&s3c2410wdt_fops,
        259. };
        260. /*interrupthandlercode*/
        261. staticirqreturn_ts3c2410wdt_irq(intirqno,void*param)
        262. {
        263. //dev_info(wdt_dev,"watchdogtimerexpired(irq)n");
        264. s3c2410_gpio_setpin(S3C2410_GPB10,(count++)%2);
        265. s3c2410wdt_keepalive();
        266. returnIRQ_HANDLED;
        267. }
        268. /*deviceinterface*/
        269. //注冊設備時執行
        270. staticints3c2410wdt_probe(structplatform_device*pdev)
        271. {
        272. structresource*res;
        273. structdevice*dev;
        274. unsignedintwtcon;
        275. intstarted=0;
        276. intret;
        277. intsize;
        278. dev=&pdev->dev;
        279. wdt_dev=&pdev->dev;
        280. /*getthememoryregionforthewatchdogtimer*/
        281. //獲取IO端口資源,這里IORESOURCE_MEM和平臺設備中資源的定義一致
        282. res=platform_get_resource(pdev,IORESOURCE_MEM,0);
        283. if(res==NULL){
        284. dev_err(dev,"nomemoryresourcespecifiedn");
        285. return-ENOENT;
        286. }
        287. size=(res->end-res->start)+1;
        288. //申請內存空間
        289. wdt_mem=request_mem_region(res->start,size,pdev->name);
        290. if(wdt_mem==NULL){
        291. dev_err(dev,"failedtogetmemoryregionn");
        292. ret=-ENOENT;
        293. gotoerr_req;
        294. }
        295. //地址映射,wdt_base為基地址
        296. wdt_base=ioremap(res->start,size);
        297. if(wdt_base==NULL){
        298. dev_err(dev,"failedtoioremap()regionn");
        299. ret=-EINVAL;
        300. gotoerr_req;
        301. }
        302. DBG("probe:mappedwdt_base=%pn",wdt_base);
        303. //從平臺設備中獲取中斷號
        304. wdt_irq=platform_get_resource(pdev,IORESOURCE_IRQ,0);
        305. if(wdt_irq==NULL){
        306. dev_err(dev,"noirqresourcespecifiedn");
        307. ret=-ENOENT;
        308. gotoerr_map;
        309. }
        310. //注冊中斷,中斷處理函數s3c2410wdt_irq()
        311. ret=request_irq(wdt_irq->start,s3c2410wdt_irq,0,pdev->name,pdev);
        312. if(ret!=0){
        313. dev_err(dev,"failedtoinstallirq(%d)n",ret);
        314. gotoerr_map;
        315. }
        316. //獲取系統時鐘
        317. wdt_clock=clk_get(&pdev->dev,"watchdog");
        318. if(IS_ERR(wdt_clock)){
        319. dev_err(dev,"failedtofindwatchdogclocksourcen");
        320. ret=PTR_ERR(wdt_clock);
        321. gotoerr_irq;
        322. }
        323. //使能時鐘
        324. clk_enable(wdt_clock);
        325. /*seeifwecanactuallysettherequestedtimermargin,andif
        326. *not,trythedefaultvalue*/
        327. if(s3c2410wdt_set_heartbeat(tmr_margin))
        328. {//這里第一次設置計時周期為tmr_margin,如果設置不成功,則重新設置為默認值
        329. started=s3c2410wdt_set_heartbeat(
        330. CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
        331. if(started==0)
        332. dev_info(dev,
        333. "tmr_marginvalueoutofrange,default%dusedn",
        334. CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
        335. else
        336. dev_info(dev,"defaulttimervalueisoutofrange,cannotstartn");
        337. }
        338. //注冊設備
        339. ret=misc_register(&s3c2410wdt_miscdev);
        340. if(ret)
        341. {
        342. dev_err(dev,"cannotregistermiscdevonminor=%d(%d)n",
        343. WATCHDOG_MINOR,ret);
        344. gotoerr_clk;
        345. }
        346. if(tmr_atboot&&started==0)
        347. {
        348. dev_info(dev,"startingwatchdogtimern");
        349. s3c2410wdt_start();//啟動看門狗
        350. }
        351. elseif(!tmr_atboot)
        352. {
        353. /*ifwerenotenablingthewatchdog,thenensureitis
        354. *disabledifithasbeenleftrunningfromthebootloader
        355. *orothersource*/
        356. s3c2410wdt_stop();
        357. }
        358. /*printoutastatementofreadiness*/
        359. wtcon=readl(wdt_base+S3C2410_WTCON);
        360. dev_info(dev,"watchdog%sactive,reset%sabled,irq%sabledn",
        361. (wtcon&S3C2410_WTCON_ENABLE)?"":"in",
        362. (wtcon&S3C2410_WTCON_RSTEN)?"":"dis",
        363. (wtcon&S3C2410_WTCON_INTEN)?"":"en");
        364. //設置GPB10端口為輸出端口,用于點亮LED10
        365. s3c2410_gpio_cfgpin(S3C2410_GPB10,S3C2410_GPB10_OUTP);
        366. return0;
        367. //出錯跳轉表,異常處理
        368. err_clk:
        369. clk_disable(wdt_clock);
        370. clk_put(wdt_clock);
        371. err_irq:
        372. free_irq(wdt_irq->start,pdev);
        373. err_map:
        374. iounmap(wdt_base);
        375. err_req:
        376. release_resource(wdt_mem);
        377. kfree(wdt_mem);
        378. returnret;
        379. }
        380. //設備移除函數,釋放資源和映射
        381. staticints3c2410wdt_remove(structplatform_device*dev)
        382. {
        383. release_resource(wdt_mem);
        384. kfree(wdt_mem);
        385. wdt_mem=NULL;
        386. free_irq(wdt_irq->start,dev);
        387. wdt_irq=NULL;
        388. clk_disable(wdt_clock);
        389. clk_put(wdt_clock);
        390. wdt_clock=NULL;
        391. iounmap(wdt_base);
        392. misc_deregister(&s3c2410wdt_miscdev);
        393. return0;
        394. }
        395. //關閉看門狗
        396. staticvoids3c2410wdt_shutdown(structplatform_device*dev)
        397. {
        398. s3c2410wdt_stop();
        399. }
        400. //定義平臺設備驅動
        401. staticstructplatform_drivers3c2410wdt_driver={
        402. .probe=s3c2410wdt_probe,
        403. .remove=s3c2410wdt_remove,
        404. .shutdown=s3c2410wdt_shutdown,
        405. .driver={
        406. .owner=THIS_MODULE,
        407. .name="s3c2410-wdt",//該名稱參照內核源碼中該資源的名稱,兩者必須一致
        408. },
        409. };
        410. staticcharbanner[]__initdata=
        411. KERN_INFO"S3C2410WatchdogTimer,(c)2004SimtecElectronicsn";
        412. //驅動安裝時執行
        413. staticint__initwatchdog_init(void)
        414. {
        415. printk(banner);
        416. returnplatform_driver_register(&s3c2410wdt_driver);//注冊設備
        417. }
        418. //驅動移除是執行
        419. staticvoid__exitwatchdog_exit(void)
        420. {
        421. platform_driver_unregister(&s3c2410wdt_driver);//移除設備
        422. }
        423. module_init(watchdog_init);
        424. module_exit(watchdog_exit);
        425. MODULE_AUTHOR("YanMing");
        426. MODULE_DESCRIPTION("S3C2410WatchdogDeviceDriver");
        427. MODULE_LICENSE("GPL");
        428. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

        設置默認不是重啟機器,而是執行中斷函數,當不喂狗,計數器減到0,點亮LED,然后喂狗,重新計數。


        關鍵詞: ARMLinux驅動看門

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 涪陵区| 三都| 磴口县| 安康市| 正阳县| 巴马| 张家港市| 鄂伦春自治旗| 南陵县| 犍为县| 乐业县| 唐海县| 奉化市| 三亚市| 宁国市| 齐齐哈尔市| 当涂县| 呼和浩特市| 平潭县| 武义县| 鹤岗市| 旅游| 修文县| 普陀区| 涞源县| 枣阳市| 焉耆| 盐边县| 青冈县| 瓮安县| 商都县| 小金县| 屏东县| 垦利县| 中山市| 马龙县| 时尚| 乌什县| 平顺县| 屏东县| 宁夏|