machzwd.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:11k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  MachZ ZF-Logic Watchdog Timer driver for Linux
  3.  *  
  4.  * 
  5.  *  This program is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU General Public License
  7.  *  as published by the Free Software Foundation; either version
  8.  *  2 of the License, or (at your option) any later version.
  9.  *
  10.  *  The author does NOT admit liability nor provide warranty for
  11.  *  any of this software. This material is provided "AS-IS" in
  12.  *  the hope that it may be useful for others.
  13.  *
  14.  *  Author: Fernando Fuganti <fuganti@conectiva.com.br>
  15.  *
  16.  *  Based on sbc60xxwdt.c by Jakob Oestergaard
  17.  * 
  18.  *
  19.  *  We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the 
  20.  *  following periods:
  21.  *      wd#1 - 2 seconds;
  22.  *      wd#2 - 7.2 ms;
  23.  *  After the expiration of wd#1, it can generate a NMI, SCI, SMI, or 
  24.  *  a system RESET and it starts wd#2 that unconditionaly will RESET 
  25.  *  the system when the counter reaches zero.
  26.  *
  27.  */
  28. #include <linux/config.h>
  29. #include <linux/module.h>
  30. #include <linux/version.h>
  31. #include <linux/types.h>
  32. #include <linux/errno.h>
  33. #include <linux/kernel.h>
  34. #include <linux/timer.h>
  35. #include <linux/sched.h>
  36. #include <linux/miscdevice.h>
  37. #include <linux/watchdog.h>
  38. #include <linux/slab.h>
  39. #include <linux/ioport.h>
  40. #include <linux/fcntl.h>
  41. #include <linux/smp_lock.h>
  42. #include <asm/io.h>
  43. #include <asm/uaccess.h>
  44. #include <asm/system.h>
  45. #include <linux/notifier.h>
  46. #include <linux/reboot.h>
  47. #include <linux/init.h>
  48. /* ports */
  49. #define ZF_IOBASE 0x218
  50. #define INDEX 0x218
  51. #define DATA_B 0x219
  52. #define DATA_W 0x21A
  53. #define DATA_D 0x21A
  54. /* indexes */ /* size */
  55. #define ZFL_VERSION 0x02 /* 16   */
  56. #define CONTROL  0x10 /* 16   */
  57. #define STATUS 0x12 /* 8    */
  58. #define COUNTER_1 0x0C /* 16   */
  59. #define COUNTER_2 0x0E /* 8    */
  60. #define PULSE_LEN 0x0F /* 8    */
  61. /* controls */
  62. #define ENABLE_WD1 0x0001
  63. #define ENABLE_WD2 0x0002
  64. #define RESET_WD1 0x0010
  65. #define RESET_WD2 0x0020
  66. #define GEN_SCI 0x0100
  67. #define GEN_NMI 0x0200
  68. #define GEN_SMI 0x0400
  69. #define GEN_RESET 0x0800
  70. /* utilities */
  71. #define WD1 0
  72. #define WD2 1
  73. #define zf_writew(port, data)  { outb(port, INDEX); outw(data, DATA_W); }
  74. #define zf_writeb(port, data)  { outb(port, INDEX); outb(data, DATA_B); }
  75. #define zf_get_ZFL_version()   zf_readw(ZFL_VERSION)
  76. static unsigned short zf_readw(unsigned char port)
  77. {
  78. outb(port, INDEX);
  79. return inw(DATA_W);
  80. }
  81. static unsigned short zf_readb(unsigned char port)
  82. {
  83. outb(port, INDEX);
  84. return inb(DATA_B);
  85. }
  86. MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
  87. MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
  88. MODULE_LICENSE("GPL");
  89. MODULE_PARM(action, "i");
  90. MODULE_PARM_DESC(action, "after watchdog resets, generate: 0 = RESET(*)  1 = SMI  2 = NMI  3 = SCI");
  91. #define PFX "machzwd"
  92. static struct watchdog_info zf_info = {
  93. options: WDIOF_KEEPALIVEPING, 
  94. firmware_version: 1, 
  95. identity: "ZF-Logic watchdog"
  96. };
  97. /*
  98.  * action refers to action taken when watchdog resets
  99.  * 0 = GEN_RESET
  100.  * 1 = GEN_SMI
  101.  * 2 = GEN_NMI
  102.  * 3 = GEN_SCI
  103.  * defaults to GEN_RESET (0)
  104.  */
  105. static int action = 0;
  106. static int zf_action = GEN_RESET;
  107. static int zf_is_open = 0;
  108. static int zf_expect_close = 0;
  109. static spinlock_t zf_lock;
  110. static spinlock_t zf_port_lock;
  111. static struct timer_list zf_timer;
  112. static unsigned long next_heartbeat = 0;
  113. /* timeout for user land heart beat (10 seconds) */
  114. #define ZF_USER_TIMEO (HZ*10)
  115. /* timeout for hardware watchdog (~500ms) */
  116. #define ZF_HW_TIMEO (HZ/2)
  117. /* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
  118. #define ZF_CTIMEOUT 0xffff
  119. #ifndef ZF_DEBUG
  120. # define dprintk(format, args...)
  121. #else
  122. # define dprintk(format, args...) printk(KERN_DEBUG PFX; ":" __FUNCTION__ ":%d: " format, __LINE__ , ## args)
  123. #endif
  124. /* STATUS register functions */
  125. static inline unsigned char zf_get_status(void)
  126. {
  127. return zf_readb(STATUS);
  128. }
  129. static inline void zf_set_status(unsigned char new)
  130. {
  131. zf_writeb(STATUS, new);
  132. }
  133. /* CONTROL register functions */
  134. static inline unsigned short zf_get_control(void)
  135. {
  136. return zf_readw(CONTROL);
  137. }
  138. static inline void zf_set_control(unsigned short new)
  139. {
  140. zf_writew(CONTROL, new);
  141. }
  142. /* WD#? counter functions */
  143. /*
  144.  * Just get current counter value
  145.  */
  146. static inline unsigned short zf_get_timer(unsigned char n)
  147. {
  148. switch(n){
  149. case WD1:
  150. return zf_readw(COUNTER_1);
  151. case WD2:
  152. return zf_readb(COUNTER_2);
  153. default:
  154. return 0;
  155. }
  156. }
  157. /*
  158.  * Just set counter value
  159.  */
  160. static inline void zf_set_timer(unsigned short new, unsigned char n)
  161. {
  162. switch(n){
  163. case WD1:
  164. zf_writew(COUNTER_1, new);
  165. case WD2:
  166. zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
  167. default:
  168. return;
  169. }
  170. }
  171. /*
  172.  * stop hardware timer
  173.  */
  174. static void zf_timer_off(void)
  175. {
  176. unsigned int ctrl_reg = 0;
  177. unsigned long flags;
  178. /* stop internal ping */
  179. del_timer_sync(&zf_timer);
  180. spin_lock_irqsave(&zf_port_lock, flags);
  181. /* stop watchdog timer */
  182. ctrl_reg = zf_get_control();
  183. ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */
  184. ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
  185. zf_set_control(ctrl_reg);
  186. spin_unlock_irqrestore(&zf_port_lock, flags);
  187. printk(KERN_INFO PFX ": Watchdog timer is now disabledn");
  188. }
  189. /*
  190.  * start hardware timer 
  191.  */
  192. static void zf_timer_on(void)
  193. {
  194. unsigned int ctrl_reg = 0;
  195. unsigned long flags;
  196. spin_lock_irqsave(&zf_port_lock, flags);
  197. zf_writeb(PULSE_LEN, 0xff);
  198. zf_set_timer(ZF_CTIMEOUT, WD1);
  199. /* user land ping */
  200. next_heartbeat = jiffies + ZF_USER_TIMEO;
  201. /* start the timer for internal ping */
  202. zf_timer.expires = jiffies + ZF_HW_TIMEO;
  203. add_timer(&zf_timer);
  204. /* start watchdog timer */
  205. ctrl_reg = zf_get_control();
  206. ctrl_reg |= (ENABLE_WD1|zf_action);
  207. zf_set_control(ctrl_reg);
  208. spin_unlock_irqrestore(&zf_port_lock, flags);
  209. printk(KERN_INFO PFX ": Watchdog timer is now enabledn");
  210. }
  211. static void zf_ping(unsigned long data)
  212. {
  213. unsigned int ctrl_reg = 0;
  214. unsigned long flags;
  215. zf_writeb(COUNTER_2, 0xff);
  216. if(time_before(jiffies, next_heartbeat)){
  217. dprintk("time_before: %ldn", next_heartbeat - jiffies);
  218. /* 
  219.  * reset event is activated by transition from 0 to 1 on
  220.  * RESET_WD1 bit and we assume that it is already zero...
  221.  */
  222. spin_lock_irqsave(&zf_port_lock, flags);
  223. ctrl_reg = zf_get_control();    
  224. ctrl_reg |= RESET_WD1;
  225. zf_set_control(ctrl_reg);
  226. /* ...and nothing changes until here */
  227. ctrl_reg &= ~(RESET_WD1);
  228. zf_set_control(ctrl_reg);
  229. spin_unlock_irqrestore(&zf_port_lock, flags);
  230. zf_timer.expires = jiffies + ZF_HW_TIMEO;
  231. add_timer(&zf_timer);
  232. }else{
  233. printk(KERN_CRIT PFX ": I will reset your machinen");
  234. }
  235. }
  236. static ssize_t zf_write(struct file *file, const char *buf, size_t count, 
  237. loff_t *ppos)
  238. {
  239. /*  Can't seek (pwrite) on this device  */
  240. if (ppos != &file->f_pos)
  241. return -ESPIPE;
  242. /* See if we got the magic character */
  243. if(count){
  244. /*
  245.  * no need to check for close confirmation
  246.  * no way to disable watchdog ;)
  247.  */
  248. #ifndef CONFIG_WATCHDOG_NOWAYOUT
  249. size_t ofs;
  250. /* 
  251.  * note: just in case someone wrote the magic character
  252.  * five months ago...
  253.  */
  254. zf_expect_close = 0;
  255. /* now scan */
  256. for(ofs = 0; ofs != count; ofs++){
  257. if(buf[ofs] == 'V'){
  258. zf_expect_close = 1;
  259. dprintk("zf_expect_close 1n");
  260. }
  261. }
  262. #endif
  263. /*
  264.  * Well, anyhow someone wrote to us,
  265.  * we should return that favour
  266.  */
  267. next_heartbeat = jiffies + ZF_USER_TIMEO;
  268. dprintk("user ping at %ldn", jiffies);
  269. return 1;
  270. }
  271. return 0;
  272. }
  273. static ssize_t zf_read(struct file *file, char *buf, size_t count, 
  274. loff_t *ppos)
  275. {
  276. return -EINVAL;
  277. }
  278. static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  279. unsigned long arg)
  280. {
  281. int ret;
  282. switch(cmd){
  283. case WDIOC_GETSUPPORT:
  284. ret = copy_to_user((struct watchdog_info *)arg, 
  285. &zf_info, sizeof(zf_info));
  286. if(ret)
  287. return -EFAULT;
  288. break;
  289.   
  290. case WDIOC_GETSTATUS:
  291. ret = copy_to_user((int *)arg, &zf_is_open,
  292. sizeof(int));
  293. if(ret)
  294. return -EFAULT;
  295. break;
  296. case WDIOC_KEEPALIVE:
  297. zf_ping(0);
  298. break;
  299. default:
  300. return -ENOTTY;
  301. }
  302. return 0;
  303. }
  304. static int zf_open(struct inode *inode, struct file *file)
  305. {
  306. switch(MINOR(inode->i_rdev)){
  307. case WATCHDOG_MINOR:
  308. spin_lock(&zf_lock);
  309. if(zf_is_open){
  310. spin_unlock(&zf_lock);
  311. return -EBUSY;
  312. }
  313. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  314. MOD_INC_USE_COUNT;
  315. #endif
  316. zf_is_open = 1;
  317. spin_unlock(&zf_lock);
  318. zf_timer_on();
  319. return 0;
  320. default:
  321. return -ENODEV;
  322. }
  323. }
  324. static int zf_close(struct inode *inode, struct file *file)
  325. {
  326. if(MINOR(inode->i_rdev) == WATCHDOG_MINOR){
  327. if(zf_expect_close){
  328. zf_timer_off();
  329. } else {
  330. del_timer(&zf_timer);
  331. printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!n");
  332. }
  333. spin_lock(&zf_lock);
  334. zf_is_open = 0;
  335. spin_unlock(&zf_lock);
  336. zf_expect_close = 0;
  337. }
  338. return 0;
  339. }
  340. /*
  341.  * Notifier for system down
  342.  */
  343. static int zf_notify_sys(struct notifier_block *this, unsigned long code,
  344. void *unused)
  345. {
  346. if(code == SYS_DOWN || code == SYS_HALT){
  347. zf_timer_off();
  348. }
  349. return NOTIFY_DONE;
  350. }
  351. static struct file_operations zf_fops = {
  352. owner:          THIS_MODULE,
  353. read:           zf_read,
  354. write:          zf_write,
  355. ioctl:          zf_ioctl,
  356. open:           zf_open,
  357. release:        zf_close,
  358. };
  359. static struct miscdevice zf_miscdev = {
  360. WATCHDOG_MINOR,
  361. "watchdog",
  362. &zf_fops
  363. };
  364.                                                                         
  365. /*
  366.  * The device needs to learn about soft shutdowns in order to
  367.  * turn the timebomb registers off.
  368.  */
  369. static struct notifier_block zf_notifier = {
  370. zf_notify_sys,
  371. NULL,
  372. 0
  373. };
  374. static void __init zf_show_action(int act)
  375. {
  376. char *str[] = { "RESET", "SMI", "NMI", "SCI" };
  377. printk(KERN_INFO PFX ": Watchdog using action = %sn", str[act]);
  378. }
  379. static int __init zf_init(void)
  380. {
  381. int ret;
  382. printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.n");
  383. ret = zf_get_ZFL_version();
  384. printk("%#xn", ret);
  385. if((!ret) || (ret != 0xffff)){
  386. printk(KERN_WARNING PFX ": no ZF-Logic foundn");
  387. return -ENODEV;
  388. }
  389. if((action <= 3) && (action >= 0)){
  390. zf_action = zf_action>>action;
  391. } else
  392. action = 0;
  393. zf_show_action(action);
  394. spin_lock_init(&zf_lock);
  395. spin_lock_init(&zf_port_lock);
  396. ret = misc_register(&zf_miscdev);
  397. if (ret){
  398. printk(KERN_ERR "can't misc_register on minor=%dn",
  399. WATCHDOG_MINOR);
  400. goto out;
  401. }
  402. if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){
  403. printk(KERN_ERR "cannot reserve I/O ports at %dn",
  404. ZF_IOBASE);
  405. ret = -EBUSY;
  406. goto no_region;
  407. }
  408. ret = register_reboot_notifier(&zf_notifier);
  409. if(ret){
  410. printk(KERN_ERR "can't register reboot notifier (err=%d)n",
  411. ret);
  412. goto no_reboot;
  413. }
  414. zf_set_status(0);
  415. zf_set_control(0);
  416. /* this is the timer that will do the hard work */
  417. init_timer(&zf_timer);
  418. zf_timer.function = zf_ping;
  419. zf_timer.data = 0;
  420. return 0;
  421. no_reboot:
  422. release_region(ZF_IOBASE, 3);
  423. no_region:
  424. misc_deregister(&zf_miscdev);
  425. out:
  426. return ret;
  427. }
  428. void __exit zf_exit(void)
  429. {
  430. zf_timer_off();
  431. misc_deregister(&zf_miscdev);
  432. unregister_reboot_notifier(&zf_notifier);
  433. release_region(ZF_IOBASE, 3);
  434. }
  435. module_init(zf_init);
  436. module_exit(zf_exit);