aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-09-18 09:47:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-18 09:47:25 -0700
commitafa12e72decb84cb8378ae2e66e51336e3c66962 (patch)
tree96f23729e440d60740819b55ae320092a8f284db /drivers
parent81ce31b773226332475f89501b1072bec0c0e241 (diff)
parente04ab958727a4b314df3e40036d72d9348835d0c (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog: [WATCHDOG] sizeof cleanup [WATCHDOG] wdt_pci: fix printk and variable type [WATCHDOG] wdt_pci - use pci_request_region [WATCHDOG] ar7_wdt: Fix error handling during probe. [WATCHDOG] ar7_wdt: convert to become a platform driver [WATCHDOG] fix book E watchdog to take WDIOC_SETTIMEOUT arg in seconds [WATCHDOG] davinci: use clock framework for timer frequency [WATCHDOG] Use DIV_ROUND_UP() macro in the coh901327 WDT [WATCHDOG] Add support for WM831x watchdog [WATCHDOG] Add watchdog driver for NUC900 [WATCHDOG] add SBC-FITPC2 watchdog driver
Diffstat (limited to 'drivers')
-rw-r--r--drivers/watchdog/Kconfig38
-rw-r--r--drivers/watchdog/Makefile3
-rw-r--r--drivers/watchdog/ar7_wdt.c107
-rw-r--r--drivers/watchdog/booke_wdt.c57
-rw-r--r--drivers/watchdog/coh901327_wdt.c2
-rw-r--r--drivers/watchdog/davinci_wdt.c19
-rw-r--r--drivers/watchdog/iop_wdt.c2
-rw-r--r--drivers/watchdog/nuc900_wdt.c353
-rw-r--r--drivers/watchdog/rm9k_wdt.c2
-rw-r--r--drivers/watchdog/sbc_fitpc2_wdt.c267
-rw-r--r--drivers/watchdog/sc1200wdt.c2
-rw-r--r--drivers/watchdog/wdt_pci.c21
-rw-r--r--drivers/watchdog/wm831x_wdt.c441
13 files changed, 1241 insertions, 73 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b1ccc04f3c9..ff3eb8ff6bd 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -55,6 +55,13 @@ config SOFT_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called softdog.
+config WM831X_WATCHDOG
+ tristate "WM831x watchdog"
+ depends on MFD_WM831X
+ help
+ Support for the watchdog in the WM831x AudioPlus PMICs. When
+ the watchdog triggers the system will be reset.
+
config WM8350_WATCHDOG
tristate "WM8350 watchdog"
depends on MFD_WM8350
@@ -266,6 +273,15 @@ config STMP3XXX_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called stmp3xxx_wdt.
+config NUC900_WATCHDOG
+ tristate "Nuvoton NUC900 watchdog"
+ depends on ARCH_W90X900
+ help
+ Say Y here if to include support for the watchdog timer
+ for the Nuvoton NUC900 series SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called nuc900_wdt.
+
# AVR32 Architecture
config AT32AP700X_WDT
@@ -369,6 +385,28 @@ config SC520_WDT
You can compile this driver directly into the kernel, or use
it as a module. The module will be called sc520_wdt.
+config SBC_FITPC2_WATCHDOG
+ tristate "Compulab SBC-FITPC2 watchdog"
+ depends on X86
+ ---help---
+ This is the driver for the built-in watchdog timer on the fit-PC2
+ Single-board computer made by Compulab.
+
+ It`s possible to enable watchdog timer either from BIOS (F2) or from booted Linux.
+ When "Watchdog Timer Value" enabled one can set 31-255 s operational range.
+
+ Entering BIOS setup temporary disables watchdog operation regardless to current state,
+ so system will not be restarted while user in BIOS setup.
+
+ Once watchdog was enabled the system will be restarted every
+ "Watchdog Timer Value" period, so to prevent it user can restart or
+ disable the watchdog.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sbc_fitpc2_wdt.
+
+ Most people will say N.
+
config EUROTECH_WDT
tristate "Eurotech CPU-1220/1410 Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 3d774294a2b..348b3b862c9 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
+obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -64,6 +65,7 @@ obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
+obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
obj-$(CONFIG_IB700_WDT) += ib700wdt.o
obj-$(CONFIG_IBMASR) += ibmasr.o
@@ -139,5 +141,6 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
# XTENSA Architecture
# Architecture Independant
+obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 2f8643efe92..2e94b71b20d 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -28,9 +28,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
#include <linux/watchdog.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/io.h>
@@ -76,24 +75,10 @@ static unsigned expect_close;
/* XXX currently fixed, allows max margin ~68.72 secs */
#define prescale_value 0xffff
-/* Offset of the WDT registers */
-static unsigned long ar7_regs_wdt;
+/* Resource of the WDT registers */
+static struct resource *ar7_regs_wdt;
/* Pointer to the remapped WDT IO space */
static struct ar7_wdt *ar7_wdt;
-static void ar7_wdt_get_regs(void)
-{
- u16 chip_id = ar7_chip_id();
- switch (chip_id) {
- case AR7_CHIP_7100:
- case AR7_CHIP_7200:
- ar7_regs_wdt = AR7_REGS_WDT;
- break;
- default:
- ar7_regs_wdt = UR8_REGS_WDT;
- break;
- }
-}
-
static void ar7_wdt_kick(u32 value)
{
@@ -202,20 +187,6 @@ static int ar7_wdt_release(struct inode *inode, struct file *file)
return 0;
}
-static int ar7_wdt_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
-{
- if (code == SYS_HALT || code == SYS_POWER_OFF)
- if (!nowayout)
- ar7_wdt_disable_wdt();
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block ar7_wdt_notifier = {
- .notifier_call = ar7_wdt_notify_sys,
-};
-
static ssize_t ar7_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
@@ -299,56 +270,86 @@ static struct miscdevice ar7_wdt_miscdev = {
.fops = &ar7_wdt_fops,
};
-static int __init ar7_wdt_init(void)
+static int __devinit ar7_wdt_probe(struct platform_device *pdev)
{
int rc;
spin_lock_init(&wdt_lock);
- ar7_wdt_get_regs();
+ ar7_regs_wdt =
+ platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!ar7_regs_wdt) {
+ printk(KERN_ERR DRVNAME ": could not get registers resource\n");
+ rc = -ENODEV;
+ goto out;
+ }
- if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt),
- LONGNAME)) {
+ if (!request_mem_region(ar7_regs_wdt->start,
+ resource_size(ar7_regs_wdt), LONGNAME)) {
printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
- return -EBUSY;
+ rc = -EBUSY;
+ goto out;
}
- ar7_wdt = (struct ar7_wdt *)
- ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt));
+ ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+ if (!ar7_wdt) {
+ printk(KERN_ERR DRVNAME ": could not ioremap registers\n");
+ rc = -ENXIO;
+ goto out_mem_region;
+ }
ar7_wdt_disable_wdt();
ar7_wdt_prescale(prescale_value);
ar7_wdt_update_margin(margin);
- rc = register_reboot_notifier(&ar7_wdt_notifier);
- if (rc) {
- printk(KERN_ERR DRVNAME
- ": unable to register reboot notifier\n");
- goto out_alloc;
- }
-
rc = misc_register(&ar7_wdt_miscdev);
if (rc) {
printk(KERN_ERR DRVNAME ": unable to register misc device\n");
- goto out_register;
+ goto out_alloc;
}
goto out;
-out_register:
- unregister_reboot_notifier(&ar7_wdt_notifier);
out_alloc:
iounmap(ar7_wdt);
- release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+out_mem_region:
+ release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
out:
return rc;
}
-static void __exit ar7_wdt_cleanup(void)
+static int __devexit ar7_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&ar7_wdt_miscdev);
- unregister_reboot_notifier(&ar7_wdt_notifier);
iounmap(ar7_wdt);
- release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
+ release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+
+ return 0;
+}
+
+static void ar7_wdt_shutdown(struct platform_device *pdev)
+{
+ if (!nowayout)
+ ar7_wdt_disable_wdt();
+}
+
+static struct platform_driver ar7_wdt_driver = {
+ .probe = ar7_wdt_probe,
+ .remove = __devexit_p(ar7_wdt_remove),
+ .shutdown = ar7_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ar7_wdt",
+ },
+};
+
+static int __init ar7_wdt_init(void)
+{
+ return platform_driver_register(&ar7_wdt_driver);
+}
+
+static void __exit ar7_wdt_cleanup(void)
+{
+ platform_driver_unregister(&ar7_wdt_driver);
}
module_init(ar7_wdt_init);
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 225398fd504..e8380ef65c1 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -22,6 +22,8 @@
#include <asm/reg_booke.h>
#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/div64.h>
/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
* Also, the wdt_period sets the watchdog timer period timeout.
@@ -32,7 +34,7 @@
*/
#ifdef CONFIG_FSL_BOOKE
-#define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
+#define WDT_PERIOD_DEFAULT 38 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
#else
#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */
#endif /* for timing information */
@@ -41,7 +43,7 @@ u32 booke_wdt_enabled;
u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
#ifdef CONFIG_FSL_BOOKE
-#define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15))
+#define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
#define WDTP_MASK (WDTP(0))
#else
#define WDTP(x) (TCR_WP(x))
@@ -50,6 +52,45 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
static DEFINE_SPINLOCK(booke_wdt_lock);
+/* For the specified period, determine the number of seconds
+ * corresponding to the reset time. There will be a watchdog
+ * exception at approximately 3/5 of this time.
+ *
+ * The formula to calculate this is given by:
+ * 2.5 * (2^(63-period+1)) / timebase_freq
+ *
+ * In order to simplify things, we assume that period is
+ * at least 1. This will still result in a very long timeout.
+ */
+static unsigned long long period_to_sec(unsigned int period)
+{
+ unsigned long long tmp = 1ULL << (64 - period);
+ unsigned long tmp2 = ppc_tb_freq;
+
+ /* tmp may be a very large number and we don't want to overflow,
+ * so divide the timebase freq instead of multiplying tmp
+ */
+ tmp2 = tmp2 / 5 * 2;
+
+ do_div(tmp, tmp2);
+ return tmp;
+}
+
+/*
+ * This procedure will find the highest period which will give a timeout
+ * greater than the one required. e.g. for a bus speed of 66666666 and
+ * and a parameter of 2 secs, then this procedure will return a value of 38.
+ */
+static unsigned int sec_to_period(unsigned int secs)
+{
+ unsigned int period;
+ for (period = 63; period > 0; period--) {
+ if (period_to_sec(period) >= secs)
+ return period;
+ }
+ return 0;
+}
+
static void __booke_wdt_ping(void *data)
{
mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
@@ -93,7 +134,7 @@ static long booke_wdt_ioctl(struct file *file,
switch (cmd) {
case WDIOC_GETSUPPORT:
- if (copy_to_user(arg, &ident, sizeof(struct watchdog_info)))
+ if (copy_to_user((void *)arg, &ident, sizeof(ident)))
return -EFAULT;
case WDIOC_GETSTATUS:
return put_user(ident.options, p);
@@ -115,8 +156,16 @@ static long booke_wdt_ioctl(struct file *file,
booke_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
- if (get_user(booke_wdt_period, p))
+ if (get_user(tmp, p))
return -EFAULT;
+#ifdef CONFIG_FSL_BOOKE
+ /* period of 1 gives the largest possible timeout */
+ if (tmp > period_to_sec(1))
+ return -EINVAL;
+ booke_wdt_period = sec_to_period(tmp);
+#else
+ booke_wdt_period = tmp;
+#endif
mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
WDTP(booke_wdt_period));
return 0;
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index aec7cefdef2..381026c0bd7 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -110,7 +110,7 @@ static void coh901327_enable(u16 timeout)
* Wait 3 32 kHz cycles for it to take effect
*/
freq = clk_get_rate(clk);
- delay_ns = (1000000000 + freq - 1) / freq; /* Freq to ns and round up */
+ delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */
delay_ns = 3 * delay_ns; /* Wait 3 cycles */
ndelay(delay_ns);
/* Enable the watchdog interrupt */
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index 83e22e7ea4a..9d7520fa9e9 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -25,6 +25,7 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
+#include <linux/clk.h>
#define MODULE_NAME "DAVINCI-WDT: "
@@ -69,6 +70,7 @@ static unsigned long wdt_status;
static struct resource *wdt_mem;
static void __iomem *wdt_base;
+struct clk *wdt_clk;
static void wdt_service(void)
{
@@ -86,6 +88,9 @@ static void wdt_enable(void)
{
u32 tgcr;
u32 timer_margin;
+ unsigned long wdt_freq;
+
+ wdt_freq = clk_get_rate(wdt_clk);
spin_lock(&io_lock);
@@ -99,9 +104,9 @@ static void wdt_enable(void)
iowrite32(0, wdt_base + TIM12);
iowrite32(0, wdt_base + TIM34);
/* set timeout period */
- timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff);
+ timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
iowrite32(timer_margin, wdt_base + PRD12);
- timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32);
+ timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
iowrite32(timer_margin, wdt_base + PRD34);
/* enable run continuously */
iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
@@ -199,6 +204,12 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev)
struct resource *res;
struct device *dev = &pdev->dev;
+ wdt_clk = clk_get(dev, NULL);
+ if (WARN_ON(IS_ERR(wdt_clk)))
+ return PTR_ERR(wdt_clk);
+
+ clk_enable(wdt_clk);
+
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
@@ -245,6 +256,10 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev)
kfree(wdt_mem);
wdt_mem = NULL;
}
+
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+
return 0;
}
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
index 0c905967669..aef94789019 100644
--- a/drivers/watchdog/iop_wdt.c
+++ b/drivers/watchdog/iop_wdt.c
@@ -139,7 +139,7 @@ static long iop_wdt_ioctl(struct file *file,
switch (cmd) {
case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof ident))
+ if (copy_to_user(argp, &ident, sizeof(ident)))
ret = -EFAULT;
else
ret = 0;
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
new file mode 100644
index 00000000000..adefe3a9d51
--- /dev/null
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#define REG_WTCR 0x1c
+#define WTCLK (0x01 << 10)
+#define WTE (0x01 << 7) /*wdt enable*/
+#define WTIS (0x03 << 4)
+#define WTIF (0x01 << 3)
+#define WTRF (0x01 << 2)
+#define WTRE (0x01 << 1)
+#define WTR (0x01 << 0)
+/*
+ * The watchdog time interval can be calculated via following formula:
+ * WTIS real time interval (formula)
+ * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
+ * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
+ * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
+ * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
+ *
+ * The external crystal freq is 15Mhz in the nuc900 evaluation board.
+ * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
+ * 0x03 = +- 16.92 seconds..
+ */
+#define WDT_HW_TIMEOUT 0x02
+#define WDT_TIMEOUT (HZ/2)
+#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) ")");
+
+struct nuc900_wdt {
+ struct resource *res;
+ struct clk *wdt_clock;
+ struct platform_device *pdev;
+ void __iomem *wdt_base;
+ char expect_close;
+ struct timer_list timer;
+ spinlock_t wdt_lock;
+ unsigned long next_heartbeat;
+};
+
+static unsigned long nuc900wdt_busy;
+struct nuc900_wdt *nuc900_wdt;
+
+static inline void nuc900_wdt_keepalive(void)
+{
+ unsigned int val;
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val |= (WTR | WTIF);
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+}
+
+static inline void nuc900_wdt_start(void)
+{
+ unsigned int val;
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val |= (WTRE | WTE | WTR | WTCLK | WTIF);
+ val &= ~WTIS;
+ val |= (WDT_HW_TIMEOUT << 0x04);
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+
+ nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
+ mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
+}
+
+static inline void nuc900_wdt_stop(void)
+{
+ unsigned int val;
+
+ del_timer(&nuc900_wdt->timer);
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val &= ~WTE;
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+}
+
+static inline void nuc900_wdt_ping(void)
+{
+ nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
+}
+
+static int nuc900_wdt_open(struct inode *inode, struct file *file)
+{
+
+ if (test_and_set_bit(0, &nuc900wdt_busy))
+ return -EBUSY;
+
+ nuc900_wdt_start();
+
+ return nonseekable_open(inode, file);
+}
+
+static int nuc900_wdt_close(struct inode *inode, struct file *file)
+{
+ if (nuc900_wdt->expect_close == 42)
+ nuc900_wdt_stop();
+ else {
+ dev_crit(&nuc900_wdt->pdev->dev,
+ "Unexpected close, not stopping watchdog!\n");
+ nuc900_wdt_ping();
+ }
+
+ nuc900_wdt->expect_close = 0;
+ clear_bit(0, &nuc900wdt_busy);
+ return 0;
+}
+
+static const struct watchdog_info nuc900_wdt_info = {
+ .identity = "nuc900 watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static long nuc900_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, &nuc900_wdt_info,
+ sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ nuc900_wdt_ping();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ nuc900_wdt_ping();
+
+ return put_user(new_value, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ nuc900_wdt->expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ nuc900_wdt->expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ nuc900_wdt_ping();
+ return len;
+}
+
+static void nuc900_wdt_timer_ping(unsigned long data)
+{
+ if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
+ nuc900_wdt_keepalive();
+ mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
+ } else
+ dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
+}
+
+static const struct file_operations nuc900wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = nuc900_wdt_ioctl,
+ .open = nuc900_wdt_open,
+ .release = nuc900_wdt_close,
+ .write = nuc900_wdt_write,
+};
+
+static struct miscdevice nuc900wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &nuc900wdt_fops,
+};
+
+static int __devinit nuc900wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
+ if (!nuc900_wdt)
+ return -ENOMEM;
+
+ nuc900_wdt->pdev = pdev;
+
+ spin_lock_init(&nuc900_wdt->wdt_lock);
+
+ nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (nuc900_wdt->res == NULL) {
+ dev_err(&pdev->dev, "no memory resource specified\n");
+ ret = -ENOENT;
+ goto err_get;
+ }
+
+ if (!request_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto err_get;
+ }
+
+ nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+ if (nuc900_wdt->wdt_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap() region\n");
+ ret = -EINVAL;
+ goto err_req;
+ }
+
+ nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(nuc900_wdt->wdt_clock)) {
+ dev_err(&pdev->dev, "failed to find watchdog clock source\n");
+ ret = PTR_ERR(nuc900_wdt->wdt_clock);
+ goto err_map;
+ }
+
+ clk_enable(nuc900_wdt->wdt_clock);
+
+ setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
+
+ if (misc_register(&nuc900wdt_miscdev)) {
+ dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ clk_disable(nuc900_wdt->wdt_clock);
+ clk_put(nuc900_wdt->wdt_clock);
+err_map:
+ iounmap(nuc900_wdt->wdt_base);
+err_req:
+ release_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+err_get:
+ kfree(nuc900_wdt);
+ return ret;
+}
+
+static int __devexit nuc900wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&nuc900wdt_miscdev);
+
+ clk_disable(nuc900_wdt->wdt_clock);
+ clk_put(nuc900_wdt->wdt_clock);
+
+ iounmap(nuc900_wdt->wdt_base);
+
+ release_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+
+ kfree(nuc900_wdt);
+
+ return 0;
+}
+
+static struct platform_driver nuc900wdt_driver = {
+ .probe = nuc900wdt_probe,
+ .remove = __devexit_p(nuc900wdt_remove),
+ .driver = {
+ .name = "nuc900-wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init nuc900_wdt_init(void)
+{
+ return platform_driver_register(&nuc900wdt_driver);
+}
+
+static void __exit nuc900_wdt_exit(void)
+{
+ platform_driver_unregister(&nuc900wdt_driver);
+}
+
+module_init(nuc900_wdt_init);
+module_exit(nuc900_wdt_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("Watchdog driver for NUC900");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:nuc900-wdt");
diff --git a/drivers/watchdog/rm9k_wdt.c b/drivers/watchdog/rm9k_wdt.c
index 2e444264226..bb66958b943 100644
--- a/drivers/watchdog/rm9k_wdt.c
+++ b/drivers/watchdog/rm9k_wdt.c
@@ -340,7 +340,7 @@ static const struct resource *wdt_gpi_get_resource(struct platform_device *pdv,
const char *name, unsigned int type)
{
char buf[80];
- if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf)
+ if (snprintf(buf, sizeof(buf), "%s_0", name) >= sizeof(buf))
return NULL;
return platform_get_resource_byname(pdv, type, buf);
}
diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c
new file mode 100644
index 00000000000..852ca197791
--- /dev/null
+++ b/drivers/watchdog/sbc_fitpc2_wdt.c
@@ -0,0 +1,267 @@
+/*
+ * Watchdog driver for SBC-FITPC2 board
+ *
+ * Author: Denis Turischev <denis@compulab.co.il>
+ *
+ * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int margin = 60; /* (secs) Default is 1 minute */
+static unsigned long wdt_status;
+static DEFINE_SPINLOCK(wdt_lock);
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+#define COMMAND_PORT 0x4c
+#define DATA_PORT 0x48
+
+#define IFACE_ON_COMMAND 1
+#define REBOOT_COMMAND 2
+
+#define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
+
+static void wdt_send_data(unsigned char command, unsigned char data)
+{
+ outb(command, COMMAND_PORT);
+ mdelay(100);
+ outb(data, DATA_PORT);
+ mdelay(200);
+}
+
+static void wdt_enable(void)
+{
+ spin_lock(&wdt_lock);
+ wdt_send_data(IFACE_ON_COMMAND, 1);
+ wdt_send_data(REBOOT_COMMAND, margin);
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&wdt_lock);
+ wdt_send_data(IFACE_ON_COMMAND, 0);
+ wdt_send_data(REBOOT_COMMAND, 0);
+ spin_unlock(&wdt_lock);
+}
+
+static int fitpc2_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 fitpc2_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ size_t i;
+
+ if (!len)
+ return 0;
+
+ if (nowayout) {
+ len = 0;
+ goto out;
+ }
+
+ 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);
+ }
+
+out:
+ wdt_enable();
+
+ return len;
+}
+
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = WATCHDOG_NAME,
+};
+
+
+static long fitpc2_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:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ 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 < 31 || time > 255) {
+ ret = -EINVAL;
+ break;
+ }
+
+ margin = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(margin, (int *)arg);
+ break;
+ }
+
+ return ret;
+}
+
+static int fitpc2_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
+ wdt_disable();
+ pr_info("Device disabled\n");
+ } else {
+ pr_warning("Device closed unexpectedly -"
+ " timer will not stop\n");
+ wdt_enable();
+ }
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations fitpc2_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fitpc2_wdt_write,
+ .unlocked_ioctl = fitpc2_wdt_ioctl,
+ .open = fitpc2_wdt_open,
+ .release = fitpc2_wdt_release,
+};
+
+static struct miscdevice fitpc2_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &fitpc2_wdt_fops,
+};
+
+static int __init fitpc2_wdt_init(void)
+{
+ int err;
+
+ if (strcmp("SBC-FITPC2", dmi_get_system_info(DMI_BOARD_NAME))) {
+ pr_info("board name is: %s. Should be SBC-FITPC2\n",
+ dmi_get_system_info(DMI_BOARD_NAME));
+ return -ENODEV;
+ }
+
+ if (!request_region(COMMAND_PORT, 1, WATCHDOG_NAME)) {
+ pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT);
+ return -EIO;
+ }
+
+ if (!request_region(DATA_PORT, 1, WATCHDOG_NAME)) {
+ pr_err("I/O address 0x%04x already in use\n", DATA_PORT);
+ err = -EIO;
+ goto err_data_port;
+ }
+
+ if (margin < 31 || margin > 255) {
+ pr_err("margin must be in range 31 - 255"
+ " seconds, you tried to set %d\n", margin);
+ err = -EINVAL;
+ goto err_margin;
+ }
+
+ err = misc_register(&fitpc2_wdt_miscdev);
+ if (!err) {
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
+ goto err_margin;
+ }
+
+ return 0;
+
+err_margin:
+ release_region(DATA_PORT, 1);
+err_data_port:
+ release_region(COMMAND_PORT, 1);
+
+ return err;
+}
+
+static void __exit fitpc2_wdt_exit(void)
+{
+ misc_deregister(&fitpc2_wdt_miscdev);
+ release_region(DATA_PORT, 1);
+ release_region(COMMAND_PORT, 1);
+}
+
+module_init(fitpc2_wdt_init);
+module_exit(fitpc2_wdt_exit);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
+
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
+
+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/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index b5e19c1820a..c01daca8405 100644
--- a/drivers/watchdog/sc1200wdt.c
+++ b/drivers/watchdog/sc1200wdt.c
@@ -197,7 +197,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof ident))
+ if (copy_to_user(argp, &ident, sizeof(ident)))
return -EFAULT;
return 0;
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index 7a1bdc7c95a..f368dd87083 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -80,7 +80,7 @@ static unsigned long open_lock;
static DEFINE_SPINLOCK(wdtpci_lock);
static char expect_close;
-static int io;
+static resource_size_t io;
static int irq;
/* Default timeout */
@@ -647,14 +647,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
goto out_pci;
}
- irq = dev->irq;
- io = pci_resource_start(dev, 2);
-
- if (request_region(io, 16, "wdt_pci") == NULL) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", io);
+ if (pci_request_region(dev, 2, "wdt_pci")) {
+ printk(KERN_ERR PFX "I/O address 0x%llx already in use\n",
+ (unsigned long long)pci_resource_start(dev, 2));
goto out_pci;
}
+ irq = dev->irq;
+ io = pci_resource_start(dev, 2);
+
if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED,
"wdt_pci", &wdtpci_miscdev)) {
printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
@@ -662,8 +663,8 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
}
printk(KERN_INFO
- "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n",
- io, irq);
+ "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
+ (unsigned long long)io, irq);
/* Check that the heartbeat value is within its range;
if not reset to the default */
@@ -717,7 +718,7 @@ out_rbt:
out_irq:
free_irq(irq, &wdtpci_miscdev);
out_reg:
- release_region(io, 16);
+ pci_release_region(dev, 2);
out_pci:
pci_disable_device(dev);
goto out;
@@ -733,7 +734,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
misc_deregister(&temp_miscdev);
unregister_reboot_notifier(&wdtpci_notifier);
free_irq(irq, &wdtpci_miscdev);
- release_region(io, 16);
+ pci_release_region(pdev, 2);
pci_disable_device(pdev);
dev_count--;
}
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
new file mode 100644
index 00000000000..775bcd807f3
--- /dev/null
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -0,0 +1,441 @@
+/*
+ * Watchdog driver for the wm831x PMICs
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics
+ *
+ * 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
+ */
+
+#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/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/watchdog.h>
+
+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 unsigned long wm831x_wdt_users;
+static struct miscdevice wm831x_wdt_miscdev;
+static int wm831x_wdt_expect_close;
+static DEFINE_MUTEX(wdt_mutex);
+static struct wm831x *wm831x;
+static unsigned int update_gpio;
+static unsigned int update_state;
+
+/* We can't use the sub-second values here but they're included
+ * for completeness. */
+static struct {
+ int time; /* Seconds */
+ u16 val; /* WDOG_TO value */
+} wm831x_wdt_cfgs[] = {
+ { 1, 2 },
+ { 2, 3 },
+ { 4, 4 },
+ { 8, 5 },
+ { 16, 6 },
+ { 32, 7 },
+ { 33, 7 }, /* Actually 32.768s so include both, others round down */
+};
+
+static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_TO_MASK, value);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_start(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_ENA, WM831X_WDOG_ENA);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_stop(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_ENA, 0);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_kick(struct wm831x *wm831x)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+
+ if (update_gpio) {
+ gpio_set_value_cansleep(update_gpio, update_state);
+ update_state = !update_state;
+ ret = 0;
+ goto out;
+ }
+
+
+ reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+
+ if (!(reg & WM831X_WDOG_RST_SRC)) {
+ dev_err(wm831x->dev, "Hardware watchdog update unsupported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reg |= WM831X_WDOG_RESET;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+out:
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ if (!wm831x)
+ return -ENODEV;
+
+ if (test_and_set_bit(0, &wm831x_wdt_users))
+ return -EBUSY;
+
+ ret = wm831x_wdt_start(wm831x);
+ if (ret != 0)
+ return ret;
+
+ return nonseekable_open(inode, file);
+}
+
+static int wm831x_wdt_release(struct inode *inode, struct file *file)
+{
+ if (wm831x_wdt_expect_close)
+ wm831x_wdt_stop(wm831x);
+ else {
+ dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n");
+ wm831x_wdt_kick(wm831x);
+ }
+
+ clear_bit(0, &wm831x_wdt_users);
+
+ return 0;
+}
+
+static ssize_t wm831x_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ size_t i;
+
+ if (count) {
+ wm831x_wdt_kick(wm831x);
+
+ if (!nowayout) {
+ /* In case it was set long ago */
+ wm831x_wdt_expect_close = 0;
+
+ /* scan to see whether or not we got the magic
+ character */
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wm831x_wdt_expect_close = 42;
+ }
+ }
+ }
+ return count;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "WM831x Watchdog",
+};
+
+static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY, time, i;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ u16 reg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ ret = -EINVAL;
+
+ /* Setting both simultaneously means at least one must fail */
+ if (options == WDIOS_DISABLECARD)
+ ret = wm831x_wdt_start(wm831x);
+
+ if (options == WDIOS_ENABLECARD)
+ ret = wm831x_wdt_stop(wm831x);
+ break;
+ }
+
+ case WDIOC_KEEPALIVE:
+ ret = wm831x_wdt_kick(wm831x);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ if (time == 0) {
+ if (nowayout)
+ ret = -EINVAL;
+ else
+ wm831x_wdt_stop(wm831x);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
+ if (wm831x_wdt_cfgs[i].time == time)
+ break;
+ if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
+ ret = -EINVAL;
+ else
+ ret = wm831x_wdt_set_timeout(wm831x,
+ wm831x_wdt_cfgs[i].val);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+ reg &= WM831X_WDOG_TO_MASK;
+ for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
+ if (wm831x_wdt_cfgs[i].val == reg)
+ break;
+ if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) {
+ dev_warn(wm831x->dev,
+ "Unknown watchdog configuration: %x\n", reg);
+ ret = -EINVAL;
+ } else
+ ret = put_user(wm831x_wdt_cfgs[i].time, p);
+
+ }
+
+ return ret;
+}
+
+static const struct file_operations wm831x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wm831x_wdt_write,
+ .unlocked_ioctl = wm831x_wdt_ioctl,
+ .open = wm831x_wdt_open,
+ .release = wm831x_wdt_release,
+};
+
+static struct miscdevice wm831x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wm831x_wdt_fops,
+};
+
+static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
+{
+ struct wm831x_pdata *chip_pdata;
+ struct wm831x_watchdog_pdata *pdata;
+ int reg, ret;
+
+ wm831x = dev_get_drvdata(pdev->dev.parent);
+
+ ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
+ ret);
+ goto err;
+ }
+ reg = ret;
+
+ if (reg & WM831X_WDOG_DEBUG)
+ dev_warn(wm831x->dev, "Watchdog is paused\n");
+
+ /* Apply any configuration */
+ if (pdev->dev.parent->platform_data) {
+ chip_pdata = pdev->dev.parent->platform_data;
+ pdata = chip_pdata->watchdog;
+ } else {
+ pdata = NULL;
+ }
+
+ if (pdata) {
+ reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
+ WM831X_WDOG_RST_SRC);
+
+ reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
+ reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
+ reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
+
+ if (pdata->update_gpio) {
+ ret = gpio_request(pdata->update_gpio,
+ "Watchdog update");
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to request update GPIO: %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = gpio_direction_output(pdata->update_gpio, 0);
+ if (ret != 0) {
+ dev_err(wm831x->dev,
+ "gpio_direction_output returned: %d\n",
+ ret);
+ goto err_gpio;
+ }
+
+ update_gpio = pdata->update_gpio;
+
+ /* Make sure the watchdog takes hardware updates */
+ reg |= WM831X_WDOG_RST_SRC;
+ }
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev,
+ "Failed to unlock security key: %d\n", ret);
+ goto err_gpio;
+ }
+ }
+
+ wm831x_wdt_miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&wm831x_wdt_miscdev);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret);
+ goto err_gpio;
+ }
+
+ return 0;
+
+err_gpio:
+ if (update_gpio) {
+ gpio_free(update_gpio);
+ update_gpio = 0;
+ }
+err:
+ return ret;
+}
+
+static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
+{
+ if (update_gpio) {
+ gpio_free(update_gpio);
+ update_gpio = 0;
+ }
+
+ misc_deregister(&wm831x_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_wdt_driver = {
+ .probe = wm831x_wdt_probe,
+ .remove = __devexit_p(wm831x_wdt_remove),
+ .driver = {
+ .name = "wm831x-watchdog",
+ },
+};
+
+static int __init wm831x_wdt_init(void)
+{
+ return platform_driver_register(&wm831x_wdt_driver);
+}
+module_init(wm831x_wdt_init);
+
+static void __exit wm831x_wdt_exit(void)
+{
+ platform_driver_unregister(&wm831x_wdt_driver);
+}
+module_exit(wm831x_wdt_exit);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM831x Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-watchdog");