summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorArjan van de Ven <arjan@linux.intel.com>2008-10-17 09:20:26 -0700
committerArjan van de Ven <arjan@linux.intel.com>2008-10-17 09:20:26 -0700
commit651dab4264e4ba0e563f5ff56f748127246e9065 (patch)
tree016630974bdcb00fe529b673f96d389e0fd6dc94 /drivers/watchdog
parent40b8606253552109815786e5d4b0de98782d31f5 (diff)
parent2e532d68a2b3e2aa6b19731501222069735c741c (diff)
Merge commit 'linus/master' into merge-linus
Conflicts: arch/x86/kvm/i8254.c
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig43
-rw-r--r--drivers/watchdog/Makefile7
-rw-r--r--drivers/watchdog/at91sam9_wdt.c328
-rw-r--r--drivers/watchdog/cpwd.c695
-rw-r--r--drivers/watchdog/geodewdt.c6
-rw-r--r--drivers/watchdog/ibmasr.c1
-rw-r--r--drivers/watchdog/it87_wdt.c725
-rw-r--r--drivers/watchdog/ixp4xx_wdt.c4
-rw-r--r--drivers/watchdog/omap_wdt.c337
-rw-r--r--drivers/watchdog/omap_wdt.h28
-rw-r--r--drivers/watchdog/orion5x_wdt.c245
-rw-r--r--drivers/watchdog/pnx4008_wdt.c4
-rw-r--r--drivers/watchdog/rc32434_wdt.c6
-rw-r--r--drivers/watchdog/rdc321x_wdt.c6
-rw-r--r--drivers/watchdog/riowd.c259
-rw-r--r--drivers/watchdog/w83697ug_wdt.c392
-rw-r--r--drivers/watchdog/wdt285.c15
17 files changed, 2947 insertions, 154 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c5103671670..1a22fe782a2 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -66,6 +66,13 @@ config AT91RM9200_WATCHDOG
Watchdog timer embedded into AT91RM9200 chips. This will reboot your
system when the timeout is reached.
+config AT91SAM9X_WATCHDOG
+ tristate "AT91SAM9X watchdog"
+ depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261)
+ help
+ Watchdog timer embedded into AT91SAM9X chips. This will reboot your
+ system when the timeout is reached.
+
config 21285_WATCHDOG
tristate "DC21285 watchdog"
depends on FOOTBRIDGE
@@ -217,6 +224,15 @@ config DAVINCI_WATCHDOG
NOTE: once enabled, this timer cannot be disabled.
Say N if you are unsure.
+config ORION5X_WATCHDOG
+ tristate "Orion5x watchdog"
+ depends on ARCH_ORION5X
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Orion5x ARM SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called orion5x_wdt.
+
# ARM26 Architecture
# AVR32 Architecture
@@ -416,6 +432,18 @@ config IT8712F_WDT
To compile this driver as a module, choose M here: the
module will be called it8712f_wdt.
+config IT87_WDT
+ tristate "IT87 Watchdog Timer"
+ depends on X86 && EXPERIMENTAL
+ ---help---
+ This is the driver for the hardware watchdog on the ITE IT8716,
+ IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog
+ simply watches your kernel to make sure it doesn't freeze, and if
+ it does, it reboots your computer after a certain amount of time.
+
+ To compile this driver as a module, choose M here: the module will
+ be called it87_wdt.
+
config HP_WATCHDOG
tristate "HP Proliant iLO 2 Hardware Watchdog Timer"
depends on X86
@@ -573,6 +601,21 @@ config W83697HF_WDT
Most people will say N.
+config W83697UG_WDT
+ tristate "W83697UG/W83697UF Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83697UG/UF
+ chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83697ug_wdt.
+
+ Most people will say N.
+
config W83877F_WDT
tristate "W83877F (EMACS) Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e0ef123fbde..e352bbb7630 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
+obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
+obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
# ARM26 Architecture
@@ -71,6 +73,7 @@ ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
endif
obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
+obj-$(CONFIG_IT87_WDT) += it87_wdt.o
obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
@@ -83,6 +86,7 @@ obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
+obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
@@ -123,6 +127,9 @@ obj-$(CONFIG_SH_WDT) += shwdt.o
# SPARC64 Architecture
+obj-$(CONFIG_WATCHDOG_RIO) += riowd.o
+obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
+
# XTENSA Architecture
# Architecture Independant
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
new file mode 100644
index 00000000000..b4babfc3158
--- /dev/null
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -0,0 +1,328 @@
+/*
+ * Watchdog driver for Atmel AT91SAM9x processors.
+ *
+ * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * The Watchdog Timer Mode Register can be only written to once. If the
+ * timeout need to be set from Linux, be sure that the bootstrap or the
+ * bootloader doesn't write to this register.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+
+#include <asm/arch/at91_wdt.h>
+
+#define DRV_NAME "AT91SAM9 Watchdog"
+
+/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
+ * use this to convert a watchdog
+ * value from/to milliseconds.
+ */
+#define ms_to_ticks(t) (((t << 8) / 1000) - 1)
+#define ticks_to_ms(t) (((t + 1) * 1000) >> 8)
+
+/* Hardware timeout in seconds */
+#define WDT_HW_TIMEOUT 2
+
+/* Timer heartbeat (500ms) */
+#define WDT_TIMEOUT (HZ/2)
+
+/* User land timeout */
+#define WDT_HEARTBEAT 15
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
+ "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void at91_ping(unsigned long data);
+
+static struct {
+ unsigned long next_heartbeat; /* the next_heartbeat for the timer */
+ unsigned long open;
+ char expect_close;
+ struct timer_list timer; /* The timer that pings the watchdog */
+} at91wdt_private;
+
+/* ......................................................................... */
+
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void at91_wdt_reset(void)
+{
+ at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
+}
+
+/*
+ * Timer tick
+ */
+static void at91_ping(unsigned long data)
+{
+ if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
+ (!nowayout && !at91wdt_private.open)) {
+ at91_wdt_reset();
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+ } else
+ printk(KERN_CRIT DRV_NAME": I will reset your machine !\n");
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at91_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &at91wdt_private.open))
+ return -EBUSY;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int at91_wdt_close(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &at91wdt_private.open);
+
+ /* stop internal ping */
+ if (!at91wdt_private.expect_close)
+ del_timer(&at91wdt_private.timer);
+
+ at91wdt_private.expect_close = 0;
+ return 0;
+}
+
+/*
+ * Set the watchdog time interval in 1/256Hz (write-once)
+ * Counter is 12 bit.
+ */
+static int at91_wdt_settimeout(unsigned int timeout)
+{
+ unsigned int reg;
+ unsigned int mr;
+
+ /* Check if disabled */
+ mr = at91_sys_read(AT91_WDT_MR);
+ if (mr & AT91_WDT_WDDIS) {
+ printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n");
+ return -EIO;
+ }
+
+ /*
+ * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
+ *
+ * Since WDV is a 12-bit counter, the maximum period is
+ * 4096 / 256 = 16 seconds.
+ */
+ reg = AT91_WDT_WDRSTEN /* causes watchdog reset */
+ /* | AT91_WDT_WDRPROC causes processor reset only */
+ | AT91_WDT_WDDBGHLT /* disabled in debug mode */
+ | AT91_WDT_WDD /* restart at any time */
+ | (timeout & AT91_WDT_WDV); /* timer value */
+ at91_sys_write(AT91_WDT_MR, reg);
+
+ return 0;
+}
+
+static const struct watchdog_info at91_wdt_info = {
+ .identity = DRV_NAME,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long at91_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &at91_wdt_info,
+ sizeof(at91_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return put_user(new_value, p); /* return current value */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ }
+ return -ENOTTY;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ at91wdt_private.expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ at91wdt_private.expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return len;
+}
+
+/* ......................................................................... */
+
+static const struct file_operations at91wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = at91_wdt_ioctl,
+ .open = at91_wdt_open,
+ .release = at91_wdt_close,
+ .write = at91_wdt_write,
+};
+
+static struct miscdevice at91wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &at91wdt_fops,
+};
+
+static int __init at91wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (at91wdt_miscdev.parent)
+ return -EBUSY;
+ at91wdt_miscdev.parent = &pdev->dev;
+
+ /* Set watchdog */
+ res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
+ if (res)
+ return res;
+
+ res = misc_register(&at91wdt_miscdev);
+ if (res)
+ return res;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ setup_timer(&at91wdt_private.timer, at91_ping, 0);
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+}
+
+static int __exit at91wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&at91wdt_miscdev);
+ if (!res)
+ at91wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+#ifdef CONFIG_PM
+
+static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ return 0;
+}
+
+static int at91wdt_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define at91wdt_suspend NULL
+#define at91wdt_resume NULL
+#endif
+
+static struct platform_driver at91wdt_driver = {
+ .remove = __exit_p(at91wdt_remove),
+ .suspend = at91wdt_suspend,
+ .resume = at91wdt_resume,
+ .driver = {
+ .name = "at91_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91sam_wdt_init(void)
+{
+ return platform_driver_probe(&at91wdt_driver, at91wdt_probe);
+}
+
+static void __exit at91sam_wdt_exit(void)
+{
+ platform_driver_unregister(&at91wdt_driver);
+}
+
+module_init(at91sam_wdt_init);
+module_exit(at91sam_wdt_exit);
+
+MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
new file mode 100644
index 00000000000..084dfe9cecf
--- /dev/null
+++ b/drivers/watchdog/cpwd.c
@@ -0,0 +1,695 @@
+/* cpwd.c - driver implementation for hardware watchdog
+ * timers found on Sun Microsystems CP1400 and CP1500 boards.
+ *
+ * This device supports both the generic Linux watchdog
+ * interface and Solaris-compatible ioctls as best it is
+ * able.
+ *
+ * NOTE: CP1400 systems appear to have a defective intr_mask
+ * register on the PLD, preventing the disabling of
+ * timer interrupts. We use a timer to periodically
+ * reset 'stopped' watchdogs on affected platforms.
+ *
+ * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/smp_lock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include <asm/watchdog.h>
+
+#define DRIVER_NAME "cpwd"
+#define PFX DRIVER_NAME ": "
+
+#define WD_OBPNAME "watchdog"
+#define WD_BADMODEL "SUNW,501-5336"
+#define WD_BTIMEOUT (jiffies + (HZ * 1000))
+#define WD_BLIMIT 0xFFFF
+
+#define WD0_MINOR 212
+#define WD1_MINOR 213
+#define WD2_MINOR 214
+
+/* Internal driver definitions. */
+#define WD0_ID 0
+#define WD1_ID 1
+#define WD2_ID 2
+#define WD_NUMDEVS 3
+
+#define WD_INTR_OFF 0
+#define WD_INTR_ON 1
+
+#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
+#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
+#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
+
+/* Register value definitions
+ */
+#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
+#define WD1_INTR_MASK 0x02
+#define WD2_INTR_MASK 0x04
+
+#define WD_S_RUNNING 0x01 /* Watchdog device status running */
+#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
+
+struct cpwd {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ unsigned int irq;
+
+ unsigned long timeout;
+ bool enabled;
+ bool reboot;
+ bool broken;
+ bool initialized;
+
+ struct {
+ struct miscdevice misc;
+ void __iomem *regs;
+ u8 intr_mask;
+ u8 runstatus;
+ u16 timeout;
+ } devs[WD_NUMDEVS];
+};
+
+static struct cpwd *cpwd_device;
+
+/* Sun uses Altera PLD EPF8820ATC144-4
+ * providing three hardware watchdogs:
+ *
+ * 1) RIC - sends an interrupt when triggered
+ * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
+ * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
+ *
+ *** Timer register block definition (struct wd_timer_regblk)
+ *
+ * dcntr and limit registers (halfword access):
+ * -------------------
+ * | 15 | ...| 1 | 0 |
+ * -------------------
+ * |- counter val -|
+ * -------------------
+ * dcntr - Current 16-bit downcounter value.
+ * When downcounter reaches '0' watchdog expires.
+ * Reading this register resets downcounter with 'limit' value.
+ * limit - 16-bit countdown value in 1/10th second increments.
+ * Writing this register begins countdown with input value.
+ * Reading from this register does not affect counter.
+ * NOTES: After watchdog reset, dcntr and limit contain '1'
+ *
+ * status register (byte access):
+ * ---------------------------
+ * | 7 | ... | 2 | 1 | 0 |
+ * --------------+------------
+ * |- UNUSED -| EXP | RUN |
+ * ---------------------------
+ * status- Bit 0 - Watchdog is running
+ * Bit 1 - Watchdog has expired
+ *
+ *** PLD register block definition (struct wd_pld_regblk)
+ *
+ * intr_mask register (byte access):
+ * ---------------------------------
+ * | 7 | ... | 3 | 2 | 1 | 0 |
+ * +-------------+------------------
+ * |- UNUSED -| WD3 | WD2 | WD1 |
+ * ---------------------------------
+ * WD3 - 1 == Interrupt disabled for watchdog 3
+ * WD2 - 1 == Interrupt disabled for watchdog 2
+ * WD1 - 1 == Interrupt disabled for watchdog 1
+ *
+ * pld_status register (byte access):
+ * UNKNOWN, MAGICAL MYSTERY REGISTER
+ *
+ */
+#define WD_TIMER_REGSZ 16
+#define WD0_OFF 0
+#define WD1_OFF (WD_TIMER_REGSZ * 1)
+#define WD2_OFF (WD_TIMER_REGSZ * 2)
+#define PLD_OFF (WD_TIMER_REGSZ * 3)
+
+#define WD_DCNTR 0x00
+#define WD_LIMIT 0x04
+#define WD_STATUS 0x08
+
+#define PLD_IMASK (PLD_OFF + 0x00)
+#define PLD_STATUS (PLD_OFF + 0x04)
+
+static struct timer_list cpwd_timer;
+
+static int wd0_timeout = 0;
+static int wd1_timeout = 0;
+static int wd2_timeout = 0;
+
+module_param (wd0_timeout, int, 0);
+MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
+module_param (wd1_timeout, int, 0);
+MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
+module_param (wd2_timeout, int, 0);
+MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
+
+MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("watchdog");
+
+static void cpwd_writew(u16 val, void __iomem *addr)
+{
+ writew(cpu_to_le16(val), addr);
+}
+static u16 cpwd_readw(void __iomem *addr)
+{
+ u16 val = readw(addr);
+
+ return le16_to_cpu(val);
+}
+
+static void cpwd_writeb(u8 val, void __iomem *addr)
+{
+ writeb(val, addr);
+}
+
+static u8 cpwd_readb(void __iomem *addr)
+{
+ return readb(addr);
+}
+
+/* Enable or disable watchdog interrupts
+ * Because of the CP1400 defect this should only be
+ * called during initialzation or by wd_[start|stop]timer()
+ *
+ * index - sub-device index, or -1 for 'all'
+ * enable - non-zero to enable interrupts, zero to disable
+ */
+static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
+{
+ unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
+ unsigned char setregs =
+ (index == -1) ?
+ (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
+ (p->devs[index].intr_mask);
+
+ if (enable == WD_INTR_ON)
+ curregs &= ~setregs;
+ else
+ curregs |= setregs;
+
+ cpwd_writeb(curregs, p->regs + PLD_IMASK);
+}
+
+/* Restarts timer with maximum limit value and
+ * does not unset 'brokenstop' value.
+ */
+static void cpwd_resetbrokentimer(struct cpwd *p, int index)
+{
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+ cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
+}
+
+/* Timer method called to reset stopped watchdogs--
+ * because of the PLD bug on CP1400, we cannot mask
+ * interrupts within the PLD so me must continually
+ * reset the timers ad infinitum.
+ */
+static void cpwd_brokentimer(unsigned long data)
+{
+ struct cpwd *p = (struct cpwd *) data;
+ int id, tripped = 0;
+
+ /* kill a running timer instance, in case we
+ * were called directly instead of by kernel timer
+ */
+ if (timer_pending(&cpwd_timer))
+ del_timer(&cpwd_timer);
+
+ for (id = 0; id < WD_NUMDEVS; id++) {
+ if (p->devs[id].runstatus & WD_STAT_BSTOP) {
+ ++tripped;
+ cpwd_resetbrokentimer(p, id);
+ }
+ }
+
+ if (tripped) {
+ /* there is at least one timer brokenstopped-- reschedule */
+ cpwd_timer.expires = WD_BTIMEOUT;
+ add_timer(&cpwd_timer);
+ }
+}
+
+/* Reset countdown timer with 'limit' value and continue countdown.
+ * This will not start a stopped timer.
+ */
+static void cpwd_pingtimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
+ cpwd_readw(p->devs[index].regs + WD_DCNTR);
+}
+
+/* Stop a running watchdog timer-- the timer actually keeps
+ * running, but the interrupt is masked so that no action is
+ * taken upon expiration.
+ */
+static void cpwd_stoptimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
+ cpwd_toggleintr(p, index, WD_INTR_OFF);
+
+ if (p->broken) {
+ p->devs[index].runstatus |= WD_STAT_BSTOP;
+ cpwd_brokentimer((unsigned long) p);
+ }
+ }
+}
+
+/* Start a watchdog timer with the specified limit value
+ * If the watchdog is running, it will be restarted with
+ * the provided limit value.
+ *
+ * This function will enable interrupts on the specified
+ * watchdog.
+ */
+static void cpwd_starttimer(struct cpwd *p, int index)
+{
+ if (p->broken)
+ p->devs[index].runstatus &= ~WD_STAT_BSTOP;
+
+ p->devs[index].runstatus &= ~WD_STAT_SVCD;
+
+ cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+}
+
+static int cpwd_getstatus(struct cpwd *p, int index)
+{
+ unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
+ unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
+ unsigned char ret = WD_STOPPED;
+
+ /* determine STOPPED */
+ if (!stat)
+ return ret;
+
+ /* determine EXPIRED vs FREERUN vs RUNNING */
+ else if (WD_S_EXPIRED & stat) {
+ ret = WD_EXPIRED;
+ } else if(WD_S_RUNNING & stat) {
+ if (intr & p->devs[index].intr_mask) {
+ ret = WD_FREERUN;
+ } else {
+ /* Fudge WD_EXPIRED status for defective CP1400--
+ * IF timer is running
+ * AND brokenstop is set
+ * AND an interrupt has been serviced
+ * we are WD_EXPIRED.
+ *
+ * IF timer is running
+ * AND brokenstop is set
+ * AND no interrupt has been serviced
+ * we are WD_FREERUN.
+ */
+ if (p->broken &&
+ (p->devs[index].runstatus & WD_STAT_BSTOP)) {
+ if (p->devs[index].runstatus & WD_STAT_SVCD) {
+ ret = WD_EXPIRED;
+ } else {
+ /* we could as well pretend we are expired */
+ ret = WD_FREERUN;
+ }
+ } else {
+ ret = WD_RUNNING;
+ }
+ }
+ }
+
+ /* determine SERVICED */
+ if (p->devs[index].runstatus & WD_STAT_SVCD)
+ ret |= WD_SERVICED;
+
+ return(ret);
+}
+
+static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
+{
+ struct cpwd *p = dev_id;
+
+ /* Only WD0 will interrupt-- others are NMI and we won't
+ * see them here....
+ */
+ spin_lock_irq(&p->lock);
+
+ cpwd_stoptimer(p, WD0_ID);
+ p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
+
+ spin_unlock_irq(&p->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int cpwd_open(struct inode *inode, struct file *f)
+{
+ struct cpwd *p = cpwd_device;
+
+ lock_kernel();
+ switch(iminor(inode)) {
+ case WD0_MINOR:
+ case WD1_MINOR:
+ case WD2_MINOR:
+ break;
+
+ default:
+ unlock_kernel();
+ return -ENODEV;
+ }
+
+ /* Register IRQ on first open of device */
+ if (!p->initialized) {
+ if (request_irq(p->irq, &cpwd_interrupt,
+ IRQF_SHARED, DRIVER_NAME, p)) {
+ printk(KERN_ERR PFX "Cannot register IRQ %d\n",
+ p->irq);
+ unlock_kernel();
+ return -EBUSY;
+ }
+ p->initialized = true;
+ }
+
+ unlock_kernel();
+
+ return nonseekable_open(inode, f);
+}
+
+static int cpwd_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int cpwd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ int index = iminor(inode) - WD0_MINOR;
+ struct cpwd *p = cpwd_device;
+ int setopt = 0;
+
+ switch (cmd) {
+ /* Generic Linux IOCTLs */
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ cpwd_pingtimer(p, index);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (setopt & WDIOS_DISABLECARD) {
+ if (p->enabled)
+ return -EINVAL;
+ cpwd_stoptimer(p, index);
+ } else if (setopt & WDIOS_ENABLECARD) {
+ cpwd_starttimer(p, index);
+ } else {
+ return -EINVAL;
+ }
+ break;
+
+ /* Solaris-compatible IOCTLs */
+ case WIOCGSTAT:
+ setopt = cpwd_getstatus(p, index);
+ if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
+ return -EFAULT;
+ break;
+
+ case WIOCSTART:
+ cpwd_starttimer(p, index);
+ break;
+
+ case WIOCSTOP:
+ if (p->enabled)
+ return(-EINVAL);
+
+ cpwd_stoptimer(p, index);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rval = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ /* solaris ioctls are specific to this driver */
+ case WIOCSTART:
+ case WIOCSTOP:
+ case WIOCGSTAT:
+ lock_kernel();
+ rval = cpwd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
+ unlock_kernel();
+ break;
+
+ /* everything else is handled by the generic compat layer */
+ default:
+ break;
+ }
+
+ return rval;
+}
+
+static ssize_t cpwd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct cpwd *p = cpwd_device;
+ int index = iminor(inode);
+
+ if (count) {
+ cpwd_pingtimer(p, index);
+ return 1;
+ }
+
+ return 0;
+}
+
+static ssize_t cpwd_read(struct file * file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static const struct file_operations cpwd_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = cpwd_ioctl,
+ .compat_ioctl = cpwd_compat_ioctl,
+ .open = cpwd_open,
+ .write = cpwd_write,
+ .read = cpwd_read,
+ .release = cpwd_release,
+};
+
+static int __devinit cpwd_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct device_node *options;
+ const char *str_prop;
+ const void *prop_val;
+ int i, err = -EINVAL;
+ struct cpwd *p;
+
+ if (cpwd_device)
+ return -EINVAL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!p) {
+ printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
+ goto out;
+ }
+
+ p->irq = op->irqs[0];
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0,
+ 4 * WD_TIMER_REGSZ, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Unable to map registers.\n");
+ goto out_free;
+ }
+
+ options = of_find_node_by_path("/options");
+ err = -ENODEV;
+ if (!options) {
+ printk(KERN_ERR PFX "Unable to find /options node.\n");
+ goto out_iounmap;
+ }
+
+ prop_val = of_get_property(options, "watchdog-enable?", NULL);
+ p->enabled = (prop_val ? true : false);
+
+ prop_val = of_get_property(options, "watchdog-reboot?", NULL);
+ p->reboot = (prop_val ? true : false);
+
+ str_prop = of_get_property(options, "watchdog-timeout", NULL);
+ if (str_prop)
+ p->timeout = simple_strtoul(str_prop, NULL, 10);
+
+ /* CP1400s seem to have broken PLD implementations-- the
+ * interrupt_mask register cannot be written, so no timer
+ * interrupts can be masked within the PLD.
+ */
+ str_prop = of_get_property(op->node, "model", NULL);
+ p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
+
+ if (!p->enabled)
+ cpwd_toggleintr(p, -1, WD_INTR_OFF);
+
+ for (i = 0; i < WD_NUMDEVS; i++) {
+ static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
+ static int *parms[] = { &wd0_timeout,
+ &wd1_timeout,
+ &wd2_timeout };
+ struct miscdevice *mp = &p->devs[i].misc;
+
+ mp->minor = WD0_MINOR + i;
+ mp->name = cpwd_names[i];
+ mp->fops = &cpwd_fops;
+
+ p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
+ p->devs[i].intr_mask = (WD0_INTR_MASK << i);
+ p->devs[i].runstatus &= ~WD_STAT_BSTOP;
+ p->devs[i].runstatus |= WD_STAT_INIT;
+ p->devs[i].timeout = p->timeout;
+ if (*parms[i])
+ p->devs[i].timeout = *parms[i];
+
+ err = misc_register(&p->devs[i].misc);
+ if (err) {
+ printk(KERN_ERR "Could not register misc device for "
+ "dev %d\n", i);
+ goto out_unregister;
+ }
+ }
+
+ if (p->broken) {
+ init_timer(&cpwd_timer);
+ cpwd_timer.function = cpwd_brokentimer;
+ cpwd_timer.data = (unsigned long) p;
+ cpwd_timer.expires = WD_BTIMEOUT;
+
+ printk(KERN_INFO PFX "PLD defect workaround enabled for "
+ "model " WD_BADMODEL ".\n");
+ }
+
+ dev_set_drvdata(&op->dev, p);
+ cpwd_device = p;
+ err = 0;
+
+out:
+ return err;
+
+out_unregister:
+ for (i--; i >= 0; i--)
+ misc_deregister(&p->devs[i].misc);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit cpwd_remove(struct of_device *op)
+{
+ struct cpwd *p = dev_get_drvdata(&op->dev);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ misc_deregister(&p->devs[i].misc);
+
+ if (!p->enabled) {
+ cpwd_stoptimer(p, i);
+ if (p->devs[i].runstatus & WD_STAT_BSTOP)
+ cpwd_resetbrokentimer(p, i);
+ }
+ }
+
+ if (p->broken)
+ del_timer_sync(&cpwd_timer);
+
+ if (p->initialized)
+ free_irq(p->irq, p);
+
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+ kfree(p);
+
+ cpwd_device = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id cpwd_match[] = {
+ {
+ .name = "watchdog",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpwd_match);
+
+static struct of_platform_driver cpwd_driver = {
+ .name = DRIVER_NAME,
+ .match_table = cpwd_match,
+ .probe = cpwd_probe,
+ .remove = __devexit_p(cpwd_remove),
+};
+
+static int __init cpwd_init(void)
+{
+ return of_register_driver(&cpwd_driver, &of_bus_type);
+}
+
+static void __exit cpwd_exit(void)
+{
+ of_unregister_driver(&cpwd_driver);
+}
+
+module_init(cpwd_init);
+module_exit(cpwd_exit);
diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c
index 614a5c7017b..6799a6de66f 100644
--- a/drivers/watchdog/geodewdt.c
+++ b/drivers/watchdog/geodewdt.c
@@ -130,8 +130,8 @@ static ssize_t geodewdt_write(struct file *file, const char __user *data,
return len;
}
-static int geodewdt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long geodewdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
@@ -198,7 +198,7 @@ static const struct file_operations geodewdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = geodewdt_write,
- .ioctl = geodewdt_ioctl,
+ .unlocked_ioctl = geodewdt_ioctl,
.open = geodewdt_open,
.release = geodewdt_release,
};
diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c
index b82405cfb4c..89fcefcc851 100644
--- a/drivers/watchdog/ibmasr.c
+++ b/drivers/watchdog/ibmasr.c
@@ -85,7 +85,6 @@ static void __asr_toggle(void)
outb(reg & ~asr_toggle_mask, asr_write_addr);
reg = inb(asr_read_addr);
- spin_unlock(&asr_lock);
}
static void asr_toggle(void)
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
new file mode 100644
index 00000000000..afb8af397a9
--- /dev/null
+++ b/drivers/watchdog/it87_wdt.c
@@ -0,0 +1,725 @@
+/*
+ * Watchdog Timer Driver
+ * for ITE IT87xx Environment Control - Low Pin Count Input / Output
+ *
+ * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com>
+ *
+ * Based on softdog.c by Alan Cox,
+ * 83977f_wdt.c by Jose Goncalves,
+ * it87.c by Chris Gauthron, Jean Delvare
+ *
+ * Data-sheets: Publicly available at the ITE website
+ * http://www.ite.com.tw/
+ *
+ * Support of the watchdog timers, which are available on
+ * IT8716, IT8718, IT8726 and IT8712 (J,K version).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#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>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_VERSION "1.12"
+#define WATCHDOG_NAME "IT87 WDT"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+#define WD_MAGIC 'V'
+
+/* Defaults for Module Parameter */
+#define DEFAULT_NOGAMEPORT 0
+#define DEFAULT_EXCLUSIVE 1
+#define DEFAULT_TIMEOUT 60
+#define DEFAULT_TESTMODE 0
+#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
+
+/* IO Ports */
+#define REG 0x2e
+#define VAL 0x2f
+
+/* Logical device Numbers LDN */
+#define GPIO 0x07
+#define GAMEPORT 0x09
+#define CIR 0x0a
+
+/* Configuration Registers and Functions */
+#define LDNREG 0x07
+#define CHIPID 0x20
+#define CHIPREV 0x22
+#define ACTREG 0x30
+#define BASEREG 0x60
+
+/* Chip Id numbers */
+#define NO_DEV_ID 0xffff
+#define IT8705_ID 0x8705
+#define IT8712_ID 0x8712
+#define IT8716_ID 0x8716
+#define IT8718_ID 0x8718
+#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
+
+/* GPIO Configuration Registers LDN=0x07 */
+#define WDTCTRL 0x71
+#define WDTCFG 0x72
+#define WDTVALLSB 0x73
+#define WDTVALMSB 0x74
+
+/* GPIO Bits WDTCTRL */
+#define WDT_CIRINT 0x80
+#define WDT_MOUSEINT 0x40
+#define WDT_KYBINT 0x20
+#define WDT_GAMEPORT 0x10 /* not it8718 */
+#define WDT_FORCE 0x02
+#define WDT_ZERO 0x01
+
+/* GPIO Bits WDTCFG */
+#define WDT_TOV1 0x80
+#define WDT_KRST 0x40
+#define WDT_TOVE 0x20
+#define WDT_PWROK 0x10
+#define WDT_INT_MASK 0x0f
+
+/* CIR Configuration Register LDN=0x0a */
+#define CIR_ILS 0x70
+
+/* The default Base address is not always available, we use this */
+#define CIR_BASE 0x0208
+
+/* CIR Controller */
+#define CIR_DR(b) (b)
+#define CIR_IER(b) (b + 1)
+#define CIR_RCR(b) (b + 2)
+#define CIR_TCR1(b) (b + 3)
+#define CIR_TCR2(b) (b + 4)
+#define CIR_TSR(b) (b + 5)
+#define CIR_RSR(b) (b + 6)
+#define CIR_BDLR(b) (b + 5)
+#define CIR_BDHR(b) (b + 6)
+#define CIR_IIR(b) (b + 7)
+
+/* Default Base address of Game port */
+#define GP_BASE_DEFAULT 0x0201
+
+/* wdt_status */
+#define WDTS_TIMER_RUN 0
+#define WDTS_DEV_OPEN 1
+#define WDTS_KEEPALIVE 2
+#define WDTS_LOCKED 3
+#define WDTS_USE_GP 4
+#define WDTS_EXPECTED 5
+
+static unsigned int base, gpact, ciract;
+static unsigned long wdt_status;
+static DEFINE_SPINLOCK(spinlock);
+
+static int nogameport = DEFAULT_NOGAMEPORT;
+static int exclusive = DEFAULT_EXCLUSIVE;
+static int timeout = DEFAULT_TIMEOUT;
+static int testmode = DEFAULT_TESTMODE;
+static int nowayout = DEFAULT_NOWAYOUT;
+
+module_param(nogameport, int, 0);
+MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
+ __MODULE_STRING(DEFAULT_NOGAMEPORT));
+module_param(exclusive, int, 0);
+MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
+ __MODULE_STRING(DEFAULT_EXCLUSIVE));
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT));
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
+ __MODULE_STRING(DEFAULT_TESTMODE));
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT));
+
+/* Superio Chip */
+
+static inline void superio_enter(void)
+{
+ outb(0x87, REG);
+ outb(0x01, REG);
+ outb(0x55, REG);
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0x02, REG);
+ outb(0x02, VAL);
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(LDNREG, REG);
+ outb(ldn, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+static inline void superio_outb(int val, int reg)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inw(int reg)
+{
+ int val;
+ outb(reg++, REG);
+ val = inb(VAL) << 8;
+ outb(reg, REG);
+ val |= inb(VAL);
+ return val;
+}
+
+static inline void superio_outw(int val, int reg)
+{
+ outb(reg++, REG);
+ outb(val >> 8, VAL);
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+/* watchdog timer handling */
+
+static void wdt_keepalive(void)
+{
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ inb(base);
+ else
+ /* The timer reloads with around 5 msec delay */
+ outb(0x55, CIR_DR(base));
+ set_bit(WDTS_KEEPALIVE, &wdt_status);
+}
+
+static void wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ superio_outb(WDT_GAMEPORT, WDTCTRL);
+ else
+ superio_outb(WDT_CIRINT, WDTCTRL);
+ if (!testmode)
+ superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG);
+ else
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(timeout>>8, WDTVALMSB);
+ superio_outb(timeout, WDTVALLSB);
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+static void wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTVALMSB);
+ superio_outb(0x00, WDTVALLSB);
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+/**
+ * wdt_set_timeout - set a new timeout value with watchdog ioctl
+ * @t: timeout value in seconds
+ *
+ * The hardware device has a 16 bit watchdog timer, thus the
+ * timeout time ranges between 1 and 65535 seconds.
+ *
+ * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
+ */
+
+static int wdt_set_timeout(int t)
+{
+ unsigned long flags;
+
+ if (t < 1 || t > 65535)
+ return -EINVAL;
+
+ timeout = t;
+
+ spin_lock_irqsave(&spinlock, flags);
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(t>>8, WDTVALMSB);
+ superio_outb(t, WDTVALLSB);
+
+ superio_exit();
+ }
+ spin_unlock_irqrestore(&spinlock, flags);
+ return 0;
+}
+
+/**
+ * wdt_get_status - determines the status supported by watchdog ioctl
+ * @status: status returned to user space
+ *
+ * The status bit of the device does not allow to distinguish
+ * between a regular system reset and a watchdog forced reset.
+ * But, in test mode it is useful, so it is supported through
+ * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
+ * reports the keepalive signal and the acception of the magic.
+ *
+ * Used within WDIOC_GETSTATUS watchdog device ioctl.
+ */
+
+static int wdt_get_status(int *status)
+{
+ unsigned long flags;
+
+ *status = 0;
+ if (testmode) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ if (superio_inb(WDTCTRL) & WDT_ZERO) {
+ superio_outb(0x00, WDTCTRL);
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ *status |= WDIOF_CARDRESET;
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+ if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
+ *status |= WDIOF_KEEPALIVEPING;
+ if (test_bit(WDTS_EXPECTED, &wdt_status))
+ *status |= WDIOF_MAGICCLOSE;
+ return 0;
+}
+
+/* /dev/watchdog handling */
+
+/**
+ * wdt_open - watchdog file_operations .open
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * The watchdog timer starts by opening the device.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
+ return -EBUSY;
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
+ __module_get(THIS_MODULE);
+ wdt_start();
+ }
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdt_release - watchdog file_operations .release
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * Closing the watchdog device either stops the watchdog timer
+ * or in the case, that nowayout is set or the magic character
+ * wasn't written, a critical warning about an running watchdog
+ * timer is given.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ } else {
+ wdt_keepalive();
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ }
+ }
+ clear_bit(WDTS_DEV_OPEN, &wdt_status);
+ return 0;
+}
+
+/**
+ * wdt_write - watchdog file_operations .write
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we don't define content meaning.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ clear_bit(WDTS_EXPECTED, &wdt_status);
+ wdt_keepalive();
+ }
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character long ago */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == WD_MAGIC)
+ set_bit(WDTS_EXPECTED, &wdt_status);
+ }
+ }
+ return count;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
+
+/**
+ * wdt_ioctl - watchdog file_operations .unlocked_ioctl
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0, status, new_options, new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident,
+ &ident, sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ wdt_get_status(&status);
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ switch (new_options) {
+ case WDIOS_DISABLECARD:
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ return 0;
+
+ case WDIOS_ENABLECARD:
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_start();
+ return 0;
+
+ default:
+ return -EFAULT;
+ }
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+ rc = wdt_set_timeout(new_timeout);
+ case WDIOC_GETTIMEOUT:
+ if (put_user(timeout, uarg.i))
+ return -EFAULT;
+ return rc;
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_stop();
+ return NOTIFY_DONE;
+}
+
+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_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init it87_wdt_init(void)
+{
+ int rc = 0;
+ u16 chip_type;
+ u8 chip_rev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ chip_type = superio_inw(CHIPID);
+ chip_rev = superio_inb(CHIPREV) & 0x0f;
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ switch (chip_type) {
+ case IT8716_ID:
+ case IT8718_ID:
+ case IT8726_ID:
+ break;
+ case IT8712_ID:
+ if (chip_rev > 7)
+ break;
+ case IT8705_ID:
+ printk(KERN_ERR PFX
+ "Unsupported Chip found, Chip %04x Revision %02x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ case NO_DEV_ID:
+ printk(KERN_ERR PFX "no device\n");
+ return -ENODEV;
+ default:
+ printk(KERN_ERR PFX
+ "Unknown Chip found, Chip %04x Revision %04x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTCTRL);
+
+ /* First try to get Gameport support */
+ if (chip_type != IT8718_ID && !nogameport) {
+ superio_select(GAMEPORT);
+ base = superio_inw(BASEREG);
+ if (!base) {
+ base = GP_BASE_DEFAULT;
+ superio_outw(base, BASEREG);
+ }
+ gpact = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ if (request_region(base, 1, WATCHDOG_NAME))
+ set_bit(WDTS_USE_GP, &wdt_status);
+ else
+ rc = -EIO;
+ } else {
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ /* If we haven't Gameport support, try to get CIR support */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
+ if (rc == -EIO)
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x and 0x%04x"
+ " already in use\n", base, CIR_BASE);
+ else
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x already in use\n",
+ CIR_BASE);
+ rc = -EIO;
+ goto err_out;
+ }
+ base = CIR_BASE;
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(CIR);
+ superio_outw(base, BASEREG);
+ superio_outb(0x00, CIR_ILS);
+ ciract = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ if (rc == -EIO) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ if (timeout < 1 || timeout > 65535) {
+ timeout = DEFAULT_TIMEOUT;
+ printk(KERN_WARNING PFX
+ "Timeout value out of range, use default %d sec\n",
+ DEFAULT_TIMEOUT);
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ /* Initialize CIR to use it as keepalive source */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ outb(0x00, CIR_RCR(base));
+ outb(0xc0, CIR_TCR1(base));
+ outb(0x5c, CIR_TCR2(base));
+ outb(0x10, CIR_IER(base));
+ outb(0x00, CIR_BDHR(base));
+ outb(0x01, CIR_BDLR(base));
+ outb(0x09, CIR_IER(base));
+ }
+
+ printk(KERN_INFO PFX "Chip it%04x revision %d initialized. "
+ "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d "
+ "nogameport=%d)\n", chip_type, chip_rev, timeout,
+ nowayout, testmode, exclusive, nogameport);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region:
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+err_out:
+ if (chip_type != IT8718_ID && !nogameport) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ return rc;
+}
+
+static void __exit it87_wdt_exit(void)
+{
+ unsigned long flags;
+ int nolock;
+
+ nolock = !spin_trylock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(0x00, WDTCFG);
+ superio_outb(0x00, WDTVALMSB);
+ superio_outb(0x00, WDTVALLSB);
+ if (test_bit(WDTS_USE_GP, &wdt_status)) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ } else {
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ }
+ superio_exit();
+ if (!nolock)
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+}
+
+module_init(it87_wdt_init);
+module_exit(it87_wdt_exit);
+
+MODULE_AUTHOR("Oliver Schuster");
+MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c
index 8302ef005be..147b4d5c63b 100644
--- a/drivers/watchdog/ixp4xx_wdt.c
+++ b/drivers/watchdog/ixp4xx_wdt.c
@@ -174,10 +174,8 @@ static struct miscdevice ixp4xx_wdt_miscdev = {
static int __init ixp4xx_wdt_init(void)
{
int ret;
- unsigned long processor_id;
- asm("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(processor_id) :);
- if (!(processor_id & 0xf) && !cpu_is_ixp46x()) {
+ if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
printk(KERN_ERR "IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected"
" - watchdog disabled\n");
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 3a11dadfd8e..7bcbb7f4745 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/char/watchdog/omap_wdt.c
+ * omap_wdt.c
*
- * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog
+ * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
*
* Author: MontaVista Software, Inc.
* <gdavis@mvista.com> or <source@mvista.com>
@@ -47,50 +47,68 @@
#include "omap_wdt.h"
+static struct platform_device *omap_wdt_dev;
+
static unsigned timer_margin;
module_param(timer_margin, uint, 0);
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
-static int omap_wdt_users;
-static struct clk *armwdt_ck;
-static struct clk *mpu_wdt_ick;
-static struct clk *mpu_wdt_fck;
-
static unsigned int wdt_trgr_pattern = 0x1234;
static spinlock_t wdt_lock;
-static void omap_wdt_ping(void)
+struct omap_wdt_dev {
+ void __iomem *base; /* physical */
+ struct device *dev;
+ int omap_wdt_users;
+ struct clk *armwdt_ck;
+ struct clk *mpu_wdt_ick;
+ struct clk *mpu_wdt_fck;
+ struct resource *mem;
+ struct miscdevice omap_wdt_miscdev;
+};
+
+static void omap_wdt_ping(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* wait for posted write to complete */
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
+
wdt_trgr_pattern = ~wdt_trgr_pattern;
- omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR));
+ __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+
/* wait for posted write to complete */
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
/* reloaded WCRR from WLDR */
}
-static void omap_wdt_enable(void)
+static void omap_wdt_enable(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* Sequence to enable the watchdog */
- omap_writel(0xBBBB, OMAP_WATCHDOG_SPR);
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
- omap_writel(0x4444, OMAP_WATCHDOG_SPR);
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+
+ __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
}
-static void omap_wdt_disable(void)
+static void omap_wdt_disable(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* sequence required to disable watchdog */
- omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
- omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+
+ __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
}
@@ -103,83 +121,90 @@ static void omap_wdt_adjust_timeout(unsigned new_timeout)
timer_margin = new_timeout;
}
-static void omap_wdt_set_timeout(void)
+static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
{
u32 pre_margin = GET_WLDR_VAL(timer_margin);
+ void __iomem *base = wdev->base;
/* just count up at 32 KHz */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
- omap_writel(pre_margin, OMAP_WATCHDOG_LDR);
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+
+ __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
}
/*
* Allow only one task to hold it open
*/
-
static int omap_wdt_open(struct inode *inode, struct file *file)
{
- if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users))
+ struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
+ void __iomem *base = wdev->base;
+
+ if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
return -EBUSY;
if (cpu_is_omap16xx())
- clk_enable(armwdt_ck); /* Enable the clock */
+ clk_enable(wdev->armwdt_ck); /* Enable the clock */
- if (cpu_is_omap24xx()) {
- clk_enable(mpu_wdt_ick); /* Enable the interface clock */
- clk_enable(mpu_wdt_fck); /* Enable the functional clock */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
+ clk_enable(wdev->mpu_wdt_ick); /* Enable the interface clock */
+ clk_enable(wdev->mpu_wdt_fck); /* Enable the functional clock */
}
/* initialize prescaler */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
- omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL);
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+
+ __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
- omap_wdt_set_timeout();
- omap_wdt_enable();
+ file->private_data = (void *) wdev;
+
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_enable(wdev);
+
return nonseekable_open(inode, file);
}
static int omap_wdt_release(struct inode *inode, struct file *file)
{
+ struct omap_wdt_dev *wdev = file->private_data;
+
/*
* Shut off the timer unless NOWAYOUT is defined.
*/
#ifndef CONFIG_WATCHDOG_NOWAYOUT
- omap_wdt_disable();
- if (cpu_is_omap16xx()) {
- clk_disable(armwdt_ck); /* Disable the clock */
- clk_put(armwdt_ck);
- armwdt_ck = NULL;
- }
+ omap_wdt_disable(wdev);
- if (cpu_is_omap24xx()) {
- clk_disable(mpu_wdt_ick); /* Disable the clock */
- clk_disable(mpu_wdt_fck); /* Disable the clock */
- clk_put(mpu_wdt_ick);
- clk_put(mpu_wdt_fck);
- mpu_wdt_ick = NULL;
- mpu_wdt_fck = NULL;
+ if (cpu_is_omap16xx())
+ clk_disable(wdev->armwdt_ck); /* Disable the clock */
+
+ if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
+ clk_disable(wdev->mpu_wdt_ick); /* Disable the clock */
+ clk_disable(wdev->mpu_wdt_fck); /* Disable the clock */
}
#else
printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
#endif
- omap_wdt_users = 0;
+ wdev->omap_wdt_users = 0;
+
return 0;
}
static ssize_t omap_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
+ struct omap_wdt_dev *wdev = file->private_data;
+
/* Refresh LOAD_TIME. */
if (len) {
spin_lock(&wdt_lock);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
}
return len;
@@ -188,6 +213,7 @@ static ssize_t omap_wdt_write(struct file *file, const char __user *data,
static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ struct omap_wdt_dev *wdev;
int new_margin;
static const struct watchdog_info ident = {
.identity = "OMAP Watchdog",
@@ -195,6 +221,8 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
.firmware_version = 0,
};
+ wdev = file->private_data;
+
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info __user *)arg, &ident,
@@ -203,14 +231,14 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
return put_user(0, (int __user *)arg);
case WDIOC_GETBOOTSTATUS:
if (cpu_is_omap16xx())
- return put_user(omap_readw(ARM_SYSST),
+ return put_user(__raw_readw(ARM_SYSST),
(int __user *)arg);
if (cpu_is_omap24xx())
return put_user(omap_prcm_get_reset_sources(),
(int __user *)arg);
case WDIOC_KEEPALIVE:
spin_lock(&wdt_lock);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
return 0;
case WDIOC_SETTIMEOUT:
@@ -219,11 +247,11 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
omap_wdt_adjust_timeout(new_margin);
spin_lock(&wdt_lock);
- omap_wdt_disable();
- omap_wdt_set_timeout();
- omap_wdt_enable();
+ omap_wdt_disable(wdev);
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_enable(wdev);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
/* Fall */
case WDIOC_GETTIMEOUT:
@@ -241,96 +269,173 @@ static const struct file_operations omap_wdt_fops = {
.release = omap_wdt_release,
};
-static struct miscdevice omap_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &omap_wdt_fops,
-};
-
static int __init omap_wdt_probe(struct platform_device *pdev)
{
struct resource *res, *mem;
+ struct omap_wdt_dev *wdev;
int ret;
/* reserve static register mappings */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
+ if (!res) {
+ ret = -ENOENT;
+ goto err_get_resource;
+ }
+
+ if (omap_wdt_dev) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
mem = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
- if (mem == NULL)
- return -EBUSY;
+ if (!mem) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
- platform_set_drvdata(pdev, mem);
+ wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
+ if (!wdev) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
- omap_wdt_users = 0;
+ wdev->omap_wdt_users = 0;
+ wdev->mem = mem;
if (cpu_is_omap16xx()) {
- armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
- if (IS_ERR(armwdt_ck)) {
- ret = PTR_ERR(armwdt_ck);
- armwdt_ck = NULL;
- goto fail;
+ wdev->armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
+ if (IS_ERR(wdev->armwdt_ck)) {
+ ret = PTR_ERR(wdev->armwdt_ck);
+ wdev->armwdt_ck = NULL;
+ goto err_clk;
}
}
if (cpu_is_omap24xx()) {
- mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
- if (IS_ERR(mpu_wdt_ick)) {
- ret = PTR_ERR(mpu_wdt_ick);
- mpu_wdt_ick = NULL;
- goto fail;
+ wdev->mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
+ if (IS_ERR(wdev->mpu_wdt_ick)) {
+ ret = PTR_ERR(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ goto err_clk;
}
- mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
- if (IS_ERR(mpu_wdt_fck)) {
- ret = PTR_ERR(mpu_wdt_fck);
- mpu_wdt_fck = NULL;
- goto fail;
+ wdev->mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
+ if (IS_ERR(wdev->mpu_wdt_fck)) {
+ ret = PTR_ERR(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ goto err_clk;
}
}
- omap_wdt_disable();
+ if (cpu_is_omap34xx()) {
+ wdev->mpu_wdt_ick = clk_get(&pdev->dev, "wdt2_ick");
+ if (IS_ERR(wdev->mpu_wdt_ick)) {
+ ret = PTR_ERR(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ goto err_clk;
+ }
+ wdev->mpu_wdt_fck = clk_get(&pdev->dev, "wdt2_fck");
+ if (IS_ERR(wdev->mpu_wdt_fck)) {
+ ret = PTR_ERR(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ goto err_clk;
+ }
+ }
+ wdev->base = ioremap(res->start, res->end - res->start + 1);
+ if (!wdev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ platform_set_drvdata(pdev, wdev);
+
+ omap_wdt_disable(wdev);
omap_wdt_adjust_timeout(timer_margin);
- omap_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&omap_wdt_miscdev);
+ wdev->omap_wdt_miscdev.parent = &pdev->dev;
+ wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
+ wdev->omap_wdt_miscdev.name = "watchdog";
+ wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
+
+ ret = misc_register(&(wdev->omap_wdt_miscdev));
if (ret)
- goto fail;
+ goto err_misc;
- pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin);
+ pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
+ __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
+ timer_margin);
/* autogate OCP interface clock */
- omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG);
+ __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
+
+ omap_wdt_dev = pdev;
+
return 0;
-fail:
- if (armwdt_ck)
- clk_put(armwdt_ck);
- if (mpu_wdt_ick)
- clk_put(mpu_wdt_ick);
- if (mpu_wdt_fck)
- clk_put(mpu_wdt_fck);
- release_resource(mem);
+err_misc:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(wdev->base);
+
+err_ioremap:
+ wdev->base = NULL;
+
+err_clk:
+ if (wdev->armwdt_ck)
+ clk_put(wdev->armwdt_ck);
+ if (wdev->mpu_wdt_ick)
+ clk_put(wdev->mpu_wdt_ick);
+ if (wdev->mpu_wdt_fck)
+ clk_put(wdev->mpu_wdt_fck);
+ kfree(wdev);
+
+err_kzalloc:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+err_busy:
+err_get_resource:
+
return ret;
}
static void omap_wdt_shutdown(struct platform_device *pdev)
{
- omap_wdt_disable();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users)
+ omap_wdt_disable(wdev);
}
static int omap_wdt_remove(struct platform_device *pdev)
{
- struct resource *mem = platform_get_drvdata(pdev);
- misc_deregister(&omap_wdt_miscdev);
- release_resource(mem);
- if (armwdt_ck)
- clk_put(armwdt_ck);
- if (mpu_wdt_ick)
- clk_put(mpu_wdt_ick);
- if (mpu_wdt_fck)
- clk_put(mpu_wdt_fck);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ misc_deregister(&(wdev->omap_wdt_miscdev));
+ release_mem_region(res->start, res->end - res->start + 1);
+ platform_set_drvdata(pdev, NULL);
+
+ if (wdev->armwdt_ck) {
+ clk_put(wdev->armwdt_ck);
+ wdev->armwdt_ck = NULL;
+ }
+
+ if (wdev->mpu_wdt_ick) {
+ clk_put(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ }
+
+ if (wdev->mpu_wdt_fck) {
+ clk_put(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ }
+ iounmap(wdev->base);
+
+ kfree(wdev);
+ omap_wdt_dev = NULL;
+
return 0;
}
@@ -344,17 +449,23 @@ static int omap_wdt_remove(struct platform_device *pdev)
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
- if (omap_wdt_users)
- omap_wdt_disable();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users)
+ omap_wdt_disable(wdev);
+
return 0;
}
static int omap_wdt_resume(struct platform_device *pdev)
{
- if (omap_wdt_users) {
- omap_wdt_enable();
- omap_wdt_ping();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users) {
+ omap_wdt_enable(wdev);
+ omap_wdt_ping(wdev);
}
+
return 0;
}
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h
index 52a532a5114..fc02ec6a038 100644
--- a/drivers/watchdog/omap_wdt.h
+++ b/drivers/watchdog/omap_wdt.h
@@ -30,25 +30,15 @@
#ifndef _OMAP_WATCHDOG_H
#define _OMAP_WATCHDOG_H
-#define OMAP1610_WATCHDOG_BASE 0xfffeb000
-#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */
-
-#ifdef CONFIG_ARCH_OMAP24XX
-#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE
-#else
-#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE
-#define RM_RSTST_WKUP 0
-#endif
-
-#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00)
-#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10)
-#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14)
-#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24)
-#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28)
-#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c)
-#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30)
-#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34)
-#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48)
+#define OMAP_WATCHDOG_REV (0x00)
+#define OMAP_WATCHDOG_SYS_CONFIG (0x10)
+#define OMAP_WATCHDOG_STATUS (0x14)
+#define OMAP_WATCHDOG_CNTRL (0x24)
+#define OMAP_WATCHDOG_CRR (0x28)
+#define OMAP_WATCHDOG_LDR (0x2c)
+#define OMAP_WATCHDOG_TGR (0x30)
+#define OMAP_WATCHDOG_WPS (0x34)
+#define OMAP_WATCHDOG_SPR (0x48)
/* Using the prescaler, the OMAP watchdog could go for many
* months before firing. These limits work without scaling,
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c
new file mode 100644
index 00000000000..14a339f58b6
--- /dev/null
+++ b/drivers/watchdog/orion5x_wdt.c
@@ -0,0 +1,245 @@
+/*
+ * drivers/watchdog/orion5x_wdt.c
+ *
+ * Watchdog driver for Orion5x processors
+ *
+ * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+/*
+ * Watchdog timer block registers.
+ */
+#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
+#define WDT_EN 0x0010
+#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
+
+#define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK)
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = WDT_MAX_DURATION; /* (seconds) */
+static unsigned long wdt_status;
+static spinlock_t wdt_lock;
+
+static void wdt_enable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Set watchdog duration */
+ writel(ORION5X_TCLK * heartbeat, WDT_VAL);
+
+ /* Clear watchdog timer interrupt */
+ reg = readl(BRIDGE_CAUSE);
+ reg &= ~WDT_INT_REQ;
+ writel(reg, BRIDGE_CAUSE);
+
+ /* Enable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg |= WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ /* Enable reset on watchdog */
+ reg = readl(CPU_RESET_MASK);
+ reg |= WDT_RESET;
+ writel(reg, CPU_RESET_MASK);
+
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Disable reset on watchdog */
+ reg = readl(CPU_RESET_MASK);
+ reg &= ~WDT_RESET;
+ writel(reg, CPU_RESET_MASK);
+
+ /* Disable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg &= ~WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ spin_unlock(&wdt_lock);
+}
+
+static int orion5x_wdt_get_timeleft(int *time_left)
+{
+ spin_lock(&wdt_lock);
+ *time_left = readl(WDT_VAL) / ORION5X_TCLK;
+ spin_unlock(&wdt_lock);
+ return 0;
+}
+
+static int orion5x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t orion5x_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Orion5x Watchdog",
+};
+
+
+static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > WDT_MAX_DURATION) {
+ ret = -EINVAL;
+ break;
+ }
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+
+ case WDIOC_GETTIMELEFT:
+ if (orion5x_wdt_get_timeleft(&time)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = put_user(time, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int orion5x_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations orion5x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = orion5x_wdt_write,
+ .unlocked_ioctl = orion5x_wdt_ioctl,
+ .open = orion5x_wdt_open,
+ .release = orion5x_wdt_release,
+};
+
+static struct miscdevice orion5x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &orion5x_wdt_fops,
+};
+
+static int __init orion5x_wdt_init(void)
+{
+ int ret;
+
+ spin_lock_init(&wdt_lock);
+
+ ret = misc_register(&orion5x_wdt_miscdev);
+ if (ret == 0)
+ printk("Orion5x Watchdog Timer: heartbeat %d sec\n",
+ heartbeat);
+
+ return ret;
+}
+
+static void __exit orion5x_wdt_exit(void)
+{
+ misc_deregister(&orion5x_wdt_miscdev);
+}
+
+module_init(orion5x_wdt_init);
+module_exit(orion5x_wdt_exit);
+
+MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
+MODULE_DESCRIPTION("Orion5x Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is "
+ __MODULE_STRING(WDT_MAX_DURATION) ")");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 0ed84162437..6d9f3d4a998 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -173,8 +173,8 @@ static const struct watchdog_info ident = {
.identity = "PNX4008 Watchdog",
};
-static long pnx4008_wdt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long pnx4008_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
int ret = -ENOTTY;
int time;
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
index 6756bcb009e..c9c73b69c5e 100644
--- a/drivers/watchdog/rc32434_wdt.c
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -182,8 +182,8 @@ static ssize_t rc32434_wdt_write(struct file *file, const char *data,
return 0;
}
-static int rc32434_wdt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
void __user *argp = (void __user *)arg;
int new_timeout;
@@ -242,7 +242,7 @@ static struct file_operations rc32434_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = rc32434_wdt_write,
- .ioctl = rc32434_wdt_ioctl,
+ .unlocked_ioctl = rc32434_wdt_ioctl,
.open = rc32434_wdt_open,
.release = rc32434_wdt_release,
};
diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c
index 9108efa73e7..bf92802f2bb 100644
--- a/drivers/watchdog/rdc321x_wdt.c
+++ b/drivers/watchdog/rdc321x_wdt.c
@@ -144,8 +144,8 @@ static int rdc321x_wdt_release(struct inode *inode, struct file *file)
return 0;
}
-static int rdc321x_wdt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
void __user *argp = (void __user *)arg;
unsigned int value;
@@ -204,7 +204,7 @@ static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
static const struct file_operations rdc321x_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .ioctl = rdc321x_wdt_ioctl,
+ .unlocked_ioctl = rdc321x_wdt_ioctl,
.open = rdc321x_wdt_open,
.write = rdc321x_wdt_write,
.release = rdc321x_wdt_release,
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
new file mode 100644
index 00000000000..09cb1833ea2
--- /dev/null
+++ b/drivers/watchdog/riowd.c
@@ -0,0 +1,259 @@
+/* riowd.c - driver for hw watchdog inside Super I/O of RIO
+ *
+ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/smp_lock.h>
+#include <linux/watchdog.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+/* RIO uses the NatSemi Super I/O power management logical device
+ * as its' watchdog.
+ *
+ * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
+ * Controller) of the machine. The BBC can only be configured to
+ * trigger a power-on reset when the signal is asserted. The BBC
+ * can be configured to ignore the signal entirely as well.
+ *
+ * The only Super I/O device register we care about is at index
+ * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
+ * If set to zero, this disables the watchdog. When set, the system
+ * must periodically (before watchdog expires) clear (set to zero) and
+ * re-set the watchdog else it will trigger.
+ *
+ * There are two other indexed watchdog registers inside this Super I/O
+ * logical device, but they are unused. The first, at index 0x06 is
+ * the watchdog control and can be used to make the watchdog timer re-set
+ * when the PS/2 mouse or serial lines show activity. The second, at
+ * index 0x07 is merely a sampling of the line from the watchdog to the
+ * BBC.
+ *
+ * The watchdog device generates no interrupts.
+ */
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
+MODULE_SUPPORTED_DEVICE("watchdog");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_NAME "riowd"
+#define PFX DRIVER_NAME ": "
+
+struct riowd {
+ void __iomem *regs;
+ spinlock_t lock;
+};
+
+static struct riowd *riowd_device;
+
+#define WDTO_INDEX 0x05
+
+static int riowd_timeout = 1; /* in minutes */
+module_param(riowd_timeout, int, 0);
+MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
+
+static void riowd_writereg(struct riowd *p, u8 val, int index)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ writeb(index, p->regs + 0);
+ writeb(val, p->regs + 1);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int riowd_open(struct inode *inode, struct file *filp)
+{
+ cycle_kernel_lock();
+ nonseekable_open(inode, filp);
+ return 0;
+}
+
+static int riowd_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int riowd_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ struct riowd *p = riowd_device;
+ unsigned int options;
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&options, argp, sizeof(options)))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ riowd_writereg(p, 0, WDTO_INDEX);
+ else if (options & WDIOS_ENABLECARD)
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ else
+ return -EINVAL;
+
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)argp))
+ return -EFAULT;
+ if ((new_margin < 60) || (new_margin > (255 * 60)))
+ return -EINVAL;
+ riowd_timeout = (new_margin + 59) / 60;
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(riowd_timeout * 60, (int __user *)argp);
+
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct riowd *p = riowd_device;
+
+ if (count) {
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct file_operations riowd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = riowd_ioctl,
+ .open = riowd_open,
+ .write = riowd_write,
+ .release = riowd_release,
+};
+
+static struct miscdevice riowd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &riowd_fops
+};
+
+static int __devinit riowd_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct riowd *p;
+ int err = -EINVAL;
+
+ if (riowd_device)
+ goto out;
+
+ err = -ENOMEM;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Cannot map registers.\n");
+ goto out_free;
+ }
+
+ err = misc_register(&riowd_miscdev);
+ if (err) {
+ printk(KERN_ERR PFX "Cannot register watchdog misc device.\n");
+ goto out_iounmap;
+ }
+
+ printk(KERN_INFO PFX "Hardware watchdog [%i minutes], "
+ "regs at %p\n", riowd_timeout, p->regs);
+
+ dev_set_drvdata(&op->dev, p);
+ riowd_device = p;
+ err = 0;
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, 2);
+
+out_free:
+ kfree(p);
+
+out:
+ return err;
+}
+
+static int __devexit riowd_remove(struct of_device *op)
+{
+ struct riowd *p = dev_get_drvdata(&op->dev);
+
+ misc_deregister(&riowd_miscdev);
+ of_iounmap(&op->resource[0], p->regs, 2);
+ kfree(p);
+
+ return 0;
+}
+
+static const struct of_device_id riowd_match[] = {
+ {
+ .name = "pmc",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, riowd_match);
+
+static struct of_platform_driver riowd_driver = {
+ .name = DRIVER_NAME,
+ .match_table = riowd_match,
+ .probe = riowd_probe,
+ .remove = __devexit_p(riowd_remove),
+};
+
+static int __init riowd_init(void)
+{
+ return of_register_driver(&riowd_driver, &of_bus_type);
+}
+
+static void __exit riowd_exit(void)
+{
+ of_unregister_driver(&riowd_driver);
+}
+
+module_init(riowd_init);
+module_exit(riowd_exit);
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
new file mode 100644
index 00000000000..c73b5e2919c
--- /dev/null
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -0,0 +1,392 @@
+/*
+ * w83697ug/uf WDT driver
+ *
+ * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
+ * reused original code to supoprt w83697ug/uf.
+ *
+ * Based on w83627hf_wdt.c which is based on advantechwdt.c
+ * which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
+ * added support for W83627THF.
+ *
+ * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ * http://www.redhat.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83697ug/uf WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(io_lock);
+
+static int wdt_io = 0x2e;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=255 (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Kernel methods.
+ */
+
+#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
+#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
+ (same as EFER) */
+#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
+
+static void w83697ug_select_wd_register(void)
+{
+ unsigned char c;
+ unsigned char version;
+
+ outb_p(0x87, WDT_EFER); /* Enter extended function mode */
+ outb_p(0x87, WDT_EFER); /* Again according to manual */
+
+ outb(0x20, WDT_EFER); /* check chip version */
+ version = inb(WDT_EFDR);
+
+ if (version == 0x68) { /* W83697UG */
+ printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
+ "W83697UG/UF found at 0x%04x\n", version, wdt_io);
+
+ outb_p(0x2b, WDT_EFER);
+ c = inb_p(WDT_EFDR); /* select WDT0 */
+ c &= ~0x04;
+ outb_p(0x2b, WDT_EFER);
+ outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
+
+ } else {
+ printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
+ return -EIO;
+ }
+
+ outb_p(0x07, WDT_EFER); /* point to logical device number reg */
+ outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
+ outb_p(0x30, WDT_EFER); /* select CR30 */
+ c = inb_p(WDT_EFDR);
+ outb_p(c || 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+}
+
+static void w83697ug_unselect_wd_register(void)
+{
+ outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+}
+
+static void w83697ug_init(void)
+{
+ unsigned char t;
+
+ w83697ug_select_wd_register();
+
+ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
+ t = inb_p(WDT_EFDR); /* read CRF6 */
+ if (t != 0) {
+ printk(KERN_INFO PFX "Watchdog already running."
+ " Resetting timeout to %d sec\n", timeout);
+ outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
+ }
+ outb_p(0xF5, WDT_EFER); /* Select CRF5 */
+ t = inb_p(WDT_EFDR); /* read CRF5 */
+ t &= ~0x0C; /* set second mode &
+ disable keyboard turning off watchdog */
+ outb_p(t, WDT_EFDR); /* Write back to CRF5 */
+
+ w83697ug_unselect_wd_register();
+}
+
+static void wdt_ctrl(int timeout)
+{
+ spin_lock(&io_lock);
+
+ w83697ug_select_wd_register();
+
+ outb_p(0xF4, WDT_EFER); /* Select CRF4 */
+ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
+
+ w83697ug_unselect_wd_register();
+
+ spin_unlock(&io_lock);
+}
+
+static int wdt_ping(void)
+{
+ wdt_ctrl(timeout);
+ return 0;
+}
+
+static int wdt_disable(void)
+{
+ wdt_ctrl(0);
+ return 0;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 255)
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+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;
+ }
+
+ w83697ug_init();
+
+ 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);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
+MODULE_DESCRIPTION("w83697ug/uf WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c
index db362c34958..191ea630210 100644
--- a/drivers/watchdog/wdt285.c
+++ b/drivers/watchdog/wdt285.c
@@ -115,8 +115,8 @@ static int watchdog_release(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t watchdog_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
+static ssize_t watchdog_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
{
/*
* Refresh the timer.
@@ -133,21 +133,22 @@ static const struct watchdog_info ident = {
};
static long watchdog_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg)
{
unsigned int new_margin;
+ int __user *int_arg = (int __user *)arg;
int ret = -ENOTTY;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = 0;
- if (copy_to_user((void *)arg, &ident, sizeof(ident)))
+ if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
ret = -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
+ ret = put_user(0, int_arg);
break;
case WDIOC_KEEPALIVE:
@@ -156,7 +157,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
break;
case WDIOC_SETTIMEOUT:
- ret = get_user(new_margin, (int *)arg);
+ ret = get_user(new_margin, int_arg);
if (ret)
break;
@@ -171,7 +172,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
watchdog_ping();
/* Fall */
case WDIOC_GETTIMEOUT:
- ret = put_user(soft_margin, (int *)arg);
+ ret = put_user(soft_margin, int_arg);
break;
}
return ret;