amd7xx_tco.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * AMD 766/768 TCO Timer Driver
  3.  * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
  4.  * All Rights Reserved.
  5.  *
  6.  * Parts from;
  7.  * Hardware driver for the AMD 768 Random Number Generator (RNG)
  8.  * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
  9.  *
  10.  * This program is free software; you can redistribute it and/or
  11.  * modify it under the terms of the GNU General Public License version 2
  12.  * as published by the Free Software Foundation.
  13.  *
  14.  * The author(s) of this software shall not be held liable for damages
  15.  * of any nature resulting due to the use of this software. This
  16.  * software is provided AS-IS with no warranties.
  17.  *
  18.  */
  19. #include <linux/config.h>
  20. #include <linux/module.h>
  21. #include <linux/version.h>
  22. #include <linux/kernel.h>
  23. #include <linux/miscdevice.h>
  24. #include <linux/watchdog.h>
  25. #include <linux/ioport.h>
  26. #include <linux/spinlock.h>
  27. #include <linux/ioport.h>
  28. #include <asm/semaphore.h>
  29. #include <asm/io.h>
  30. #include <asm/uaccess.h>
  31. #include <linux/notifier.h>
  32. #include <linux/reboot.h>
  33. #include <linux/init.h>
  34. #include <linux/pci.h>
  35. #define AMDTCO_MODULE_VER "build 20020601"
  36. #define AMDTCO_MODULE_NAME "amd7xx_tco"
  37. #define PFX AMDTCO_MODULE_NAME ": "
  38. #define MAX_TIMEOUT 38 /* max of 38 seconds */
  39. /* pmbase registers */
  40. #define GLOBAL_SMI_REG 0x2a
  41. #define TCO_EN (1 << 1) /* bit 1 in global SMI register */
  42. #define TCO_RELOAD_REG 0x40 /* bits 0-5 are current count, 6-7 are reserved */
  43. #define TCO_INITVAL_REG 0x41 /* bits 0-5 are value to load, 6-7 are reserved */
  44. #define TCO_TIMEOUT_MASK 0x3f
  45. #define TCO_STATUS2_REG 0x46
  46. #define NDTO_STS2 (1 << 1) /* we're interested in the second timeout */ 
  47. #define BOOT_STS (1 << 2) /* will be set if NDTO_STS2 was set before reboot */
  48. #define TCO_CTRL1_REG 0x48
  49. #define TCO_HALT (1 << 11)
  50. static char banner[] __initdata = KERN_INFO PFX AMDTCO_MODULE_VER;
  51. static int timeout = 38;
  52. static u32 pmbase; /* PMxx I/O base */
  53. static struct pci_dev *dev;
  54. static struct semaphore open_sem;
  55. spinlock_t amdtco_lock; /* only for device access */
  56. static int expect_close = 0;
  57. MODULE_PARM(timeout, "i");
  58. MODULE_PARM_DESC(timeout, "range is 0-38 seconds, default is 38");
  59. static inline int amdtco_status(void)
  60. {
  61. u16 reg;
  62. int status = 0;
  63. reg = inb(pmbase+TCO_CTRL1_REG);
  64. if ((reg & TCO_HALT) == 0)
  65. status |= WDIOF_KEEPALIVEPING;
  66. reg = inb(pmbase+TCO_STATUS2_REG);
  67. if (reg & BOOT_STS)
  68. status |= WDIOF_CARDRESET;
  69. return status;
  70. }
  71. static inline void amdtco_ping(void)
  72. {
  73. u8 reg;
  74. spin_lock(&amdtco_lock);
  75. reg = inb(pmbase+TCO_RELOAD_REG);
  76. outb(1 | reg, pmbase+TCO_RELOAD_REG);
  77. spin_unlock(&amdtco_lock);
  78. }
  79. static inline int amdtco_gettimeout(void)
  80. {
  81. return inb(TCO_RELOAD_REG) & TCO_TIMEOUT_MASK;
  82. }
  83. static inline void amdtco_settimeout(unsigned int timeout)
  84. {
  85. u8 reg;
  86. spin_lock(&amdtco_lock);
  87. reg = inb(pmbase+TCO_INITVAL_REG);
  88. reg |= timeout & TCO_TIMEOUT_MASK;
  89. outb(reg, pmbase+TCO_INITVAL_REG);
  90. spin_unlock(&amdtco_lock);
  91. }
  92. static inline void amdtco_global_enable(void)
  93. {
  94. u16 reg;
  95. spin_lock(&amdtco_lock);
  96. reg = inw(pmbase+GLOBAL_SMI_REG);
  97. reg |= TCO_EN;
  98. outw(reg, pmbase+GLOBAL_SMI_REG);
  99. spin_unlock(&amdtco_lock);
  100. }
  101. static inline void amdtco_enable(void)
  102. {
  103. u16 reg;
  104. spin_lock(&amdtco_lock);
  105. reg = inw(pmbase+TCO_CTRL1_REG);
  106. reg &= ~TCO_HALT;
  107. outw(reg, pmbase+TCO_CTRL1_REG);
  108. spin_unlock(&amdtco_lock);
  109. }
  110. static inline void amdtco_disable(void)
  111. {
  112. u16 reg;
  113. spin_lock(&amdtco_lock);
  114. reg = inw(pmbase+TCO_CTRL1_REG);
  115. reg |= TCO_HALT;
  116. outw(reg, pmbase+TCO_CTRL1_REG);
  117. spin_unlock(&amdtco_lock);
  118. }
  119. static int amdtco_fop_open(struct inode *inode, struct file *file)
  120. {
  121. if (down_trylock(&open_sem))
  122. return -EBUSY;
  123. #ifdef CONFIG_WATCHDOG_NOWAYOUT
  124. MOD_INC_USE_COUNT;
  125. #endif
  126. if (timeout > MAX_TIMEOUT)
  127. timeout = MAX_TIMEOUT;
  128. amdtco_settimeout(timeout);
  129. amdtco_global_enable();
  130. amdtco_ping();
  131. printk(KERN_INFO PFX "Watchdog enabled, timeout = %d/%d seconds",
  132. amdtco_gettimeout(), timeout);
  133. return 0;
  134. }
  135. static int amdtco_fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  136. {
  137. int new_timeout;
  138. int tmp;
  139. static struct watchdog_info ident = {
  140. options: WDIOF_SETTIMEOUT | WDIOF_CARDRESET,
  141. identity: "AMD 766/768"
  142. };
  143. switch (cmd) {
  144. default:
  145. return -ENOTTY;
  146. case WDIOC_GETSUPPORT:
  147. if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
  148. return -EFAULT;
  149. return 0;
  150. case WDIOC_GETSTATUS:
  151. return put_user(amdtco_status(), (int *)arg);
  152. case WDIOC_KEEPALIVE:
  153. amdtco_ping();
  154. return 0;
  155. case WDIOC_SETTIMEOUT:
  156. if (get_user(new_timeout, (int *)arg))
  157. return -EFAULT;
  158. if (new_timeout < 0)
  159. return -EINVAL;
  160. if (new_timeout > MAX_TIMEOUT)
  161. new_timeout = MAX_TIMEOUT;
  162. timeout = new_timeout;
  163. amdtco_settimeout(timeout);
  164. /* fall through and return the new timeout */
  165. case WDIOC_GETTIMEOUT:
  166. return put_user(amdtco_gettimeout(), (int *)arg);
  167. case WDIOC_SETOPTIONS:
  168. if (copy_from_user(&tmp, (int *)arg, sizeof tmp))
  169.                                 return -EFAULT;
  170. if (tmp & WDIOS_DISABLECARD)
  171. amdtco_disable();
  172. if (tmp & WDIOS_ENABLECARD)
  173. amdtco_enable();
  174. return 0;
  175. }
  176. }
  177. static int amdtco_fop_release(struct inode *inode, struct file *file)
  178. {
  179. if (expect_close) {
  180. amdtco_disable();
  181. printk(KERN_INFO PFX "Watchdog disabledn");
  182. } else {
  183. amdtco_ping();
  184. printk(KERN_CRIT PFX "Unexpected close!, timeout in %d seconds)n", timeout);
  185. }
  186. up(&open_sem);
  187. return 0;
  188. }
  189. static ssize_t amdtco_fop_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  190. {
  191. if (ppos != &file->f_pos)
  192. return -ESPIPE;
  193. if (len) {
  194. #ifndef CONFIG_WATCHDOG_NOWAYOUT
  195. size_t i;
  196. char c;
  197. expect_close = 0;
  198. for (i = 0; i != len; i++) {
  199. if (get_user(c, data + i))
  200. return -EFAULT;
  201. if (c == 'V')
  202. expect_close = 1;
  203. }
  204. #endif
  205. amdtco_ping();
  206. return len;
  207. }
  208. return 0;
  209. }
  210. static int amdtco_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
  211. {
  212. if (code == SYS_DOWN || code == SYS_HALT)
  213. amdtco_disable();
  214. return NOTIFY_DONE;
  215. }
  216. static struct notifier_block amdtco_notifier =
  217. {
  218. notifier_call: amdtco_notify_sys
  219. };
  220. static struct file_operations amdtco_fops =
  221. {
  222. owner: THIS_MODULE,
  223. write: amdtco_fop_write,
  224. ioctl: amdtco_fop_ioctl,
  225. open: amdtco_fop_open,
  226. release: amdtco_fop_release
  227. };
  228. static struct miscdevice amdtco_miscdev =
  229. {
  230. minor: WATCHDOG_MINOR,
  231. name: "watchdog",
  232. fops: &amdtco_fops
  233. };
  234. static struct pci_device_id amdtco_pci_tbl[] __initdata = {
  235. /* AMD 766 PCI_IDs here */
  236. { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, },
  237. { 0, }
  238. };
  239. MODULE_DEVICE_TABLE (pci, amdtco_pci_tbl);
  240. static int __init amdtco_init(void)
  241. {
  242. int ret;
  243. sema_init(&open_sem, 1);
  244. spin_lock_init(&amdtco_lock);
  245. pci_for_each_dev(dev) {
  246. if (pci_match_device (amdtco_pci_tbl, dev) != NULL)
  247. goto found_one;
  248. }
  249. return -ENODEV;
  250. found_one:
  251. if ((ret = register_reboot_notifier(&amdtco_notifier))) {
  252. printk(KERN_ERR PFX "Unable to register reboot notifier err = %dn", ret);
  253. goto out_clean;
  254. }
  255. if ((ret = misc_register(&amdtco_miscdev))) {
  256. printk(KERN_ERR PFX "Unable to register miscdev on minor %dn", WATCHDOG_MINOR);
  257. goto out_unreg_reboot;
  258. }
  259. pci_read_config_dword(dev, 0x58, &pmbase);
  260. pmbase &= 0x0000FF00;
  261. if (pmbase == 0) {
  262. printk (KERN_ERR PFX "power management base not setn");
  263. ret = -EIO;
  264. goto out_unreg_misc;
  265. }
  266. /* ret = 0; */
  267. printk(banner);
  268. goto out_clean;
  269. out_unreg_misc:
  270. misc_deregister(&amdtco_miscdev);
  271. out_unreg_reboot:
  272. unregister_reboot_notifier(&amdtco_notifier);
  273. out_clean:
  274. return ret;
  275. }
  276. static void __exit amdtco_exit(void)
  277. {
  278. misc_deregister(&amdtco_miscdev);
  279. unregister_reboot_notifier(&amdtco_notifier);
  280. }
  281. #ifndef MODULE
  282. static int __init amdtco_setup(char *str)
  283. {
  284. int ints[4];
  285. str = get_options (str, ARRAY_SIZE(ints), ints);
  286. if (ints[0] > 0)
  287. timeout = ints[1];
  288. return 1;
  289. }
  290. __setup("amd7xx_tco=", amdtco_setup);
  291. #endif
  292. module_init(amdtco_init);
  293. module_exit(amdtco_exit);
  294. MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
  295. MODULE_DESCRIPTION("AMD 766/768 TCO Timer Driver");
  296. MODULE_LICENSE("GPL");
  297. EXPORT_NO_SYMBOLS;