pc87413_wdt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*
  2. * NS pc87413-wdt Watchdog Timer driver for Linux 2.6.x.x
  3. *
  4. * This code is based on wdt.c with original copyright
  5. *
  6. * (C) Copyright 2006 Marcus Junker, <junker@anduras.de>
  7. * and Sven Anders, <anders@anduras.de>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version
  12. * 2 of the License, or (at your option) any later version.
  13. *
  14. * Neither Marcus Junker, Sven Anders nor ANDURAS AG
  15. * admit liability nor provide warranty for any of this software.
  16. * This material is provided "AS-IS" and at no charge.
  17. *
  18. * Release 1.00.
  19. *
  20. */
  21. /* #define DEBUG 1 */
  22. #include <linux/config.h>
  23. #include <linux/module.h>
  24. #include <linux/types.h>
  25. #include <linux/miscdevice.h>
  26. #include <linux/watchdog.h>
  27. #include <linux/ioport.h>
  28. #include <linux/delay.h>
  29. #include <linux/notifier.h>
  30. #include <linux/fs.h>
  31. #include <linux/reboot.h>
  32. #include <linux/init.h>
  33. #include <linux/spinlock.h>
  34. #include <linux/moduleparam.h>
  35. #include <linux/version.h>
  36. #include <asm/io.h>
  37. #include <asm/uaccess.h>
  38. #include <asm/system.h>
  39. #define WATCHDOG_NAME "pc87413 WDT"
  40. #define PFX WATCHDOG_NAME ": "
  41. #define DPFX WATCHDOG_NAME " - DEBUG: "
  42. #define WDT_INDEX_IO_PORT (io+0) /* */
  43. #define WDT_DATA_IO_PORT (WDT_INDEX_IO_PORT+1)
  44. #define SWC_LDN 0x04
  45. #define SIOCFG2 0x22 /* Serial IO register */
  46. #define WDCTL 0x10 /* Watchdog-Timer-Controll-Register */
  47. #define WDTO 0x11 /* Watchdog timeout register */
  48. #define WDCFG 0x12 /* Watchdog config register */
  49. #define WD_TIMEOUT 1 /* minutes (1 ... 255) */
  50. static int pc87413_is_open=0;
  51. /*
  52. * You must set these - there is no sane way to probe for this board.
  53. * You can use pc87413=x to set these now.
  54. */
  55. /**
  56. * module_params
  57. *
  58. * Setup options. The board isn't really probe-able so we have to
  59. * get the user to tell us the configuration.
  60. */
  61. static int io=0x2E; /* Normally used addres on Portwell Boards */
  62. module_param(io, int, 0);
  63. MODULE_PARM_DESC(wdt_io, WATCHDOG_NAME " io port (default 0x2E)");
  64. static int timeout = WD_TIMEOUT; /* in minutes */
  65. module_param(timeout, int, 0);
  66. MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes. 1<= timeout <=63, default=" __MODULE_STRING(WD_TIMEOUT) ".");
  67. /******************************************
  68. * Helper functions
  69. ******************************************/
  70. static void
  71. pc87413_select_wdt_out (void)
  72. {
  73. unsigned int cr_data=0;
  74. /* Select multiple pin,pin55,as WDT output */
  75. outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
  76. cr_data = inb (WDT_DATA_IO_PORT);
  77. cr_data |= 0x80; /* Set Bit7 to 1*/
  78. outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
  79. outb_p(cr_data, WDT_DATA_IO_PORT);
  80. #ifdef DEBUG
  81. printk(KERN_INFO DPFX "Select multiple pin,pin55,as WDT output: Bit7 to 1: %d\n", cr_data);
  82. #endif
  83. }
  84. static void
  85. pc87413_enable_swc(void)
  86. {
  87. unsigned int cr_data=0;
  88. /* Step 2: Enable SWC functions */
  89. outb_p(0x07, WDT_INDEX_IO_PORT); /* Point SWC_LDN (LDN=4) */
  90. outb_p(SWC_LDN, WDT_DATA_IO_PORT);
  91. outb_p(0x30, WDT_INDEX_IO_PORT); /* Read Index 0x30 First */
  92. cr_data = inb (WDT_DATA_IO_PORT);
  93. cr_data |= 0x01; /* Set Bit0 to 1 */
  94. outb_p(0x30, WDT_INDEX_IO_PORT);
  95. outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */
  96. #ifdef DEBUG
  97. printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n");
  98. #endif
  99. }
  100. static unsigned int
  101. pc87413_get_swc_base(void)
  102. {
  103. unsigned int swc_base_addr = 0;
  104. unsigned char addr_l, addr_h = 0;
  105. /* Step 3: Read SWC I/O Base Address */
  106. outb_p(0x60, WDT_INDEX_IO_PORT); /* Read Index 0x60 */
  107. addr_h = inb (WDT_DATA_IO_PORT);
  108. outb_p(0x61, WDT_INDEX_IO_PORT); /* Read Index 0x61 */
  109. addr_l = inb (WDT_DATA_IO_PORT);
  110. swc_base_addr = (addr_h << 8) + addr_l;
  111. #ifdef DEBUG
  112. printk(KERN_INFO DPFX "Read SWC I/O Base Address: low %d, high %d, res %d\n", addr_l, addr_h, swc_base_addr);
  113. #endif
  114. return swc_base_addr;
  115. }
  116. static void
  117. pc87413_swc_bank3(unsigned int swc_base_addr)
  118. {
  119. /* Step 4: Select Bank3 of SWC */
  120. outb_p (inb (swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
  121. #ifdef DEBUG
  122. printk(KERN_INFO DPFX "Select Bank3 of SWC\n");
  123. #endif
  124. }
  125. static void
  126. pc87413_programm_wdto (unsigned int swc_base_addr, char pc87413_time)
  127. {
  128. /* Step 5: Programm WDTO, Twd. */
  129. outb_p (pc87413_time, swc_base_addr + WDTO);
  130. #ifdef DEBUG
  131. printk(KERN_INFO DPFX "Set WDTO to %d minutes\n", pc87413_time);
  132. #endif
  133. }
  134. static void
  135. pc87413_enable_wden (unsigned int swc_base_addr)
  136. {
  137. /* Step 6: Enable WDEN */
  138. outb_p(inb (swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
  139. #ifdef DEBUG
  140. printk(KERN_INFO DPFX "Enable WDEN\n");
  141. #endif
  142. }
  143. static void
  144. pc87413_enable_sw_wd_tren (unsigned int swc_base_addr)
  145. {
  146. /* Enable SW_WD_TREN */
  147. outb_p(inb (swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
  148. #ifdef DEBUG
  149. printk(KERN_INFO DPFX "Enable SW_WD_TREN\n");
  150. #endif
  151. }
  152. static void
  153. pc87413_disable_sw_wd_tren (unsigned int swc_base_addr)
  154. {
  155. /* Disable SW_WD_TREN */
  156. outb_p(inb (swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
  157. #ifdef DEBUG
  158. printk(KERN_INFO DPFX "pc87413 - Disable SW_WD_TREN\n");
  159. #endif
  160. }
  161. static void
  162. pc87413_enable_sw_wd_trg (unsigned int swc_base_addr)
  163. {
  164. /* Enable SW_WD_TRG */
  165. outb_p(inb (swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
  166. #ifdef DEBUG
  167. printk(KERN_INFO DPFX "pc87413 - Enable SW_WD_TRG\n");
  168. #endif
  169. }
  170. static void
  171. pc87413_disable_sw_wd_trg (unsigned int swc_base_addr)
  172. {
  173. /* Disable SW_WD_TRG */
  174. outb_p(inb (swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
  175. #ifdef DEBUG
  176. printk(KERN_INFO DPFX "Disable SW_WD_TRG\n");
  177. #endif
  178. }
  179. static void
  180. pc87413_disable(void)
  181. {
  182. unsigned int swc_base_addr;
  183. pc87413_select_wdt_out();
  184. pc87413_enable_swc();
  185. swc_base_addr = pc87413_get_swc_base();
  186. pc87413_swc_bank3(swc_base_addr);
  187. pc87413_disable_sw_wd_tren(swc_base_addr);
  188. pc87413_disable_sw_wd_trg(swc_base_addr);
  189. pc87413_programm_wdto(swc_base_addr, 0);
  190. }
  191. static void
  192. pc87413_refresh(char pc87413_time)
  193. {
  194. unsigned int swc_base_addr;
  195. pc87413_select_wdt_out();
  196. pc87413_enable_swc();
  197. swc_base_addr = pc87413_get_swc_base();
  198. pc87413_swc_bank3(swc_base_addr);
  199. pc87413_disable_sw_wd_tren(swc_base_addr);
  200. pc87413_disable_sw_wd_trg(swc_base_addr);
  201. pc87413_programm_wdto(swc_base_addr, pc87413_time);
  202. pc87413_enable_wden(swc_base_addr);
  203. pc87413_enable_sw_wd_tren(swc_base_addr);
  204. pc87413_enable_sw_wd_trg(swc_base_addr);
  205. }
  206. static void
  207. pc87413_enable(char pc87413_time)
  208. {
  209. unsigned int swc_base_addr;
  210. pc87413_select_wdt_out();
  211. pc87413_enable_swc();
  212. swc_base_addr = pc87413_get_swc_base();
  213. pc87413_swc_bank3(swc_base_addr);
  214. pc87413_programm_wdto(swc_base_addr, pc87413_time);
  215. pc87413_enable_wden(swc_base_addr);
  216. pc87413_enable_sw_wd_tren(swc_base_addr);
  217. pc87413_enable_sw_wd_trg(swc_base_addr);
  218. }
  219. /*******************************************
  220. * Kernel methods.
  221. *******************************************/
  222. /**
  223. * pc87413_status:
  224. *
  225. * Extract the status information from a WDT watchdog device. There are
  226. * several board variants so we have to know which bits are valid. Some
  227. * bits default to one and some to zero in order to be maximally painful.
  228. *
  229. * we then map the bits onto the status ioctl flags.
  230. */
  231. static int pc87413_status(void)
  232. {
  233. /* Not supported */
  234. return 1;
  235. }
  236. static long long pc87413_llseek(struct file *file, long long offset, int origin)
  237. {
  238. return -ESPIPE;
  239. }
  240. /**
  241. * pc87413_write:
  242. * @file: file handle to the watchdog
  243. * @buf: buffer to write (unused as data does not matter here
  244. * @count: count of bytes
  245. * @ppos: pointer to the position to write. No seeks allowed
  246. *
  247. * A write to a watchdog device is defined as a keepalive signal. Any
  248. * write of data will do, as we we don't define content meaning.
  249. */
  250. static ssize_t
  251. pc87413_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  252. {
  253. if (count) {
  254. pc87413_refresh (WD_TIMEOUT);
  255. #ifdef DEBUG
  256. printk(KERN_INFO DPFX "Write\n");
  257. #endif
  258. }
  259. return count;
  260. }
  261. /*
  262. * Read reports the temperature in degrees Fahrenheit.
  263. */
  264. static ssize_t
  265. pc87413_read(struct file *file, char *buf, size_t count, loff_t *ptr)
  266. {
  267. // char timeout;
  268. //
  269. // outb_p(0x08, WDT_EFER); /* Select locical device 8 */
  270. // outb_p(0x0F6, WDT_EFER); /* Select CRF6 */
  271. // timeout = inb(WDT_EFDR); /* Read Timeout counter from CRF6 */
  272. // if(copy_to_user(buf,&timeout,1))
  273. // return -EFAULT;
  274. return 1;
  275. }
  276. /**
  277. * pc87413_ioctl:
  278. * @inode: inode of the device
  279. * @file: file handle to the device
  280. * @cmd: watchdog command
  281. * @arg: argument pointer
  282. *
  283. * The watchdog API defines a common set of functions for all watchdogs
  284. * according to their available features. We only actually usefully support
  285. * querying capabilities and current status.
  286. */
  287. static int
  288. pc87413_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  289. unsigned long arg)
  290. {
  291. static struct watchdog_info ident=
  292. {
  293. .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
  294. .firmware_version = 1,
  295. .identity = "pc87413(HF/F)"
  296. };
  297. ident.options=1; /* Mask down to the card we have */
  298. switch(cmd)
  299. {
  300. default:
  301. return -ENOIOCTLCMD;
  302. case WDIOC_GETSUPPORT:
  303. return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
  304. case WDIOC_GETSTATUS:
  305. return put_user(pc87413_status(),(int *)arg);
  306. case WDIOC_GETBOOTSTATUS:
  307. return put_user(0, (int *)arg);
  308. case WDIOC_KEEPALIVE:
  309. pc87413_refresh(WD_TIMEOUT);
  310. #ifdef DEBUG
  311. printk(KERN_INFO DPFX "keepalive\n");
  312. #endif
  313. return 0;
  314. }
  315. }
  316. /**
  317. * pc87413_open:
  318. * @inode: inode of device
  319. * @file: file handle to device
  320. *
  321. * One of our two misc devices has been opened. The watchdog device is
  322. * single open and on opening we load the counters. Counter zero is a
  323. * 100Hz cascade, into counter 1 which downcounts to reboot. When the
  324. * counter triggers counter 2 downcounts the length of the reset pulse
  325. * which set set to be as long as possible.
  326. */
  327. static int
  328. pc87413_open(struct inode *inode, struct file *file)
  329. {
  330. switch(MINOR(inode->i_rdev))
  331. {
  332. case WATCHDOG_MINOR:
  333. if(pc87413_is_open)
  334. return -EBUSY;
  335. /*
  336. * Activate
  337. */
  338. pc87413_is_open=1;
  339. pc87413_refresh(WD_TIMEOUT);
  340. #ifdef DEBUG
  341. printk(KERN_INFO DPFX "Open\n");
  342. #endif
  343. return 0;
  344. case TEMP_MINOR:
  345. return 0;
  346. default:
  347. return -ENODEV;
  348. }
  349. }
  350. /**
  351. * pc87413_close:
  352. * @inode: inode to board
  353. * @file: file handle to board
  354. *
  355. * The watchdog has a configurable API. There is a religious dispute
  356. * between people who want their watchdog to be able to shut down and
  357. * those who want to be sure if the watchdog manager dies the machine
  358. * reboots. In the former case we disable the counters, in the latter
  359. * case you have to open it again very soon.
  360. */
  361. static int
  362. pc87413_release(struct inode *inode, struct file *file)
  363. {
  364. if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
  365. {
  366. #ifndef CONFIG_WATCHDOG_NOWAYOUT
  367. pc87413_disable();
  368. #endif
  369. pc87413_is_open=0;
  370. #ifdef DEBUG
  371. printk(KERN_INFO DPFX "Release\n");
  372. #endif
  373. }
  374. return 0;
  375. }
  376. /**
  377. * notify_sys:
  378. * @this: our notifier block
  379. * @code: the event being reported
  380. * @unused: unused
  381. *
  382. * Our notifier is called on system shutdowns. We want to turn the card
  383. * off at reboot otherwise the machine will reboot again during memory
  384. * test or worse yet during the following fsck. This would suck, in fact
  385. * trust me - if it happens it does suck.
  386. */
  387. static int
  388. pc87413_notify_sys(struct notifier_block *this, unsigned long code,
  389. void *unused)
  390. {
  391. if(code==SYS_DOWN || code==SYS_HALT)
  392. {
  393. /* Turn the card off */
  394. pc87413_disable();
  395. }
  396. return NOTIFY_DONE;
  397. }
  398. /*****************************************************
  399. * Kernel Interfaces
  400. *****************************************************/
  401. static struct file_operations pc87413_fops = {
  402. .owner = THIS_MODULE,
  403. .llseek = pc87413_llseek,
  404. .read = pc87413_read,
  405. .write = pc87413_write,
  406. .ioctl = pc87413_ioctl,
  407. .open = pc87413_open,
  408. .release = pc87413_release,
  409. };
  410. static struct miscdevice pc87413_miscdev=
  411. {
  412. .minor = WATCHDOG_MINOR,
  413. .name = "watchdog",
  414. .fops = &pc87413_fops
  415. };
  416. /*
  417. * The WDT card needs to learn about soft shutdowns in order to
  418. * turn the timebomb registers off.
  419. */
  420. static struct notifier_block pc87413_notifier=
  421. {
  422. pc87413_notify_sys,
  423. NULL,
  424. 0
  425. };
  426. /**
  427. * pc87413_exit:
  428. *
  429. * Unload the watchdog. You cannot do this with any file handles open.
  430. * If your watchdog is set to continue ticking on close and you unload
  431. * it, well it keeps ticking. We won't get the interrupt but the board
  432. * will not touch PC memory so all is fine. You just have to load a new
  433. * module in 60 seconds or reboot.
  434. */
  435. static void
  436. pc87413_exit(void)
  437. {
  438. pc87413_disable();
  439. misc_deregister(&pc87413_miscdev);
  440. unregister_reboot_notifier(&pc87413_notifier);
  441. /* release_region(io,2); */
  442. }
  443. /**
  444. * pc87413_init:
  445. *
  446. * Set up the WDT watchdog board. All we have to do is grab the
  447. * resources we require and bitch if anyone beat us to them.
  448. * The open() function will actually kick the board off.
  449. */
  450. static int
  451. pc87413_init(void)
  452. {
  453. int ret;
  454. printk(KERN_INFO PFX "Version 1.00 at io 0x%X\n", WDT_INDEX_IO_PORT);
  455. /* request_region(io, 2, "pc87413"); */
  456. ret = register_reboot_notifier(&pc87413_notifier);
  457. if (ret != 0) {
  458. printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
  459. ret);
  460. }
  461. ret = misc_register(&pc87413_miscdev);
  462. if (ret != 0) {
  463. printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
  464. WATCHDOG_MINOR, ret);
  465. unregister_reboot_notifier(&pc87413_notifier);
  466. return ret;
  467. }
  468. printk (KERN_INFO PFX "initialized. timeout=%d min \n", timeout);
  469. pc87413_enable(WD_TIMEOUT);
  470. return 0;
  471. }
  472. module_init(pc87413_init);
  473. module_exit(pc87413_exit);
  474. MODULE_LICENSE("GPL");
  475. MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, Sven Anders <anders@anduras.de>");
  476. MODULE_DESCRIPTION("PC87413 WDT driver");
  477. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);