diff -urpN --exclude-from=/home/davej/.exclude bk-linus/drivers/char/amd7xx_tco.c linux-2.5/drivers/char/amd7xx_tco.c --- bk-linus/drivers/char/amd7xx_tco.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.5/drivers/char/amd7xx_tco.c 2002-11-21 17:57:37.000000000 +0000 @@ -0,0 +1,377 @@ +/* + * AMD 766/768 TCO Timer Driver + * (c) Copyright 2002 Zwane Mwaikambo + * All Rights Reserved. + * + * Parts from; + * Hardware driver for the AMD 768 Random Number Generator (RNG) + * (c) Copyright 2001 Red Hat Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * The author(s) of this software shall not be held liable for damages + * of any nature resulting due to the use of this software. This + * software is provided AS-IS with no warranties. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AMDTCO_MODULE_VER "build 20020601" +#define AMDTCO_MODULE_NAME "amd7xx_tco" +#define PFX AMDTCO_MODULE_NAME ": " + +#define MAX_TIMEOUT 38 /* max of 38 seconds */ + +/* pmbase registers */ +#define GLOBAL_SMI_REG 0x2a +#define TCO_EN (1 << 1) /* bit 1 in global SMI register */ +#define TCO_RELOAD_REG 0x40 /* bits 0-5 are current count, 6-7 are reserved */ +#define TCO_INITVAL_REG 0x41 /* bits 0-5 are value to load, 6-7 are reserved */ +#define TCO_TIMEOUT_MASK 0x3f +#define TCO_STATUS2_REG 0x46 +#define NDTO_STS2 (1 << 1) /* we're interested in the second timeout */ +#define BOOT_STS (1 << 2) /* will be set if NDTO_STS2 was set before reboot */ +#define TCO_CTRL1_REG 0x48 +#define TCO_HALT (1 << 11) + +static char banner[] __initdata = KERN_INFO PFX AMDTCO_MODULE_VER; +static int timeout = 38; +static u32 pmbase; /* PMxx I/O base */ +static struct pci_dev *dev; +static struct semaphore open_sem; +spinlock_t amdtco_lock; /* only for device access */ +static int expect_close = 0; + +MODULE_PARM(timeout, "i"); +MODULE_PARM_DESC(timeout, "range is 0-38 seconds, default is 38"); + +static inline int amdtco_status(void) +{ + u16 reg; + int status = 0; + + reg = inb(pmbase+TCO_CTRL1_REG); + if ((reg & TCO_HALT) == 0) + status |= WDIOF_KEEPALIVEPING; + + reg = inb(pmbase+TCO_STATUS2_REG); + if (reg & BOOT_STS) + status |= WDIOF_CARDRESET; + + return status; +} + +static inline void amdtco_ping(void) +{ + u8 reg; + + spin_lock(&amdtco_lock); + reg = inb(pmbase+TCO_RELOAD_REG); + outb(1 | reg, pmbase+TCO_RELOAD_REG); + spin_unlock(&amdtco_lock); +} + +static inline int amdtco_gettimeout(void) +{ + return inb(TCO_RELOAD_REG) & TCO_TIMEOUT_MASK; +} + +static inline void amdtco_settimeout(unsigned int timeout) +{ + u8 reg; + + spin_lock(&amdtco_lock); + reg = inb(pmbase+TCO_INITVAL_REG); + reg |= timeout & TCO_TIMEOUT_MASK; + outb(reg, pmbase+TCO_INITVAL_REG); + spin_unlock(&amdtco_lock); +} + +static inline void amdtco_global_enable(void) +{ + u16 reg; + + spin_lock(&amdtco_lock); + reg = inw(pmbase+GLOBAL_SMI_REG); + reg |= TCO_EN; + outw(reg, pmbase+GLOBAL_SMI_REG); + spin_unlock(&amdtco_lock); +} + +static inline void amdtco_enable(void) +{ + u16 reg; + + spin_lock(&amdtco_lock); + reg = inw(pmbase+TCO_CTRL1_REG); + reg &= ~TCO_HALT; + outw(reg, pmbase+TCO_CTRL1_REG); + spin_unlock(&amdtco_lock); +} + +static inline void amdtco_disable(void) +{ + u16 reg; + + spin_lock(&amdtco_lock); + reg = inw(pmbase+TCO_CTRL1_REG); + reg |= TCO_HALT; + outw(reg, pmbase+TCO_CTRL1_REG); + spin_unlock(&amdtco_lock); +} + +static int amdtco_fop_open(struct inode *inode, struct file *file) +{ + if (down_trylock(&open_sem)) + return -EBUSY; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT + MOD_INC_USE_COUNT; +#endif + + if (timeout > MAX_TIMEOUT) + timeout = MAX_TIMEOUT; + + amdtco_settimeout(timeout); + amdtco_global_enable(); + amdtco_ping(); + printk(KERN_INFO PFX "Watchdog enabled, timeout = %d/%d seconds", + amdtco_gettimeout(), timeout); + + return 0; +} + + +static int amdtco_fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int new_timeout; + int tmp; + + static struct watchdog_info ident = { + options: WDIOF_SETTIMEOUT | WDIOF_CARDRESET, + identity: "AMD 766/768" + }; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident)) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + return put_user(amdtco_status(), (int *)arg); + + case WDIOC_KEEPALIVE: + amdtco_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + + if (new_timeout < 0) + return -EINVAL; + + if (new_timeout > MAX_TIMEOUT) + new_timeout = MAX_TIMEOUT; + + timeout = new_timeout; + amdtco_settimeout(timeout); + /* fall through and return the new timeout */ + + case WDIOC_GETTIMEOUT: + return put_user(amdtco_gettimeout(), (int *)arg); + + case WDIOC_SETOPTIONS: + if (copy_from_user(&tmp, (int *)arg, sizeof tmp)) + return -EFAULT; + + if (tmp & WDIOS_DISABLECARD) + amdtco_disable(); + + if (tmp & WDIOS_ENABLECARD) + amdtco_enable(); + + return 0; + } +} + + +static int amdtco_fop_release(struct inode *inode, struct file *file) +{ + if (expect_close) { + amdtco_disable(); + printk(KERN_INFO PFX "Watchdog disabled\n"); + } else { + amdtco_ping(); + printk(KERN_CRIT PFX "Unexpected close!, timeout in %d seconds)\n", timeout); + } + + up(&open_sem); + return 0; +} + + +static ssize_t amdtco_fop_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + char c; + expect_close = 0; + + for (i = 0; i != len; i++) { + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + expect_close = 1; + } +#endif + amdtco_ping(); + return len; + } + + return 0; +} + + +static int amdtco_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + amdtco_disable(); + + return NOTIFY_DONE; +} + + +static struct notifier_block amdtco_notifier = +{ + notifier_call: amdtco_notify_sys +}; + +static struct file_operations amdtco_fops = +{ + owner: THIS_MODULE, + write: amdtco_fop_write, + ioctl: amdtco_fop_ioctl, + open: amdtco_fop_open, + release: amdtco_fop_release +}; + +static struct miscdevice amdtco_miscdev = +{ + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &amdtco_fops +}; + +static struct pci_device_id amdtco_pci_tbl[] __initdata = { + /* AMD 766 PCI_IDs here */ + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, amdtco_pci_tbl); + +static int __init amdtco_init(void) +{ + int ret; + + sema_init(&open_sem, 1); + spin_lock_init(&amdtco_lock); + + pci_for_each_dev(dev) { + if (pci_match_device (amdtco_pci_tbl, dev) != NULL) + goto found_one; + } + + return -ENODEV; + +found_one: + + if ((ret = register_reboot_notifier(&amdtco_notifier))) { + printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); + goto out_clean; + } + + if ((ret = misc_register(&amdtco_miscdev))) { + printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); + goto out_unreg_reboot; + } + + pci_read_config_dword(dev, 0x58, &pmbase); + pmbase &= 0x0000FF00; + + if (pmbase == 0) { + printk (KERN_ERR PFX "power management base not set\n"); + ret = -EIO; + goto out_unreg_misc; + } + + /* ret = 0; */ + printk(banner); + goto out_clean; + +out_unreg_misc: + misc_deregister(&amdtco_miscdev); +out_unreg_reboot: + unregister_reboot_notifier(&amdtco_notifier); +out_clean: + return ret; +} + +static void __exit amdtco_exit(void) +{ + misc_deregister(&amdtco_miscdev); + unregister_reboot_notifier(&amdtco_notifier); +} + + +#ifndef MODULE +static int __init amdtco_setup(char *str) +{ + int ints[4]; + + str = get_options (str, ARRAY_SIZE(ints), ints); + if (ints[0] > 0) + timeout = ints[1]; + + return 1; +} + +__setup("amd7xx_tco=", amdtco_setup); +#endif + +module_init(amdtco_init); +module_exit(amdtco_exit); + +MODULE_AUTHOR("Zwane Mwaikambo "); +MODULE_DESCRIPTION("AMD 766/768 TCO Timer Driver"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; +