diff options
Diffstat (limited to 'drivers/watchdog/bfin_wdt.c')
| -rw-r--r-- | drivers/watchdog/bfin_wdt.c | 261 |
1 files changed, 108 insertions, 153 deletions
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index 03b3e3d91e7..a3b6a5b30f9 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c @@ -1,17 +1,18 @@ /* * Blackfin On-Chip Watchdog Driver - * Supports BF53[123]/BF53[467]/BF54[2489]/BF561 * * Originally based on softdog.c - * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2010 Analog Devices Inc. * Copyright 2006-2007 Michele d'Amico - * Copyright 1996 Alan Cox <alan@redhat.com> + * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> * * Enter bugs at http://blackfin.uclinux.org/ * * Licensed under the GPL-2 or later. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/platform_device.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -20,20 +21,17 @@ #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/fs.h> -#include <linux/notifier.h> -#include <linux/reboot.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/uaccess.h> #include <asm/blackfin.h> -#include <asm/uaccess.h> +#include <asm/bfin_watchdog.h> -#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) +#define stamp(fmt, args...) \ + pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) #define stampit() stamp("here i am") -#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) -#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) #define WATCHDOG_NAME "bfin-wdt" -#define PFX WATCHDOG_NAME ": " /* The BF561 has two watchdogs (one per core), but since Linux * only runs on core A, we'll just work with that one. @@ -47,30 +45,12 @@ # define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) #endif -/* Bit in SWRST that indicates boot caused by watchdog */ -#define SWRST_RESET_WDOG 0x4000 - -/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */ -#define WDOG_EXPIRED 0x8000 - -/* Masks for WDEV field in WDOG_CTL register */ -#define ICTL_RESET 0x0 -#define ICTL_NMI 0x2 -#define ICTL_GPI 0x4 -#define ICTL_NONE 0x6 -#define ICTL_MASK 0x6 - -/* Masks for WDEN field in WDOG_CTL register */ -#define WDEN_MASK 0x0FF0 -#define WDEN_ENABLE 0x0000 -#define WDEN_DISABLE 0x0AD0 - /* some defaults */ #define WATCHDOG_TIMEOUT 20 static unsigned int timeout = WATCHDOG_TIMEOUT; -static int nowayout = WATCHDOG_NOWAYOUT; -static struct watchdog_info bfin_wdt_info; +static bool nowayout = WATCHDOG_NOWAYOUT; +static const struct watchdog_info bfin_wdt_info; static unsigned long open_check; static char expect_close; static DEFINE_SPINLOCK(bfin_wdt_spinlock); @@ -78,7 +58,7 @@ static DEFINE_SPINLOCK(bfin_wdt_spinlock); /** * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive * - * The Userspace watchdog got a KeepAlive: schedule the next timeout. + * The Userspace watchdog got a KeepAlive: schedule the next timeout. */ static int bfin_wdt_keepalive(void) { @@ -132,14 +112,16 @@ static int bfin_wdt_running(void) */ static int bfin_wdt_set_timeout(unsigned long t) { - u32 cnt; + u32 cnt, max_t, sclk; unsigned long flags; - stampit(); + sclk = get_sclk(); + max_t = -1 / sclk; + cnt = t * sclk; + stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt); - cnt = t * get_sclk(); - if (cnt < get_sclk()) { - printk(KERN_WARNING PFX "timeout value is too large\n"); + if (t > max_t) { + pr_warn("timeout value is too large\n"); return -EINVAL; } @@ -148,7 +130,8 @@ static int bfin_wdt_set_timeout(unsigned long t) int run = bfin_wdt_running(); bfin_wdt_stop(); bfin_write_WDOG_CNT(cnt); - if (run) bfin_wdt_start(); + if (run) + bfin_wdt_start(); } spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); @@ -191,16 +174,14 @@ static int bfin_wdt_release(struct inode *inode, struct file *file) { stampit(); - if (expect_close == 42) { + if (expect_close == 42) bfin_wdt_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + else { + pr_crit("Unexpected close, not stopping watchdog!\n"); bfin_wdt_keepalive(); } - expect_close = 0; clear_bit(0, &open_check); - return 0; } @@ -214,7 +195,7 @@ static int bfin_wdt_release(struct inode *inode, struct file *file) * Pings the watchdog on write. */ static ssize_t bfin_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) + size_t len, loff_t *ppos) { stampit(); @@ -241,7 +222,6 @@ static ssize_t bfin_wdt_write(struct file *file, const char __user *data, /** * bfin_wdt_ioctl - Query Device - * @inode: inode of device * @file: file handle of device * @cmd: watchdog command * @arg: argument @@ -249,8 +229,8 @@ static ssize_t bfin_wdt_write(struct file *file, const char __user *data, * Query basic information from the device or ping it, as outlined by the * watchdog API. */ -static int bfin_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long bfin_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; @@ -258,80 +238,50 @@ static int bfin_wdt_ioctl(struct inode *inode, struct file *file, stampit(); switch (cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info))) - return -EFAULT; - else - return 0; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p); - - case WDIOC_KEEPALIVE: - bfin_wdt_keepalive(); + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info))) + return -EFAULT; + else return 0; - - case WDIOC_SETTIMEOUT: { - int new_timeout; - - if (get_user(new_timeout, p)) - return -EFAULT; - - if (bfin_wdt_set_timeout(new_timeout)) - return -EINVAL; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p); + case WDIOC_SETOPTIONS: { + unsigned long flags; + int options, ret = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + spin_lock_irqsave(&bfin_wdt_spinlock, flags); + if (options & WDIOS_DISABLECARD) { + bfin_wdt_stop(); + ret = 0; } - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - - case WDIOC_SETOPTIONS: { - unsigned long flags; - int options, ret = -EINVAL; - - if (get_user(options, p)) - return -EFAULT; - - spin_lock_irqsave(&bfin_wdt_spinlock, flags); - - if (options & WDIOS_DISABLECARD) { - bfin_wdt_stop(); - ret = 0; - } - - if (options & WDIOS_ENABLECARD) { - bfin_wdt_start(); - ret = 0; - } - - spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); - - return ret; + if (options & WDIOS_ENABLECARD) { + bfin_wdt_start(); + ret = 0; } + spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); + return ret; + } + case WDIOC_KEEPALIVE: + bfin_wdt_keepalive(); + return 0; + case WDIOC_SETTIMEOUT: { + int new_timeout; + + if (get_user(new_timeout, p)) + return -EFAULT; + if (bfin_wdt_set_timeout(new_timeout)) + return -EINVAL; + } + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + default: + return -ENOTTY; } -} - -/** - * bfin_wdt_notify_sys - Notifier Handler - * @this: notifier block - * @code: notifier event - * @unused: unused - * - * Handles specific events, such as turning off the watchdog during a - * shutdown event. - */ -static int bfin_wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - stampit(); - - if (code == SYS_DOWN || code == SYS_HALT) - bfin_wdt_stop(); - - return NOTIFY_DONE; } #ifdef CONFIG_PM @@ -379,12 +329,12 @@ static int bfin_wdt_resume(struct platform_device *pdev) #endif static const struct file_operations bfin_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = bfin_wdt_write, - .ioctl = bfin_wdt_ioctl, - .open = bfin_wdt_open, - .release = bfin_wdt_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = bfin_wdt_write, + .unlocked_ioctl = bfin_wdt_ioctl, + .open = bfin_wdt_open, + .release = bfin_wdt_release, }; static struct miscdevice bfin_wdt_miscdev = { @@ -393,43 +343,32 @@ static struct miscdevice bfin_wdt_miscdev = { .fops = &bfin_wdt_fops, }; -static struct watchdog_info bfin_wdt_info = { +static const struct watchdog_info bfin_wdt_info = { .identity = "Blackfin Watchdog", .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -static struct notifier_block bfin_wdt_notifier = { - .notifier_call = bfin_wdt_notify_sys, + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, }; /** * bfin_wdt_probe - Initialize module * - * Registers the misc device and notifier handler. Actual device + * Registers the misc device. Actual device * initialization is handled by bfin_wdt_open(). */ -static int __devinit bfin_wdt_probe(struct platform_device *pdev) +static int bfin_wdt_probe(struct platform_device *pdev) { int ret; - ret = register_reboot_notifier(&bfin_wdt_notifier); - if (ret) { - pr_devinit(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); - return ret; - } - ret = misc_register(&bfin_wdt_miscdev); if (ret) { - pr_devinit(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&bfin_wdt_notifier); return ret; } - pr_devinit(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); + pr_info("initialized: timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); return 0; } @@ -437,21 +376,33 @@ static int __devinit bfin_wdt_probe(struct platform_device *pdev) /** * bfin_wdt_remove - Initialize module * - * Unregisters the misc device and notifier handler. Actual device + * Unregisters the misc device. Actual device * deinitialization is handled by bfin_wdt_close(). */ -static int __devexit bfin_wdt_remove(struct platform_device *pdev) +static int bfin_wdt_remove(struct platform_device *pdev) { misc_deregister(&bfin_wdt_miscdev); - unregister_reboot_notifier(&bfin_wdt_notifier); return 0; } +/** + * bfin_wdt_shutdown - Soft Shutdown Handler + * + * Handles the soft shutdown event. + */ +static void bfin_wdt_shutdown(struct platform_device *pdev) +{ + stampit(); + + bfin_wdt_stop(); +} + static struct platform_device *bfin_wdt_device; static struct platform_driver bfin_wdt_driver = { .probe = bfin_wdt_probe, - .remove = __devexit_p(bfin_wdt_remove), + .remove = bfin_wdt_remove, + .shutdown = bfin_wdt_shutdown, .suspend = bfin_wdt_suspend, .resume = bfin_wdt_resume, .driver = { @@ -481,13 +432,14 @@ static int __init bfin_wdt_init(void) */ ret = platform_driver_register(&bfin_wdt_driver); if (ret) { - pr_init(KERN_ERR PFX "unable to register driver\n"); + pr_err("unable to register driver\n"); return ret; } - bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME, -1, NULL, 0); + bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME, + -1, NULL, 0); if (IS_ERR(bfin_wdt_device)) { - pr_init(KERN_ERR PFX "unable to register device\n"); + pr_err("unable to register device\n"); platform_driver_unregister(&bfin_wdt_driver); return PTR_ERR(bfin_wdt_device); } @@ -513,10 +465,13 @@ module_exit(bfin_wdt_exit); MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>"); MODULE_DESCRIPTION("Blackfin Watchdog Device Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); module_param(timeout, uint, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
