2 * AMD 766/768 TCO Timer Driver
3 * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
7 * Hardware driver for the AMD 768 Random Number Generator (RNG)
8 * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
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.
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.
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/version.h>
23 #include <linux/kernel.h>
24 #include <linux/miscdevice.h>
25 #include <linux/watchdog.h>
26 #include <linux/ioport.h>
27 #include <linux/spinlock.h>
28 #include <linux/ioport.h>
29 #include <asm/semaphore.h>
31 #include <asm/uaccess.h>
32 #include <linux/notifier.h>
33 #include <linux/reboot.h>
34 #include <linux/init.h>
35 #include <linux/pci.h>
37 #define AMDTCO_MODULE_VER "build 20020601"
38 #define AMDTCO_MODULE_NAME "amd7xx_tco"
39 #define PFX AMDTCO_MODULE_NAME ": "
41 #define MAX_TIMEOUT 38 /* max of 38 seconds */
43 /* pmbase registers */
44 #define GLOBAL_SMI_REG 0x2a
45 #define TCO_EN (1 << 1) /* bit 1 in global SMI register */
46 #define TCO_RELOAD_REG 0x40 /* bits 0-5 are current count, 6-7 are reserved */
47 #define TCO_INITVAL_REG 0x41 /* bits 0-5 are value to load, 6-7 are reserved */
48 #define TCO_TIMEOUT_MASK 0x3f
49 #define TCO_STATUS2_REG 0x46
50 #define NDTO_STS2 (1 << 1) /* we're interested in the second timeout */
51 #define BOOT_STS (1 << 2) /* will be set if NDTO_STS2 was set before reboot */
52 #define TCO_CTRL1_REG 0x48
53 #define TCO_HALT (1 << 11)
55 static char banner[] __initdata = KERN_INFO PFX AMDTCO_MODULE_VER;
56 static int timeout = 38;
57 static u32 pmbase; /* PMxx I/O base */
58 static struct pci_dev *dev;
59 static struct semaphore open_sem;
60 spinlock_t amdtco_lock; /* only for device access */
61 static int expect_close = 0;
63 MODULE_PARM(timeout, "i");
64 MODULE_PARM_DESC(timeout, "range is 0-38 seconds, default is 38");
66 static inline int amdtco_status(void)
71 reg = inb(pmbase+TCO_CTRL1_REG);
72 if ((reg & TCO_HALT) == 0)
73 status |= WDIOF_KEEPALIVEPING;
75 reg = inb(pmbase+TCO_STATUS2_REG);
77 status |= WDIOF_CARDRESET;
82 static inline void amdtco_ping(void)
86 spin_lock(&amdtco_lock);
87 reg = inb(pmbase+TCO_RELOAD_REG);
88 outb(1 | reg, pmbase+TCO_RELOAD_REG);
89 spin_unlock(&amdtco_lock);
92 static inline int amdtco_gettimeout(void)
94 return inb(TCO_RELOAD_REG) & TCO_TIMEOUT_MASK;
97 static inline void amdtco_settimeout(unsigned int timeout)
101 spin_lock(&amdtco_lock);
102 reg = inb(pmbase+TCO_INITVAL_REG);
103 reg |= timeout & TCO_TIMEOUT_MASK;
104 outb(reg, pmbase+TCO_INITVAL_REG);
105 spin_unlock(&amdtco_lock);
108 static inline void amdtco_global_enable(void)
112 spin_lock(&amdtco_lock);
113 reg = inw(pmbase+GLOBAL_SMI_REG);
115 outw(reg, pmbase+GLOBAL_SMI_REG);
116 spin_unlock(&amdtco_lock);
119 static inline void amdtco_enable(void)
123 spin_lock(&amdtco_lock);
124 reg = inw(pmbase+TCO_CTRL1_REG);
126 outw(reg, pmbase+TCO_CTRL1_REG);
127 spin_unlock(&amdtco_lock);
130 static inline void amdtco_disable(void)
134 spin_lock(&amdtco_lock);
135 reg = inw(pmbase+TCO_CTRL1_REG);
137 outw(reg, pmbase+TCO_CTRL1_REG);
138 spin_unlock(&amdtco_lock);
141 static int amdtco_fop_open(struct inode *inode, struct file *file)
143 if (down_trylock(&open_sem))
146 #ifdef CONFIG_WATCHDOG_NOWAYOUT
150 if (timeout > MAX_TIMEOUT)
151 timeout = MAX_TIMEOUT;
153 amdtco_settimeout(timeout);
154 amdtco_global_enable();
156 printk(KERN_INFO PFX "Watchdog enabled, timeout = %d/%d seconds",
157 amdtco_gettimeout(), timeout);
163 static int amdtco_fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
168 static struct watchdog_info ident = {
169 options: WDIOF_SETTIMEOUT | WDIOF_CARDRESET,
170 identity: "AMD 766/768"
177 case WDIOC_GETSUPPORT:
178 if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
182 case WDIOC_GETSTATUS:
183 return put_user(amdtco_status(), (int *)arg);
185 case WDIOC_KEEPALIVE:
189 case WDIOC_SETTIMEOUT:
190 if (get_user(new_timeout, (int *)arg))
196 if (new_timeout > MAX_TIMEOUT)
197 new_timeout = MAX_TIMEOUT;
199 timeout = new_timeout;
200 amdtco_settimeout(timeout);
201 /* fall through and return the new timeout */
203 case WDIOC_GETTIMEOUT:
204 return put_user(amdtco_gettimeout(), (int *)arg);
206 case WDIOC_SETOPTIONS:
207 if (copy_from_user(&tmp, (int *)arg, sizeof tmp))
210 if (tmp & WDIOS_DISABLECARD)
213 if (tmp & WDIOS_ENABLECARD)
221 static int amdtco_fop_release(struct inode *inode, struct file *file)
225 printk(KERN_INFO PFX "Watchdog disabled\n");
228 printk(KERN_CRIT PFX "Unexpected close!, timeout in %d seconds)\n", timeout);
236 static ssize_t amdtco_fop_write(struct file *file, const char *data, size_t len, loff_t *ppos)
238 if (ppos != &file->f_pos)
242 #ifndef CONFIG_WATCHDOG_NOWAYOUT
247 for (i = 0; i != len; i++) {
248 if (get_user(c, data + i))
263 static int amdtco_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
265 if (code == SYS_DOWN || code == SYS_HALT)
272 static struct notifier_block amdtco_notifier =
274 notifier_call: amdtco_notify_sys
277 static struct file_operations amdtco_fops =
280 write: amdtco_fop_write,
281 ioctl: amdtco_fop_ioctl,
282 open: amdtco_fop_open,
283 release: amdtco_fop_release
286 static struct miscdevice amdtco_miscdev =
288 minor: WATCHDOG_MINOR,
293 static struct pci_device_id amdtco_pci_tbl[] __initdata = {
294 /* AMD 766 PCI_IDs here */
295 { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, },
299 MODULE_DEVICE_TABLE (pci, amdtco_pci_tbl);
301 static int __init amdtco_init(void)
305 sema_init(&open_sem, 1);
306 spin_lock_init(&amdtco_lock);
308 pci_for_each_dev(dev) {
309 if (pci_match_device (amdtco_pci_tbl, dev) != NULL)
317 if ((ret = register_reboot_notifier(&amdtco_notifier))) {
318 printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
322 if ((ret = misc_register(&amdtco_miscdev))) {
323 printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
324 goto out_unreg_reboot;
327 pci_read_config_dword(dev, 0x58, &pmbase);
328 pmbase &= 0x0000FF00;
331 printk (KERN_ERR PFX "power management base not set\n");
341 misc_deregister(&amdtco_miscdev);
343 unregister_reboot_notifier(&amdtco_notifier);
348 static void __exit amdtco_exit(void)
350 misc_deregister(&amdtco_miscdev);
351 unregister_reboot_notifier(&amdtco_notifier);
356 static int __init amdtco_setup(char *str)
360 str = get_options (str, ARRAY_SIZE(ints), ints);
367 __setup("amd7xx_tco=", amdtco_setup);
370 module_init(amdtco_init);
371 module_exit(amdtco_exit);
373 MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
374 MODULE_DESCRIPTION("AMD 766/768 TCO Timer Driver");
375 MODULE_LICENSE("GPL");