i8xx_tco.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * i8xx_tco: TCO timer driver for i8xx chipsets
  3. *
  4. * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
  5. * http://www.kernelconcepts.de
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version
  10. * 2 of the License, or (at your option) any later version.
  11. *
  12. * Neither kernel concepts nor Nils Faerber admit liability nor provide
  13. * warranty for any of this software. This material is provided
  14. * "AS-IS" and at no charge.
  15. *
  16. * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>
  17. * developed for
  18. * Jentro AG, Haar/Munich (Germany)
  19. *
  20. * TCO timer driver for i8xx chipsets
  21. * based on softdog.c by Alan Cox <alan@redhat.com>
  22. *
  23. * The TCO timer is implemented in the following I/O controller hubs:
  24. * (See the intel documentation on http://developer.intel.com.)
  25. * 82801AA (ICH) : document number 290655-003, 290677-014,
  26. * 82801AB (ICHO) : document number 290655-003, 290677-014,
  27. * 82801BA (ICH2) : document number 290687-002, 298242-027,
  28. * 82801BAM (ICH2-M) : document number 290687-002, 298242-027,
  29. * 82801CA (ICH3-S) : document number 290733-003, 290739-013,
  30. * 82801CAM (ICH3-M) : document number 290716-001, 290718-007,
  31. * 82801DB (ICH4) : document number 290744-001, 290745-020,
  32. * 82801DBM (ICH4-M) : document number 252337-001, 252663-005,
  33. * 82801E (C-ICH) : document number 273599-001, 273645-002,
  34. * 82801EB (ICH5) : document number 252516-001, 252517-003,
  35. * 82801ER (ICH5R) : document number 252516-001, 252517-003,
  36. * 82801FB (ICH6) : document number 301473-002, 301474-007,
  37. * 82801FR (ICH6R) : document number 301473-002, 301474-007,
  38. * 82801FBM (ICH6-M) : document number 301473-002, 301474-007,
  39. * 82801FW (ICH6W) : document number 301473-001, 301474-007,
  40. * 82801FRW (ICH6RW) : document number 301473-001, 301474-007
  41. *
  42. * 20000710 Nils Faerber
  43. * Initial Version 0.01
  44. * 20000728 Nils Faerber
  45. * 0.02 Fix for SMI_EN->TCO_EN bit, some cleanups
  46. * 20011214 Matt Domsch <Matt_Domsch@dell.com>
  47. * 0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
  48. * Didn't add timeout option as i810_margin already exists.
  49. * 20020224 Joel Becker, Wim Van Sebroeck
  50. * 0.04 Support for 82801CA(M) chipset, timer margin needs to be > 3,
  51. * add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT.
  52. * 20020412 Rob Radez <rob@osinvestor.com>, Wim Van Sebroeck
  53. * 0.05 Fix possible timer_alive race, add expect close support,
  54. * clean up ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS and
  55. * WDIOC_SETOPTIONS), made i810tco_getdevice __init,
  56. * removed boot_status, removed tco_timer_read,
  57. * added support for 82801DB and 82801E chipset,
  58. * added support for 82801EB and 8280ER chipset,
  59. * general cleanup.
  60. * 20030921 Wim Van Sebroeck <wim@iguana.be>
  61. * 0.06 change i810_margin to heartbeat, use module_param,
  62. * added notify system support, renamed module to i8xx_tco.
  63. * 20050128 Wim Van Sebroeck <wim@iguana.be>
  64. * 0.07 Added support for the ICH4-M, ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW
  65. * chipsets. Also added support for the "undocumented" ICH7 chipset.
  66. * 20050807 Wim Van Sebroeck <wim@iguana.be>
  67. * 0.08 Make sure that the watchdog is only "armed" when started.
  68. * (Kernel Bug 4251)
  69. */
  70. /*
  71. * Includes, defines, variables, module parameters, ...
  72. */
  73. #include <linux/module.h>
  74. #include <linux/moduleparam.h>
  75. #include <linux/types.h>
  76. #include <linux/miscdevice.h>
  77. #include <linux/watchdog.h>
  78. #include <linux/notifier.h>
  79. #include <linux/reboot.h>
  80. #include <linux/init.h>
  81. #include <linux/fs.h>
  82. #include <linux/pci.h>
  83. #include <linux/ioport.h>
  84. #include <asm/uaccess.h>
  85. #include <asm/io.h>
  86. #include "i8xx_tco.h"
  87. /* Module and version information */
  88. #define TCO_VERSION "0.08"
  89. #define TCO_MODULE_NAME "i8xx TCO timer"
  90. #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
  91. #define PFX TCO_MODULE_NAME ": "
  92. /* internal variables */
  93. static unsigned int ACPIBASE;
  94. static spinlock_t tco_lock; /* Guards the hardware */
  95. static unsigned long timer_alive;
  96. static char tco_expect_close;
  97. static struct pci_dev *i8xx_tco_pci;
  98. /* module parameters */
  99. #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */
  100. static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
  101. module_param(heartbeat, int, 0);
  102. MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
  103. static int nowayout = WATCHDOG_NOWAYOUT;
  104. module_param(nowayout, int, 0);
  105. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
  106. /*
  107. * Some TCO specific functions
  108. */
  109. static inline unsigned char seconds_to_ticks(int seconds)
  110. {
  111. /* the internal timer is stored as ticks which decrement
  112. * every 0.6 seconds */
  113. return (seconds * 10) / 6;
  114. }
  115. static int tco_timer_start (void)
  116. {
  117. unsigned char val;
  118. spin_lock(&tco_lock);
  119. /* disable chipset's NO_REBOOT bit */
  120. pci_read_config_byte (i8xx_tco_pci, 0xd4, &val);
  121. val &= 0xfd;
  122. pci_write_config_byte (i8xx_tco_pci, 0xd4, val);
  123. /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
  124. val = inb (TCO1_CNT + 1);
  125. val &= 0xf7;
  126. outb (val, TCO1_CNT + 1);
  127. val = inb (TCO1_CNT + 1);
  128. spin_unlock(&tco_lock);
  129. if (val & 0x08)
  130. return -1;
  131. return 0;
  132. }
  133. static int tco_timer_stop (void)
  134. {
  135. unsigned char val, val1;
  136. spin_lock(&tco_lock);
  137. /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
  138. val = inb (TCO1_CNT + 1);
  139. val |= 0x08;
  140. outb (val, TCO1_CNT + 1);
  141. val = inb (TCO1_CNT + 1);
  142. /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
  143. pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
  144. val1 |= 0x02;
  145. pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
  146. spin_unlock(&tco_lock);
  147. if ((val & 0x08) == 0)
  148. return -1;
  149. return 0;
  150. }
  151. static int tco_timer_keepalive (void)
  152. {
  153. spin_lock(&tco_lock);
  154. /* Reload the timer by writing to the TCO Timer Reload register */
  155. outb (0x01, TCO1_RLD);
  156. spin_unlock(&tco_lock);
  157. return 0;
  158. }
  159. static int tco_timer_set_heartbeat (int t)
  160. {
  161. unsigned char val;
  162. unsigned char tmrval;
  163. tmrval = seconds_to_ticks(t);
  164. /* from the specs: */
  165. /* "Values of 0h-3h are ignored and should not be attempted" */
  166. if (tmrval > 0x3f || tmrval < 0x04)
  167. return -EINVAL;
  168. /* Write new heartbeat to watchdog */
  169. spin_lock(&tco_lock);
  170. val = inb (TCO1_TMR);
  171. val &= 0xc0;
  172. val |= tmrval;
  173. outb (val, TCO1_TMR);
  174. val = inb (TCO1_TMR);
  175. spin_unlock(&tco_lock);
  176. if ((val & 0x3f) != tmrval)
  177. return -EINVAL;
  178. heartbeat = t;
  179. return 0;
  180. }
  181. /*
  182. * /dev/watchdog handling
  183. */
  184. static int i8xx_tco_open (struct inode *inode, struct file *file)
  185. {
  186. /* /dev/watchdog can only be opened once */
  187. if (test_and_set_bit(0, &timer_alive))
  188. return -EBUSY;
  189. /*
  190. * Reload and activate timer
  191. */
  192. tco_timer_keepalive ();
  193. tco_timer_start ();
  194. return nonseekable_open(inode, file);
  195. }
  196. static int i8xx_tco_release (struct inode *inode, struct file *file)
  197. {
  198. /*
  199. * Shut off the timer.
  200. */
  201. if (tco_expect_close == 42) {
  202. tco_timer_stop ();
  203. } else {
  204. printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
  205. tco_timer_keepalive ();
  206. }
  207. clear_bit(0, &timer_alive);
  208. tco_expect_close = 0;
  209. return 0;
  210. }
  211. static ssize_t i8xx_tco_write (struct file *file, const char __user *data,
  212. size_t len, loff_t * ppos)
  213. {
  214. /* See if we got the magic character 'V' and reload the timer */
  215. if (len) {
  216. if (!nowayout) {
  217. size_t i;
  218. /* note: just in case someone wrote the magic character
  219. * five months ago... */
  220. tco_expect_close = 0;
  221. /* scan to see whether or not we got the magic character */
  222. for (i = 0; i != len; i++) {
  223. char c;
  224. if(get_user(c, data+i))
  225. return -EFAULT;
  226. if (c == 'V')
  227. tco_expect_close = 42;
  228. }
  229. }
  230. /* someone wrote to us, we should reload the timer */
  231. tco_timer_keepalive ();
  232. }
  233. return len;
  234. }
  235. static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
  236. unsigned int cmd, unsigned long arg)
  237. {
  238. int new_options, retval = -EINVAL;
  239. int new_heartbeat;
  240. void __user *argp = (void __user *)arg;
  241. int __user *p = argp;
  242. static struct watchdog_info ident = {
  243. .options = WDIOF_SETTIMEOUT |
  244. WDIOF_KEEPALIVEPING |
  245. WDIOF_MAGICCLOSE,
  246. .firmware_version = 0,
  247. .identity = TCO_MODULE_NAME,
  248. };
  249. switch (cmd) {
  250. case WDIOC_GETSUPPORT:
  251. return copy_to_user(argp, &ident,
  252. sizeof (ident)) ? -EFAULT : 0;
  253. case WDIOC_GETSTATUS:
  254. case WDIOC_GETBOOTSTATUS:
  255. return put_user (0, p);
  256. case WDIOC_KEEPALIVE:
  257. tco_timer_keepalive ();
  258. return 0;
  259. case WDIOC_SETOPTIONS:
  260. {
  261. if (get_user (new_options, p))
  262. return -EFAULT;
  263. if (new_options & WDIOS_DISABLECARD) {
  264. tco_timer_stop ();
  265. retval = 0;
  266. }
  267. if (new_options & WDIOS_ENABLECARD) {
  268. tco_timer_keepalive ();
  269. tco_timer_start ();
  270. retval = 0;
  271. }
  272. return retval;
  273. }
  274. case WDIOC_SETTIMEOUT:
  275. {
  276. if (get_user(new_heartbeat, p))
  277. return -EFAULT;
  278. if (tco_timer_set_heartbeat(new_heartbeat))
  279. return -EINVAL;
  280. tco_timer_keepalive ();
  281. /* Fall */
  282. }
  283. case WDIOC_GETTIMEOUT:
  284. return put_user(heartbeat, p);
  285. default:
  286. return -ENOIOCTLCMD;
  287. }
  288. }
  289. /*
  290. * Notify system
  291. */
  292. static int i8xx_tco_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
  293. {
  294. if (code==SYS_DOWN || code==SYS_HALT) {
  295. /* Turn the WDT off */
  296. tco_timer_stop ();
  297. }
  298. return NOTIFY_DONE;
  299. }
  300. /*
  301. * Kernel Interfaces
  302. */
  303. static struct file_operations i8xx_tco_fops = {
  304. .owner = THIS_MODULE,
  305. .llseek = no_llseek,
  306. .write = i8xx_tco_write,
  307. .ioctl = i8xx_tco_ioctl,
  308. .open = i8xx_tco_open,
  309. .release = i8xx_tco_release,
  310. };
  311. static struct miscdevice i8xx_tco_miscdev = {
  312. .minor = WATCHDOG_MINOR,
  313. .name = "watchdog",
  314. .fops = &i8xx_tco_fops,
  315. };
  316. static struct notifier_block i8xx_tco_notifier = {
  317. .notifier_call = i8xx_tco_notify_sys,
  318. };
  319. /*
  320. * Data for PCI driver interface
  321. *
  322. * This data only exists for exporting the supported
  323. * PCI ids via MODULE_DEVICE_TABLE. We do not actually
  324. * register a pci_driver, because someone else might one day
  325. * want to register another driver on the same PCI id.
  326. */
  327. static struct pci_device_id i8xx_tco_pci_tbl[] = {
  328. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, },
  329. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, },
  330. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, },
  331. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, },
  332. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, },
  333. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, },
  334. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, },
  335. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, },
  336. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, },
  337. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, },
  338. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, PCI_ANY_ID, PCI_ANY_ID, },
  339. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, PCI_ANY_ID, PCI_ANY_ID, },
  340. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, },
  341. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, },
  342. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, },
  343. { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, },
  344. { 0, }, /* End of list */
  345. };
  346. MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl);
  347. /*
  348. * Init & exit routines
  349. */
  350. static unsigned char __init i8xx_tco_getdevice (void)
  351. {
  352. struct pci_dev *dev = NULL;
  353. u8 val1, val2;
  354. u16 badr;
  355. /*
  356. * Find the PCI device
  357. */
  358. while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
  359. if (pci_match_id(i8xx_tco_pci_tbl, dev)) {
  360. i8xx_tco_pci = dev;
  361. break;
  362. }
  363. }
  364. if (i8xx_tco_pci) {
  365. /*
  366. * Find the ACPI base I/O address which is the base
  367. * for the TCO registers (TCOBASE=ACPIBASE + 0x60)
  368. * ACPIBASE is bits [15:7] from 0x40-0x43
  369. */
  370. pci_read_config_byte (i8xx_tco_pci, 0x40, &val1);
  371. pci_read_config_byte (i8xx_tco_pci, 0x41, &val2);
  372. badr = ((val2 << 1) | (val1 >> 7)) << 7;
  373. ACPIBASE = badr;
  374. /* Something's wrong here, ACPIBASE has to be set */
  375. if (badr == 0x0001 || badr == 0x0000) {
  376. printk (KERN_ERR PFX "failed to get TCOBASE address\n");
  377. return 0;
  378. }
  379. /* Check chipset's NO_REBOOT bit */
  380. pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
  381. if (val1 & 0x02) {
  382. val1 &= 0xfd;
  383. pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
  384. pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
  385. if (val1 & 0x02) {
  386. printk (KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
  387. return 0; /* Cannot reset NO_REBOOT bit */
  388. }
  389. }
  390. /* Disable reboots untill the watchdog starts */
  391. val1 |= 0x02;
  392. pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
  393. /* Set the TCO_EN bit in SMI_EN register */
  394. if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) {
  395. printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
  396. SMI_EN + 1);
  397. return 0;
  398. }
  399. val1 = inb (SMI_EN + 1);
  400. val1 &= 0xdf;
  401. outb (val1, SMI_EN + 1);
  402. release_region (SMI_EN + 1, 1);
  403. return 1;
  404. }
  405. return 0;
  406. }
  407. static int __init watchdog_init (void)
  408. {
  409. int ret;
  410. spin_lock_init(&tco_lock);
  411. /* Check whether or not the hardware watchdog is there */
  412. if (!i8xx_tco_getdevice () || i8xx_tco_pci == NULL)
  413. return -ENODEV;
  414. if (!request_region (TCOBASE, 0x10, "i8xx TCO")) {
  415. printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
  416. TCOBASE);
  417. ret = -EIO;
  418. goto out;
  419. }
  420. /* Clear out the (probably old) status */
  421. outb (0, TCO1_STS);
  422. outb (3, TCO2_STS);
  423. /* Check that the heartbeat value is within it's range ; if not reset to the default */
  424. if (tco_timer_set_heartbeat (heartbeat)) {
  425. heartbeat = WATCHDOG_HEARTBEAT;
  426. tco_timer_set_heartbeat (heartbeat);
  427. printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, using %d\n",
  428. heartbeat);
  429. }
  430. ret = register_reboot_notifier(&i8xx_tco_notifier);
  431. if (ret != 0) {
  432. printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
  433. ret);
  434. goto unreg_region;
  435. }
  436. ret = misc_register(&i8xx_tco_miscdev);
  437. if (ret != 0) {
  438. printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
  439. WATCHDOG_MINOR, ret);
  440. goto unreg_notifier;
  441. }
  442. tco_timer_stop ();
  443. printk (KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n",
  444. TCOBASE, heartbeat, nowayout);
  445. return 0;
  446. unreg_notifier:
  447. unregister_reboot_notifier(&i8xx_tco_notifier);
  448. unreg_region:
  449. release_region (TCOBASE, 0x10);
  450. out:
  451. return ret;
  452. }
  453. static void __exit watchdog_cleanup (void)
  454. {
  455. /* Stop the timer before we leave */
  456. if (!nowayout)
  457. tco_timer_stop ();
  458. /* Deregister */
  459. misc_deregister (&i8xx_tco_miscdev);
  460. unregister_reboot_notifier(&i8xx_tco_notifier);
  461. release_region (TCOBASE, 0x10);
  462. }
  463. module_init(watchdog_init);
  464. module_exit(watchdog_cleanup);
  465. MODULE_AUTHOR("Nils Faerber");
  466. MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets");
  467. MODULE_LICENSE("GPL");
  468. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);