static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; static const struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "SC520", }; switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: { int new_options, retval = -EINVAL; if (get_user(new_options, p)) return -EFAULT; if (new_options & WDIOS_DISABLECARD) { wdt_turnoff(); retval = 0; } if (new_options & WDIOS_ENABLECARD) { wdt_startup(); retval = 0; } return retval; } case WDIOC_KEEPALIVE: wdt_keepalive(); return 0; case WDIOC_SETTIMEOUT: { int new_timeout; if (get_user(new_timeout, p)) return -EFAULT; if (wdt_set_heartbeat(new_timeout)) return -EINVAL; wdt_keepalive(); } case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: return -ENOTTY; } } static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, }; static struct miscdevice wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wdt_fops, }; static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) wdt_turnoff(); return NOTIFY_DONE; } static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; static void __exit sc520_wdt_unload(void) { if (!nowayout) wdt_turnoff(); misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); iounmap(wdtmrctl); } static int __init sc520_wdt_init(void) { int rc = -EBUSY; if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n", WATCHDOG_TIMEOUT); } wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2); if (!wdtmrctl) { pr_err("Unable to remap memory\n"); rc = -ENOMEM; goto err_out_region2; } rc = register_reboot_notifier(&wdt_notifier); if (rc) { pr_err("cannot register reboot notifier (err=%d)\n", rc); goto err_out_ioremap; } rc = misc_register(&wdt_miscdev); if (rc) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, rc); goto err_out_notifier; } pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); return 0; err_out_notifier: unregister_reboot_notifier(&wdt_notifier); err_out_ioremap: iounmap(wdtmrctl); err_out_region2: return rc; } module_init(sc520_wdt_init); module_exit(sc520_wdt_unload); MODULE_AUTHOR("Scott and Bill Jennings"); MODULE_DESCRIPTION( "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int new_timeout; static struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "W83697HF WDT", }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: wdt_ping(); break; case WDIOC_SETTIMEOUT: if (get_user(new_timeout, p)) return -EFAULT; if (wdt_set_heartbeat(new_timeout)) return -EINVAL; wdt_ping(); /* Fall */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); case WDIOC_SETOPTIONS: { int options, retval = -EINVAL; if (get_user(options, p)) return -EFAULT; if (options & WDIOS_DISABLECARD) { wdt_disable(); retval = 0; } if (options & WDIOS_ENABLECARD) { wdt_enable(); retval = 0; } return retval; } default: return -ENOTTY; } return 0; } static int wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; /* * Activate */ wdt_enable(); return nonseekable_open(inode, file); } static int wdt_close(struct inode *inode, struct file *file) { if (expect_close == 42) { wdt_disable(); } else { printk (KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); wdt_ping(); } expect_close = 0; clear_bit(0, &wdt_is_open); return 0; } /* * Notifier for system down */ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) { /* Turn the WDT off */ wdt_disable(); } return NOTIFY_DONE; } /* * Kernel Interfaces */ static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = wdt_write, .ioctl = wdt_ioctl, .open = wdt_open, .release = wdt_close, }; static struct miscdevice wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wdt_fops, }; /* * The WDT needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; static int w83697hf_check_wdt(void) { if (!request_region(wdt_io, 2, WATCHDOG_NAME)) { printk (KERN_ERR PFX "I/O address 0x%x already in use\n", wdt_io); return -EIO; } printk (KERN_DEBUG PFX "Looking for watchdog at address 0x%x\n", wdt_io); w83697hf_unlock(); if (w83697hf_get_reg(0x20) == 0x60) { printk (KERN_INFO PFX "watchdog found at address 0x%x\n", wdt_io); w83697hf_lock(); return 0; } w83697hf_lock(); /* Reprotect in case it was a compatible device */ printk (KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io); release_region(wdt_io, 2); return -EIO; } static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 }; static int __init wdt_init(void) { int ret, i, found = 0; printk (KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n"); if (wdt_io == 0) { /* we will autodetect the W83697HF/HG watchdog */ for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) { wdt_io = w83697hf_ioports[i]; if (!w83697hf_check_wdt()) found++; } } else { if (!w83697hf_check_wdt()) found++; } if (!found) { printk (KERN_ERR PFX "No W83697HF/HG could be found\n"); ret = -EIO; goto out; } w83697hf_init(); if (early_disable) { if (wdt_running()) printk (KERN_WARNING PFX "Stopping previously enabled watchdog until userland kicks in\n"); wdt_disable(); } if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); printk (KERN_INFO PFX "timeout value must be 1<=timeout<=255, using %d\n", WATCHDOG_TIMEOUT); } ret = register_reboot_notifier(&wdt_notifier); if (ret != 0) { printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); goto unreg_regions; } ret = misc_register(&wdt_miscdev); if (ret != 0) { printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); goto unreg_reboot; } printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); out: return ret; unreg_reboot: unregister_reboot_notifier(&wdt_notifier); unreg_regions: release_region(wdt_io, 2); goto out; } static void __exit wdt_exit(void) { misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); release_region(wdt_io, 2); }
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int new_timeout; static const struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "W83697UG WDT", }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: { int options, retval = -EINVAL; if (get_user(options, p)) return -EFAULT; if (options & WDIOS_DISABLECARD) { wdt_disable(); retval = 0; } if (options & WDIOS_ENABLECARD) { wdt_ping(); retval = 0; } return retval; } case WDIOC_KEEPALIVE: wdt_ping(); break; case WDIOC_SETTIMEOUT: if (get_user(new_timeout, p)) return -EFAULT; if (wdt_set_heartbeat(new_timeout)) return -EINVAL; wdt_ping(); /* Fall */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: return -ENOTTY; } return 0; } static int wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; /* * Activate */ wdt_ping(); return nonseekable_open(inode, file); } static int wdt_close(struct inode *inode, struct file *file) { if (expect_close == 42) wdt_disable(); else { printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); wdt_ping(); } expect_close = 0; clear_bit(0, &wdt_is_open); return 0; } /* * Notifier for system down */ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) wdt_disable(); /* Turn the WDT off */ return NOTIFY_DONE; } /* * Kernel Interfaces */ static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, .open = wdt_open, .release = wdt_close, }; static struct miscdevice wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wdt_fops, }; /* * The WDT needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; static int __init wdt_init(void) { int ret; printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n"); if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); printk(KERN_INFO PFX "timeout value must be 1<=timeout<=255, using %d\n", WATCHDOG_TIMEOUT); } if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", wdt_io); ret = -EIO; goto out; } ret = w83697ug_init(); if (ret != 0) goto unreg_regions; ret = register_reboot_notifier(&wdt_notifier); if (ret != 0) { printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); goto unreg_regions; } ret = misc_register(&wdt_miscdev); if (ret != 0) { printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); goto unreg_reboot; } printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); out: return ret; unreg_reboot: unregister_reboot_notifier(&wdt_notifier); unreg_regions: release_region(wdt_io, 1); goto out; } static void __exit wdt_exit(void) { misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); release_region(wdt_io, 1); }
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int new_heartbeat; int status; static struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT| WDIOF_MAGICCLOSE| WDIOF_KEEPALIVEPING, .firmware_version = 1, .identity = "WDT500/501", }; /* Add options according to the card we have */ ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); #ifdef CONFIG_WDT_501 ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); if (tachometer) ident.options |= WDIOF_FANFAULT; #endif /* CONFIG_WDT_501 */ switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: wdt_get_status(&status); return put_user(status, p); case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: wdt_ping(); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_heartbeat, p)) return -EFAULT; if (wdt_set_heartbeat(new_heartbeat)) return -EINVAL; wdt_ping(); /* Fall */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: return -ENOTTY; } } /** * wdt_open: * @inode: inode of device * @file: file handle to device * * The watchdog device has been opened. The watchdog device is single * open and on opening we load the counters. Counter zero is a 100Hz * cascade, into counter 1 which downcounts to reboot. When the counter * triggers counter 2 downcounts the length of the reset pulse which * set set to be as long as possible. */ static int wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; /* * Activate */ wdt_start(); return nonseekable_open(inode, file); } /** * wdt_release: * @inode: inode to board * @file: file handle to board * * The watchdog has a configurable API. There is a religious dispute * between people who want their watchdog to be able to shut down and * those who want to be sure if the watchdog manager dies the machine * reboots. In the former case we disable the counters, in the latter * case you have to open it again very soon. */ static int wdt_release(struct inode *inode, struct file *file) { if (expect_close == 42) { wdt_stop(); clear_bit(0, &wdt_is_open); } else { printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); wdt_ping(); } expect_close = 0; return 0; } #ifdef CONFIG_WDT_501 /** * wdt_temp_read: * @file: file handle to the watchdog board * @buf: buffer to write 1 byte into * @count: length of buffer * @ptr: offset (no seek allowed) * * Temp_read reports the temperature in degrees Fahrenheit. The API is in * farenheit. It was designed by an imperial measurement luddite. */ static ssize_t wdt_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) { int temperature; if (wdt_get_temperature(&temperature)) return -EFAULT; if (copy_to_user(buf, &temperature, 1)) return -EFAULT; return 1; }
static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { void *argp = (void *)arg; int *p = argp; int new_timeout; static struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "W83627HF WDT", }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: wdt_ping(); break; case WDIOC_SETTIMEOUT: if (get_user(new_timeout, p)) return -EFAULT; if (wdt_set_heartbeat(new_timeout)) return -EINVAL; wdt_ping(); /* Fall */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); case WDIOC_SETOPTIONS: { int options, retval = -EINVAL; if (get_user(options, p)) return -EFAULT; if (options & WDIOS_DISABLECARD) { wdt_disable(); retval = 0; } if (options & WDIOS_ENABLECARD) { wdt_ping(); retval = 0; } return retval; } default: return -ENOTTY; } return 0; } static int wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; /* * Activate */ wdt_ping(); return 0; } static int wdt_close(struct inode *inode, struct file *file) { if (expect_close == 42) { wdt_disable(); } else { printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); wdt_ping(); } expect_close = 0; clear_bit(0, &wdt_is_open); return 0; } /* * Notifier for system down */ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) { /* Turn the WDT off */ wdt_disable(); } return NOTIFY_DONE; } /* * Kernel Interfaces */ static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .write = wdt_write, .ioctl = wdt_ioctl, .open = wdt_open, .release = wdt_close, }; static struct miscdevice wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wdt_fops, }; /* * The WDT needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; static int __init wdt_init(void) { int ret = -EBUSY; spin_lock_init(&io_lock); printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n"); if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); printk (KERN_INFO PFX "timeout value must be 1<=timeout<=63, using %d\n", WATCHDOG_TIMEOUT); } /* on the board I was working on, wdt_io consistently coincided with things already * being used, hence the continuing here. * there's probably a better solution to this */ if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { printk (KERN_WARNING PFX "I/O address 0x%04x already in use, continuing anyway\n", wdt_io); } if(!create_proc_read_entry("watchdog",0,NULL,&wdt_readproc,NULL)) goto unreg_regions; w83627hf_init(); ret = register_reboot_notifier(&wdt_notifier); if (ret != 0) { printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); goto unreg_proc; } ret = misc_register(&wdt_miscdev); if (ret != 0) { printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); goto unreg_reboot; } printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); out: return ret; unreg_reboot: unregister_reboot_notifier(&wdt_notifier); unreg_proc: remove_proc_entry("watchdog",NULL); unreg_regions: release_region(wdt_io, 1); goto out; } static void __exit wdt_exit(void) { remove_proc_entry("watchdog",NULL); misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); release_region(wdt_io,1); /* we might not have gotten it... is this safe? */ }