[PATCH] ARM Netwinder watchdog wdt977 update

Cleanup for the ARM-only watchdog driver wdt977.

This is probably the last update, since we want to merge with w83977f_wdt.
Jose Goncalves has ported this driver to i386, so probably we can iron out
configuration differences.

Signed-off-by: Woody Suwalski <woodys@xandros.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c
index 44d49df..3843900 100644
--- a/drivers/char/watchdog/wdt977.c
+++ b/drivers/char/watchdog/wdt977.c
@@ -1,5 +1,5 @@
 /*
- *	Wdt977	0.03:	A Watchdog Device for Netwinder W83977AF chip
+ *	Wdt977	0.04:	A Watchdog Device for Netwinder W83977AF chip
  *
  *	(c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
  *
@@ -18,6 +18,8 @@
  *				    from minutes to seconds.
  *      07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in
  *                                    nwwatchdog_init.
+ *      25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks
+ *				    remove limitiation to be used on Netwinders only
  */
 
 #include <linux/module.h>
@@ -28,6 +30,7 @@
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
 #include <linux/init.h>
+#include <linux/ioport.h>
 #include <linux/watchdog.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
@@ -37,8 +40,18 @@
 #include <asm/mach-types.h>
 #include <asm/uaccess.h>
 
-#define PFX "Wdt977: "
-#define WATCHDOG_MINOR	130
+#define WATCHDOG_VERSION  "0.04"
+#define WATCHDOG_NAME     "Wdt977"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION    WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+
+#define IO_INDEX_PORT	0x370		/* on some systems it can be 0x3F0 */
+#define IO_DATA_PORT	(IO_INDEX_PORT+1)
+
+#define UNLOCK_DATA	0x87
+#define LOCK_DATA	0xAA
+#define DEVICE_REGISTER	0x07
+
 
 #define	DEFAULT_TIMEOUT	60			/* default timeout in seconds */
 
@@ -47,6 +60,7 @@
 static	unsigned long timer_alive;
 static	int testmode;
 static	char expect_close;
+static	spinlock_t spinlock;
 
 module_param(timeout, int, 0);
 MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")");
@@ -63,9 +77,13 @@
 
 static int wdt977_start(void)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&spinlock, flags);
+
 	/* unlock the SuperIO chip */
-	outb(0x87,0x370);
-	outb(0x87,0x370);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
 
 	/* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
 	 * F2 has the timeout in minutes
@@ -73,28 +91,29 @@
 	 *   at timeout, and to reset timer on kbd/mouse activity (not impl.)
 	 * F4 is used to just clear the TIMEOUT'ed state (bit 0)
 	 */
-	outb(0x07,0x370);
-	outb(0x08,0x371);
-	outb(0xF2,0x370);
-	outb(timeoutM,0x371);
-	outb(0xF3,0x370);
-	outb(0x00,0x371);	/* another setting is 0E for kbd/mouse/LED */
-	outb(0xF4,0x370);
-	outb(0x00,0x371);
+	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+	outb_p(0x08, IO_DATA_PORT);
+	outb_p(0xF2, IO_INDEX_PORT);
+	outb_p(timeoutM, IO_DATA_PORT);
+	outb_p(0xF3, IO_INDEX_PORT);
+	outb_p(0x00, IO_DATA_PORT);	/* another setting is 0E for kbd/mouse/LED */
+	outb_p(0xF4, IO_INDEX_PORT);
+	outb_p(0x00, IO_DATA_PORT);
 
 	/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
 	/* in test mode watch the bit 1 on F4 to indicate "triggered" */
 	if (!testmode)
 	{
-		outb(0x07,0x370);
-		outb(0x07,0x371);
-		outb(0xE6,0x370);
-		outb(0x08,0x371);
+		outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+		outb_p(0x07, IO_DATA_PORT);
+		outb_p(0xE6, IO_INDEX_PORT);
+		outb_p(0x08, IO_DATA_PORT);
 	}
 
 	/* lock the SuperIO chip */
-	outb(0xAA,0x370);
+	outb_p(LOCK_DATA, IO_INDEX_PORT);
 
+	spin_unlock_irqrestore(&spinlock, flags);
 	printk(KERN_INFO PFX "activated.\n");
 
 	return 0;
@@ -106,35 +125,39 @@
 
 static int wdt977_stop(void)
 {
+	unsigned long flags;
+	spin_lock_irqsave(&spinlock, flags);
+
 	/* unlock the SuperIO chip */
-	outb(0x87,0x370);
-	outb(0x87,0x370);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
 
 	/* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
 	* F3 is reset to its default state
 	* F4 can clear the TIMEOUT'ed state (bit 0) - back to default
 	* We can not use GP17 as a PowerLed, as we use its usage as a RedLed
 	*/
-	outb(0x07,0x370);
-	outb(0x08,0x371);
-	outb(0xF2,0x370);
-	outb(0xFF,0x371);
-	outb(0xF3,0x370);
-	outb(0x00,0x371);
-	outb(0xF4,0x370);
-	outb(0x00,0x371);
-	outb(0xF2,0x370);
-	outb(0x00,0x371);
+	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+	outb_p(0x08, IO_DATA_PORT);
+	outb_p(0xF2, IO_INDEX_PORT);
+	outb_p(0xFF, IO_DATA_PORT);
+	outb_p(0xF3, IO_INDEX_PORT);
+	outb_p(0x00, IO_DATA_PORT);
+	outb_p(0xF4, IO_INDEX_PORT);
+	outb_p(0x00, IO_DATA_PORT);
+	outb_p(0xF2, IO_INDEX_PORT);
+	outb_p(0x00, IO_DATA_PORT);
 
 	/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
-	outb(0x07,0x370);
-	outb(0x07,0x371);
-	outb(0xE6,0x370);
-	outb(0x08,0x371);
+	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+	outb_p(0x07, IO_DATA_PORT);
+	outb_p(0xE6, IO_INDEX_PORT);
+	outb_p(0x08, IO_DATA_PORT);
 
 	/* lock the SuperIO chip */
-	outb(0xAA,0x370);
+	outb_p(LOCK_DATA, IO_INDEX_PORT);
 
+	spin_unlock_irqrestore(&spinlock, flags);
 	printk(KERN_INFO PFX "shutdown.\n");
 
 	return 0;
@@ -147,19 +170,23 @@
 
 static int wdt977_keepalive(void)
 {
+	unsigned long flags;
+	spin_lock_irqsave(&spinlock, flags);
+
 	/* unlock the SuperIO chip */
-	outb(0x87,0x370);
-	outb(0x87,0x370);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
 
 	/* select device Aux2 (device=8) and kicks watchdog reg F2 */
 	/* F2 has the timeout in minutes */
-	outb(0x07,0x370);
-	outb(0x08,0x371);
-	outb(0xF2,0x370);
-	outb(timeoutM,0x371);
+	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+	outb_p(0x08, IO_DATA_PORT);
+	outb_p(0xF2, IO_INDEX_PORT);
+	outb_p(timeoutM, IO_DATA_PORT);
 
 	/* lock the SuperIO chip */
-	outb(0xAA,0x370);
+	outb_p(LOCK_DATA, IO_INDEX_PORT);
+	spin_unlock_irqrestore(&spinlock, flags);
 
 	return 0;
 }
@@ -198,22 +225,26 @@
 static int wdt977_get_status(int *status)
 {
 	int new_status;
+	unsigned long flags;
 
-	*status=0;
+	spin_lock_irqsave(&spinlock, flags);
 
 	/* unlock the SuperIO chip */
-	outb(0x87,0x370);
-	outb(0x87,0x370);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
 
 	/* select device Aux2 (device=8) and read watchdog reg F4 */
-	outb(0x07,0x370);
-	outb(0x08,0x371);
-	outb(0xF4,0x370);
-	new_status = inb(0x371);
+	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+	outb_p(0x08, IO_DATA_PORT);
+	outb_p(0xF4, IO_INDEX_PORT);
+	new_status = inb_p(IO_DATA_PORT);
 
 	/* lock the SuperIO chip */
-	outb(0xAA,0x370);
+	outb_p(LOCK_DATA, IO_INDEX_PORT);
 
+	spin_unlock_irqrestore(&spinlock, flags);
+
+	*status=0;
 	if (new_status & 1)
 		*status |= WDIOF_CARDRESET;
 
@@ -249,8 +280,8 @@
 		wdt977_stop();
 		clear_bit(0,&timer_alive);
 	} else {
-		printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
 		wdt977_keepalive();
+		printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
 	}
 	expect_close = 0;
 	return 0;
@@ -271,14 +302,17 @@
 static ssize_t wdt977_write(struct file *file, const char __user *buf,
 			    size_t count, loff_t *ppos)
 {
-	if (count) {
-		if (!nowayout) {
+	if (count)
+	{
+		if (!nowayout)
+		{
 			size_t i;
 
 			/* In case it was set long ago */
 			expect_close = 0;
 
-			for (i = 0; i != count; i++) {
+			for (i = 0; i != count; i++)
+			{
 				char c;
 				if (get_user(c, buf + i))
 					return -EFAULT;
@@ -287,6 +321,7 @@
 			}
 		}
 
+		/* someone wrote to us, we should restart timer */
 		wdt977_keepalive();
 	}
 	return count;
@@ -308,7 +343,7 @@
 				WDIOF_MAGICCLOSE |
 				WDIOF_KEEPALIVEPING,
 	.firmware_version =	1,
-	.identity =		"Winbond 83977",
+	.identity =		WATCHDOG_NAME,
 };
 
 static int wdt977_ioctl(struct inode *inode, struct file *file,
@@ -405,50 +440,81 @@
 	.notifier_call = wdt977_notify_sys,
 };
 
-static int __init nwwatchdog_init(void)
+static int __init wd977_init(void)
 {
-	int retval;
-	if (!machine_is_netwinder())
-		return -ENODEV;
+	int rc;
+
+	//if (!machine_is_netwinder())
+	//	return -ENODEV;
+
+	printk(KERN_INFO PFX DRIVER_VERSION);
+
+	spin_lock_init(&spinlock);
 
 	/* Check that the timeout value is within it's range ; if not reset to the default */
-	if (wdt977_set_timeout(timeout)) {
+	if (wdt977_set_timeout(timeout))
+	{
 		wdt977_set_timeout(DEFAULT_TIMEOUT);
 		printk(KERN_INFO PFX "timeout value must be 60<timeout<15300, using %d\n",
 			DEFAULT_TIMEOUT);
 	}
 
-	retval = register_reboot_notifier(&wdt977_notifier);
-	if (retval) {
-		printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
-			retval);
-		return retval;
+	/* on Netwinder the IOports are already reserved by
+	 * arch/arm/mach-footbridge/netwinder-hw.c
+	 */
+	if (!machine_is_netwinder())
+	{
+		if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME))
+		{
+			printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+				IO_INDEX_PORT);
+			rc = -EIO;
+			goto err_out;
+		}
 	}
 
-	retval = misc_register(&wdt977_miscdev);
-	if (retval) {
+	rc = misc_register(&wdt977_miscdev);
+	if (rc)
+	{
 		printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
-			WATCHDOG_MINOR, retval);
-		unregister_reboot_notifier(&wdt977_notifier);
-		return retval;
+			wdt977_miscdev.minor, rc);
+		goto err_out_region;
 	}
 
-	printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d, testmode = %i)\n",
+	rc = register_reboot_notifier(&wdt977_notifier);
+	if (rc)
+	{
+		printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
+			rc);
+		goto err_out_miscdev;
+	}
+
+	printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n",
 		timeout, nowayout, testmode);
 
 	return 0;
+
+err_out_miscdev:
+        misc_deregister(&wdt977_miscdev);
+err_out_region:
+	if (!machine_is_netwinder())
+	        release_region(IO_INDEX_PORT,2);
+err_out:
+	return rc;
 }
 
-static void __exit nwwatchdog_exit(void)
+static void __exit wd977_exit(void)
 {
+	wdt977_stop();
 	misc_deregister(&wdt977_miscdev);
 	unregister_reboot_notifier(&wdt977_notifier);
+	release_region(IO_INDEX_PORT,2);
 }
 
-module_init(nwwatchdog_init);
-module_exit(nwwatchdog_exit);
+module_init(wd977_init);
+module_exit(wd977_exit);
 
-MODULE_AUTHOR("Woody Suwalski <woody@netwinder.org>");
+MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>");
 MODULE_DESCRIPTION("W83977AF Watchdog driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);