summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig19
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/hpwdt.c5
-rw-r--r--drivers/watchdog/iTCO_vendor_support.c31
-rw-r--r--drivers/watchdog/iTCO_wdt.c164
-rw-r--r--drivers/watchdog/ib700wdt.c49
-rw-r--r--drivers/watchdog/mtx-1_wdt.c4
-rw-r--r--drivers/watchdog/s3c2410_wdt.c2
-rw-r--r--drivers/watchdog/sa1100_wdt.c12
-rw-r--r--drivers/watchdog/sch311x_wdt.c578
-rw-r--r--drivers/watchdog/wm8350_wdt.c329
11 files changed, 1071 insertions, 124 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 4fd3fa5546b..ec68c741b56 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 WM8350_WATCHDOG
+ tristate "WM8350 watchdog"
+ depends on MFD_WM8350
+ help
+ Support for the watchdog in the WM8350 AudioPlus PMIC. When
+ the watchdog triggers the system will be reset.
+
# ALPHA Architecture
# ARM Architecture
@@ -551,6 +558,18 @@ config CPU5_WDT
To compile this driver as a module, choose M here: the
module will be called cpu5wdt.
+config SMSC_SCH311X_WDT
+ tristate "SMSC SCH311X Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog timer on the
+ SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset
+ (LPC IO with 8042 KBC, Reset Generation, HWM and multiple
+ serial ports).
+
+ To compile this driver as a module, choose M here: the
+ module will be called sch311x_wdt.
+
config SMSC37B787_WDT
tristate "Winbond SMsC37B787 Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e352bbb7630..c19b866f5ed 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
+obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
@@ -133,4 +134,5 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
# XTENSA Architecture
# Architecture Independant
+obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index a3765e0be4a..763c1ea5dce 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -40,6 +40,7 @@
#include <linux/bootmem.h>
#include <linux/slab.h>
#include <asm/desc.h>
+#include <asm/cacheflush.h>
#define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */
#define CRU_BIOS_SIGNATURE_VALUE 0x55524324
@@ -394,6 +395,8 @@ static void __devinit dmi_find_cru(const struct dmi_header *dm)
smbios_cru64_ptr->double_offset;
cru_rom_addr = ioremap(cru_physical_address,
smbios_cru64_ptr->double_length);
+ set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
+ smbios_cru64_ptr->double_length >> PAGE_SHIFT);
}
}
}
@@ -482,7 +485,7 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,
"Management Log for details.\n");
}
- return NOTIFY_STOP;
+ return NOTIFY_OK;
}
/*
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c
index ca344a85eb9..2474ebca88f 100644
--- a/drivers/watchdog/iTCO_vendor_support.c
+++ b/drivers/watchdog/iTCO_vendor_support.c
@@ -1,7 +1,7 @@
/*
* intel TCO vendor specific watchdog driver support
*
- * (c) Copyright 2006 Wim Van Sebroeck <wim@iguana.be>.
+ * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,8 +19,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_vendor_support"
-#define DRV_VERSION "1.01"
-#define DRV_RELDATE "11-Nov-2006"
+#define DRV_VERSION "1.02"
#define PFX DRV_NAME ": "
/* Includes */
@@ -78,24 +77,6 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 (n
* 20.6 seconds.
*/
-static void supermicro_old_pre_start(unsigned long acpibase)
-{
- unsigned long val32;
-
- val32 = inl(SMI_EN);
- val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
- outl(val32, SMI_EN); /* Needed to activate watchdog */
-}
-
-static void supermicro_old_pre_stop(unsigned long acpibase)
-{
- unsigned long val32;
-
- val32 = inl(SMI_EN);
- val32 &= 0x00002000; /* Turn on SMI clearing watchdog */
- outl(val32, SMI_EN); /* Needed to deactivate watchdog */
-}
-
static void supermicro_old_pre_keepalive(unsigned long acpibase)
{
/* Reload TCO Timer (done in iTCO_wdt_keepalive) + */
@@ -247,18 +228,14 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
void iTCO_vendor_pre_start(unsigned long acpibase,
unsigned int heartbeat)
{
- if (vendorsupport == SUPERMICRO_OLD_BOARD)
- supermicro_old_pre_start(acpibase);
- else if (vendorsupport == SUPERMICRO_NEW_BOARD)
+ if (vendorsupport == SUPERMICRO_NEW_BOARD)
supermicro_new_pre_start(heartbeat);
}
EXPORT_SYMBOL(iTCO_vendor_pre_start);
void iTCO_vendor_pre_stop(unsigned long acpibase)
{
- if (vendorsupport == SUPERMICRO_OLD_BOARD)
- supermicro_old_pre_stop(acpibase);
- else if (vendorsupport == SUPERMICRO_NEW_BOARD)
+ if (vendorsupport == SUPERMICRO_NEW_BOARD)
supermicro_new_pre_stop();
}
EXPORT_SYMBOL(iTCO_vendor_pre_stop);
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index bfb93bc2ca9..5b395a4ddfd 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -1,7 +1,7 @@
/*
* intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets)
*
- * (c) Copyright 2006-2007 Wim Van Sebroeck <wim@iguana.be>.
+ * (c) Copyright 2006-2008 Wim Van Sebroeck <wim@iguana.be>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,34 +20,41 @@
* 82801BAM (ICH2-M) : document number 290687-002, 298242-027,
* 82801CA (ICH3-S) : document number 290733-003, 290739-013,
* 82801CAM (ICH3-M) : document number 290716-001, 290718-007,
- * 82801DB (ICH4) : document number 290744-001, 290745-020,
- * 82801DBM (ICH4-M) : document number 252337-001, 252663-005,
+ * 82801DB (ICH4) : document number 290744-001, 290745-025,
+ * 82801DBM (ICH4-M) : document number 252337-001, 252663-008,
* 82801E (C-ICH) : document number 273599-001, 273645-002,
- * 82801EB (ICH5) : document number 252516-001, 252517-003,
- * 82801ER (ICH5R) : document number 252516-001, 252517-003,
- * 82801FB (ICH6) : document number 301473-002, 301474-007,
- * 82801FR (ICH6R) : document number 301473-002, 301474-007,
- * 82801FBM (ICH6-M) : document number 301473-002, 301474-007,
- * 82801FW (ICH6W) : document number 301473-001, 301474-007,
- * 82801FRW (ICH6RW) : document number 301473-001, 301474-007,
- * 82801GB (ICH7) : document number 307013-002, 307014-009,
- * 82801GR (ICH7R) : document number 307013-002, 307014-009,
- * 82801GDH (ICH7DH) : document number 307013-002, 307014-009,
- * 82801GBM (ICH7-M) : document number 307013-002, 307014-009,
- * 82801GHM (ICH7-M DH) : document number 307013-002, 307014-009,
- * 82801HB (ICH8) : document number 313056-003, 313057-009,
- * 82801HR (ICH8R) : document number 313056-003, 313057-009,
- * 82801HBM (ICH8M) : document number 313056-003, 313057-009,
- * 82801HH (ICH8DH) : document number 313056-003, 313057-009,
- * 82801HO (ICH8DO) : document number 313056-003, 313057-009,
- * 82801HEM (ICH8M-E) : document number 313056-003, 313057-009,
- * 82801IB (ICH9) : document number 316972-001, 316973-006,
- * 82801IR (ICH9R) : document number 316972-001, 316973-006,
- * 82801IH (ICH9DH) : document number 316972-001, 316973-006,
- * 82801IO (ICH9DO) : document number 316972-001, 316973-006,
- * 6300ESB (6300ESB) : document number 300641-003, 300884-010,
- * 631xESB (631xESB) : document number 313082-001, 313075-005,
- * 632xESB (632xESB) : document number 313082-001, 313075-005
+ * 82801EB (ICH5) : document number 252516-001, 252517-028,
+ * 82801ER (ICH5R) : document number 252516-001, 252517-028,
+ * 6300ESB (6300ESB) : document number 300641-004, 300884-013,
+ * 82801FB (ICH6) : document number 301473-002, 301474-026,
+ * 82801FR (ICH6R) : document number 301473-002, 301474-026,
+ * 82801FBM (ICH6-M) : document number 301473-002, 301474-026,
+ * 82801FW (ICH6W) : document number 301473-001, 301474-026,
+ * 82801FRW (ICH6RW) : document number 301473-001, 301474-026,
+ * 631xESB (631xESB) : document number 313082-001, 313075-006,
+ * 632xESB (632xESB) : document number 313082-001, 313075-006,
+ * 82801GB (ICH7) : document number 307013-003, 307014-024,
+ * 82801GR (ICH7R) : document number 307013-003, 307014-024,
+ * 82801GDH (ICH7DH) : document number 307013-003, 307014-024,
+ * 82801GBM (ICH7-M) : document number 307013-003, 307014-024,
+ * 82801GHM (ICH7-M DH) : document number 307013-003, 307014-024,
+ * 82801GU (ICH7-U) : document number 307013-003, 307014-024,
+ * 82801HB (ICH8) : document number 313056-003, 313057-017,
+ * 82801HR (ICH8R) : document number 313056-003, 313057-017,
+ * 82801HBM (ICH8M) : document number 313056-003, 313057-017,
+ * 82801HH (ICH8DH) : document number 313056-003, 313057-017,
+ * 82801HO (ICH8DO) : document number 313056-003, 313057-017,
+ * 82801HEM (ICH8M-E) : document number 313056-003, 313057-017,
+ * 82801IB (ICH9) : document number 316972-004, 316973-012,
+ * 82801IR (ICH9R) : document number 316972-004, 316973-012,
+ * 82801IH (ICH9DH) : document number 316972-004, 316973-012,
+ * 82801IO (ICH9DO) : document number 316972-004, 316973-012,
+ * 82801IBM (ICH9M) : document number 316972-004, 316973-012,
+ * 82801IEM (ICH9M-E) : document number 316972-004, 316973-012,
+ * 82801JIB (ICH10) : document number 319973-002, 319974-002,
+ * 82801JIR (ICH10R) : document number 319973-002, 319974-002,
+ * 82801JD (ICH10D) : document number 319973-002, 319974-002,
+ * 82801JDO (ICH10DO) : document number 319973-002, 319974-002
*/
/*
@@ -56,8 +63,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
-#define DRV_VERSION "1.03"
-#define DRV_RELDATE "30-Apr-2008"
+#define DRV_VERSION "1.04"
#define PFX DRV_NAME ": "
/* Includes */
@@ -96,19 +102,26 @@ enum iTCO_chipsets {
TCO_ICH6, /* ICH6 & ICH6R */
TCO_ICH6M, /* ICH6-M */
TCO_ICH6W, /* ICH6W & ICH6RW */
+ TCO_631XESB, /* 631xESB/632xESB */
TCO_ICH7, /* ICH7 & ICH7R */
- TCO_ICH7M, /* ICH7-M */
+ TCO_ICH7DH, /* ICH7DH */
+ TCO_ICH7M, /* ICH7-M & ICH7-U */
TCO_ICH7MDH, /* ICH7-M DH */
TCO_ICH8, /* ICH8 & ICH8R */
- TCO_ICH8ME, /* ICH8M-E */
TCO_ICH8DH, /* ICH8DH */
TCO_ICH8DO, /* ICH8DO */
TCO_ICH8M, /* ICH8M */
+ TCO_ICH8ME, /* ICH8M-E */
TCO_ICH9, /* ICH9 */
TCO_ICH9R, /* ICH9R */
TCO_ICH9DH, /* ICH9DH */
TCO_ICH9DO, /* ICH9DO */
- TCO_631XESB, /* 631xESB/632xESB */
+ TCO_ICH9M, /* ICH9M */
+ TCO_ICH9ME, /* ICH9M-E */
+ TCO_ICH10, /* ICH10 */
+ TCO_ICH10R, /* ICH10R */
+ TCO_ICH10D, /* ICH10D */
+ TCO_ICH10DO, /* ICH10DO */
};
static struct {
@@ -129,19 +142,26 @@ static struct {
{"ICH6 or ICH6R", 2},
{"ICH6-M", 2},
{"ICH6W or ICH6RW", 2},
+ {"631xESB/632xESB", 2},
{"ICH7 or ICH7R", 2},
- {"ICH7-M", 2},
+ {"ICH7DH", 2},
+ {"ICH7-M or ICH7-U", 2},
{"ICH7-M DH", 2},
{"ICH8 or ICH8R", 2},
- {"ICH8M-E", 2},
{"ICH8DH", 2},
{"ICH8DO", 2},
{"ICH8M", 2},
+ {"ICH8M-E", 2},
{"ICH9", 2},
{"ICH9R", 2},
{"ICH9DH", 2},
{"ICH9DO", 2},
- {"631xESB/632xESB", 2},
+ {"ICH9M", 2},
+ {"ICH9M-E", 2},
+ {"ICH10", 2},
+ {"ICH10R", 2},
+ {"ICH10D", 2},
+ {"ICH10DO", 2},
{NULL, 0}
};
@@ -175,18 +195,6 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0, TCO_ICH6)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1, TCO_ICH6M)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2, TCO_ICH6W)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)},
- { ITCO_PCI_DEVICE(0x2918, TCO_ICH9)},
- { ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)},
- { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0, TCO_631XESB)},
{ ITCO_PCI_DEVICE(0x2671, TCO_631XESB)},
{ ITCO_PCI_DEVICE(0x2672, TCO_631XESB)},
@@ -203,6 +211,25 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
{ ITCO_PCI_DEVICE(0x267d, TCO_631XESB)},
{ ITCO_PCI_DEVICE(0x267e, TCO_631XESB)},
{ ITCO_PCI_DEVICE(0x267f, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)},
+ { ITCO_PCI_DEVICE(0x2918, TCO_ICH9)},
+ { ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)},
+ { ITCO_PCI_DEVICE(0x2919, TCO_ICH9M)},
+ { ITCO_PCI_DEVICE(0x2917, TCO_ICH9ME)},
+ { ITCO_PCI_DEVICE(0x3a18, TCO_ICH10)},
+ { ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)},
+ { ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)},
+ { ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
@@ -311,6 +338,7 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
static int iTCO_wdt_start(void)
{
unsigned int val;
+ unsigned long val32;
spin_lock(&iTCO_wdt_private.io_lock);
@@ -323,6 +351,18 @@ static int iTCO_wdt_start(void)
return -EIO;
}
+ /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+ val32 = inl(SMI_EN);
+ val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
+ outl(val32, SMI_EN);
+
+ /* Force the timer to its reload value by writing to the TCO_RLD
+ register */
+ if (iTCO_wdt_private.iTCO_version == 2)
+ outw(0x01, TCO_RLD);
+ else if (iTCO_wdt_private.iTCO_version == 1)
+ outb(0x01, TCO_RLD);
+
/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
val = inw(TCO1_CNT);
val &= 0xf7ff;
@@ -338,6 +378,7 @@ static int iTCO_wdt_start(void)
static int iTCO_wdt_stop(void)
{
unsigned int val;
+ unsigned long val32;
spin_lock(&iTCO_wdt_private.io_lock);
@@ -349,6 +390,11 @@ static int iTCO_wdt_stop(void)
outw(val, TCO1_CNT);
val = inw(TCO1_CNT);
+ /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
+ val32 = inl(SMI_EN);
+ val32 |= 0x00002000;
+ outl(val32, SMI_EN);
+
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();
@@ -459,7 +505,6 @@ static int iTCO_wdt_open(struct inode *inode, struct file *file)
/*
* Reload and activate timer
*/
- iTCO_wdt_keepalive();
iTCO_wdt_start();
return nonseekable_open(inode, file);
}
@@ -604,7 +649,6 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
int ret;
u32 base_address;
unsigned long RCBA;
- unsigned long val32;
/*
* Find the ACPI/PM base I/O address which is the base
@@ -644,17 +688,13 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();
- /* Set the TCO_EN bit in SMI_EN register */
+ /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
printk(KERN_ERR PFX
"I/O address 0x%04lx already in use\n", SMI_EN);
ret = -EIO;
goto out;
}
- val32 = inl(SMI_EN);
- val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
- outl(val32, SMI_EN);
- release_region(SMI_EN, 4);
/* The TCO I/O registers reside in a 32-byte range pointed to
by the TCOBASE value */
@@ -662,7 +702,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n",
TCOBASE);
ret = -EIO;
- goto out;
+ goto unreg_smi_en;
}
printk(KERN_INFO PFX
@@ -672,8 +712,9 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
TCOBASE);
/* Clear out the (probably old) status */
- outb(0, TCO1_STS);
- outb(3, TCO2_STS);
+ outb(8, TCO1_STS); /* Clear the Time Out Status bit */
+ outb(2, TCO2_STS); /* Clear SECOND_TO_STS bit */
+ outb(4, TCO2_STS); /* Clear BOOT_STS bit */
/* Make sure the watchdog is not running */
iTCO_wdt_stop();
@@ -701,6 +742,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
unreg_region:
release_region(TCOBASE, 0x20);
+unreg_smi_en:
+ release_region(SMI_EN, 4);
out:
if (iTCO_wdt_private.iTCO_version == 2)
iounmap(iTCO_wdt_private.gcs);
@@ -718,6 +761,7 @@ static void __devexit iTCO_wdt_cleanup(void)
/* Deregister */
misc_deregister(&iTCO_wdt_miscdev);
release_region(TCOBASE, 0x20);
+ release_region(SMI_EN, 4);
if (iTCO_wdt_private.iTCO_version == 2)
iounmap(iTCO_wdt_private.gcs);
pci_dev_put(iTCO_wdt_private.pdev);
@@ -782,8 +826,8 @@ static int __init iTCO_wdt_init_module(void)
{
int err;
- printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s (%s)\n",
- DRV_VERSION, DRV_RELDATE);
+ printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n",
+ DRV_VERSION);
err = platform_driver_register(&iTCO_wdt_driver);
if (err)
diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c
index 317ef2b16cf..4bef3ddff4a 100644
--- a/drivers/watchdog/ib700wdt.c
+++ b/drivers/watchdog/ib700wdt.c
@@ -91,32 +91,16 @@ static char expect_close;
*
*/
-static int wd_times[] = {
- 30, /* 0x0 */
- 28, /* 0x1 */
- 26, /* 0x2 */
- 24, /* 0x3 */
- 22, /* 0x4 */
- 20, /* 0x5 */
- 18, /* 0x6 */
- 16, /* 0x7 */
- 14, /* 0x8 */
- 12, /* 0x9 */
- 10, /* 0xA */
- 8, /* 0xB */
- 6, /* 0xC */
- 4, /* 0xD */
- 2, /* 0xE */
- 0, /* 0xF */
-};
-
#define WDT_STOP 0x441
#define WDT_START 0x443
/* Default timeout */
-#define WD_TIMO 0 /* 30 seconds +/- 20%, from table */
-
-static int wd_margin = WD_TIMO;
+#define WATCHDOG_TIMEOUT 30 /* 30 seconds +/- 20% */
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 0<= timeout <=30, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
@@ -131,6 +115,8 @@ MODULE_PARM_DESC(nowayout,
static void ibwdt_ping(void)
{
+ int wd_margin = 15 - ((timeout + 1) / 2);
+
spin_lock(&ibwdt_lock);
/* Write a watchdog value */
@@ -148,15 +134,10 @@ static void ibwdt_disable(void)
static int ibwdt_set_heartbeat(int t)
{
- int i;
-
- if ((t < 0) || (t > 30))
+ if (t < 0 || t > 30)
return -EINVAL;
- for (i = 0x0F; i > -1; i--)
- if (wd_times[i] >= t)
- break;
- wd_margin = i;
+ timeout = t;
return 0;
}
@@ -240,7 +221,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* Fall */
case WDIOC_GETTIMEOUT:
- return put_user(wd_times[wd_margin], p);
+ return put_user(timeout, p);
default:
return -ENOTTY;
@@ -317,6 +298,14 @@ static int __devinit ibwdt_probe(struct platform_device *dev)
goto out_nostartreg;
}
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (ibwdt_set_heartbeat(timeout)) {
+ ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 0<=x<=30, using %d\n", timeout);
+ }
+
res = misc_register(&ibwdt_miscdev);
if (res) {
printk(KERN_ERR PFX "failed to register misc device\n");
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index b4b7b0a4c11..3acce623f20 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -98,6 +98,8 @@ static void mtx1_wdt_reset(void)
static void mtx1_wdt_start(void)
{
+ unsigned long flags;
+
spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
if (!mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 1;
@@ -110,6 +112,8 @@ static void mtx1_wdt_start(void)
static int mtx1_wdt_stop(void)
{
+ unsigned long flags;
+
spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
if (mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 0;
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f7f6ce82a5e..e31925ee834 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -42,7 +42,7 @@
#undef S3C_VA_WATCHDOG
#define S3C_VA_WATCHDOG (0)
-#include <asm/plat-s3c/regs-watchdog.h>
+#include <plat/regs-watchdog.h>
#define PFX "s3c2410-wdt: "
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index ed01e4c2bef..e19b4579471 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/timex.h>
#ifdef CONFIG_ARCH_PXA
#include <mach/pxa-regs.h>
@@ -35,8 +36,7 @@
#include <mach/reset.h>
#include <mach/hardware.h>
-#define OSCR_FREQ CLOCK_TICK_RATE
-
+static unsigned long oscr_freq;
static unsigned long sa1100wdt_users;
static int pre_margin;
static int boot_status;
@@ -123,12 +123,12 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
break;
}
- pre_margin = OSCR_FREQ * time;
+ pre_margin = oscr_freq * time;
OSMR3 = OSCR + pre_margin;
/*fall through*/
case WDIOC_GETTIMEOUT:
- ret = put_user(pre_margin / OSCR_FREQ, p);
+ ret = put_user(pre_margin / oscr_freq, p);
break;
}
return ret;
@@ -155,6 +155,8 @@ static int __init sa1100dog_init(void)
{
int ret;
+ oscr_freq = get_clock_tick_rate();
+
/*
* Read the reset status, and save it for later. If
* we suspend, RCSR will be cleared, and the watchdog
@@ -162,7 +164,7 @@ static int __init sa1100dog_init(void)
*/
boot_status = (reset_status & RESET_STATUS_WATCHDOG) ?
WDIOF_CARDRESET : 0;
- pre_margin = OSCR_FREQ * margin;
+ pre_margin = oscr_freq * margin;
ret = misc_register(&sa1100dog_miscdev);
if (ret == 0)
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
new file mode 100644
index 00000000000..569eb295a7a
--- /dev/null
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -0,0 +1,578 @@
+/*
+ * sch311x_wdt.c - Driver for the SCH311x Super-I/O chips
+ * integrated watchdog.
+ *
+ * (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * 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 Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+/* Includes */
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/... */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
+ (WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/platform_device.h> /* For platform_driver framework */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+/* Module and version information */
+#define DRV_NAME "sch311x_wdt"
+#define PFX DRV_NAME ": "
+
+/* Runtime registers */
+#define RESGEN 0x1d
+#define GP60 0x47
+#define WDT_TIME_OUT 0x65
+#define WDT_VAL 0x66
+#define WDT_CFG 0x67
+#define WDT_CTRL 0x68
+
+/* internal variables */
+static unsigned long sch311x_wdt_is_open;
+static char sch311x_wdt_expect_close;
+static struct platform_device *sch311x_wdt_pdev;
+
+static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x00 };
+
+static struct { /* The devices private data */
+ /* the Runtime Register base address */
+ unsigned short runtime_reg;
+ /* The card's boot status */
+ int boot_status;
+ /* the lock for io operations */
+ spinlock_t io_lock;
+} sch311x_wdt_data;
+
+/* Module load parameters */
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short therm_trip;
+module_param(therm_trip, ushort, 0);
+MODULE_PARM_DESC(therm_trip, "Should a ThermTrip trigger the reset generator");
+
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=15300, 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) ")");
+
+/*
+ * Super-IO functions
+ */
+
+static inline void sch311x_sio_enter(int sio_config_port)
+{
+ outb(0x55, sio_config_port);
+}
+
+static inline void sch311x_sio_exit(int sio_config_port)
+{
+ outb(0xaa, sio_config_port);
+}
+
+static inline int sch311x_sio_inb(int sio_config_port, int reg)
+{
+ outb(reg, sio_config_port);
+ return inb(sio_config_port + 1);
+}
+
+static inline void sch311x_sio_outb(int sio_config_port, int reg, int val)
+{
+ outb(reg, sio_config_port);
+ outb(val, sio_config_port + 1);
+}
+
+/*
+ * Watchdog Operations
+ */
+
+static void sch311x_wdt_set_timeout(int t)
+{
+ unsigned char timeout_unit = 0x80;
+
+ /* When new timeout is bigger then 255 seconds, we will use minutes */
+ if (t > 255) {
+ timeout_unit = 0;
+ t /= 60;
+ }
+
+ /* -- Watchdog Timeout --
+ * Bit 0-6 (Reserved)
+ * Bit 7 WDT Time-out Value Units Select
+ * (0 = Minutes, 1 = Seconds)
+ */
+ outb(timeout_unit, sch311x_wdt_data.runtime_reg + WDT_TIME_OUT);
+
+ /* -- Watchdog Timer Time-out Value --
+ * Bit 0-7 Binary coded units (0=Disabled, 1..255)
+ */
+ outb(t, sch311x_wdt_data.runtime_reg + WDT_VAL);
+}
+
+static void sch311x_wdt_start(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* set watchdog's timeout */
+ sch311x_wdt_set_timeout(timeout);
+ /* enable the watchdog */
+ /* -- General Purpose I/O Bit 6.0 --
+ * Bit 0, In/Out: 0 = Output, 1 = Input
+ * Bit 1, Polarity: 0 = No Invert, 1 = Invert
+ * Bit 2-3, Function select: 00 = GPI/O, 01 = LED1, 11 = WDT,
+ * 10 = Either Edge Triggered Intr.4
+ * Bit 4-6 (Reserved)
+ * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
+ */
+ outb(0x0e, sch311x_wdt_data.runtime_reg + GP60);
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+
+}
+
+static void sch311x_wdt_stop(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* stop the watchdog */
+ outb(0x01, sch311x_wdt_data.runtime_reg + GP60);
+ /* disable timeout by setting it to 0 */
+ sch311x_wdt_set_timeout(0);
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+static void sch311x_wdt_keepalive(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+ sch311x_wdt_set_timeout(timeout);
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+static int sch311x_wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > (255*60))
+ return -EINVAL;
+
+ /* When new timeout is bigger then 255 seconds,
+ * we will round up to minutes (with a max of 255) */
+ if (t > 255)
+ t = (((t - 1) / 60) + 1) * 60;
+
+ timeout = t;
+ return 0;
+}
+
+static void sch311x_wdt_get_status(int *status)
+{
+ unsigned char new_status;
+
+ *status = 0;
+
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* -- Watchdog timer control --
+ * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured
+ * Bit 1 Reserved
+ * Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning)
+ * Bit 3 P20 Force Timeout enabled:
+ * 0 = P20 activity does not generate the WD timeout event
+ * 1 = P20 Allows rising edge of P20, from the keyboard
+ * controller, to force the WD timeout event.
+ * Bit 4-7 Reserved
+ */
+ new_status = inb(sch311x_wdt_data.runtime_reg + WDT_CTRL);
+ if (new_status & 0x01)
+ *status |= WDIOF_CARDRESET;
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t sch311x_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ sch311x_wdt_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ sch311x_wdt_expect_close = 42;
+ }
+ }
+ sch311x_wdt_keepalive();
+ }
+ return count;
+}
+
+static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_timeout;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = DRV_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ {
+ sch311x_wdt_get_status(&status);
+ return put_user(status, p);
+ }
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(sch311x_wdt_data.boot_status, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ sch311x_wdt_stop();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ sch311x_wdt_start();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ sch311x_wdt_keepalive();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (sch311x_wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ sch311x_wdt_keepalive();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int sch311x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &sch311x_wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+ sch311x_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int sch311x_wdt_close(struct inode *inode, struct file *file)
+{
+ if (sch311x_wdt_expect_close == 42) {
+ sch311x_wdt_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ sch311x_wdt_keepalive();
+ }
+ clear_bit(0, &sch311x_wdt_is_open);
+ sch311x_wdt_expect_close = 0;
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations sch311x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sch311x_wdt_write,
+ .unlocked_ioctl = sch311x_wdt_ioctl,
+ .open = sch311x_wdt_open,
+ .release = sch311x_wdt_close,
+};
+
+static struct miscdevice sch311x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sch311x_wdt_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned char val;
+ int err;
+
+ spin_lock_init(&sch311x_wdt_data.io_lock);
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + RESGEN, 1,
+ DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + RESGEN,
+ sch311x_wdt_data.runtime_reg + RESGEN);
+ err = -EBUSY;
+ goto exit;
+ }
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + GP60, 1, DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + GP60,
+ sch311x_wdt_data.runtime_reg + GP60);
+ err = -EBUSY;
+ goto exit_release_region;
+ }
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4,
+ DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + WDT_TIME_OUT,
+ sch311x_wdt_data.runtime_reg + WDT_CTRL);
+ err = -EBUSY;
+ goto exit_release_region2;
+ }
+
+ /* Make sure that the watchdog is not running */
+ sch311x_wdt_stop();
+
+ /* Disable keyboard and mouse interaction and interrupt */
+ /* -- Watchdog timer configuration --
+ * Bit 0 Reserved
+ * Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
+ * Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
+ * Bit 3 Reserved
+ * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
+ * 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
+ */
+ outb(0, sch311x_wdt_data.runtime_reg + WDT_CFG);
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (sch311x_wdt_set_heartbeat(timeout)) {
+ sch311x_wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ dev_info(dev, "timeout value must be 1<=x<=15300, using %d\n",
+ timeout);
+ }
+
+ /* Get status at boot */
+ sch311x_wdt_get_status(&sch311x_wdt_data.boot_status);
+
+ /* enable watchdog */
+ /* -- Reset Generator --
+ * Bit 0 Enable Watchdog Timer Generation: 0* = Enabled, 1 = Disabled
+ * Bit 1 Thermtrip Source Select: O* = No Source, 1 = Source
+ * Bit 2 WDT2_CTL: WDT input bit
+ * Bit 3-7 Reserved
+ */
+ outb(0, sch311x_wdt_data.runtime_reg + RESGEN);
+ val = therm_trip ? 0x06 : 0x04;
+ outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
+
+ err = misc_register(&sch311x_wdt_miscdev);
+ if (err != 0) {
+ dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
+ goto exit_release_region3;
+ }
+
+ sch311x_wdt_miscdev.parent = dev;
+
+ dev_info(dev,
+ "SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return 0;
+
+exit_release_region3:
+ release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
+exit_release_region2:
+ release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+exit_release_region:
+ release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+ sch311x_wdt_data.runtime_reg = 0;
+exit:
+ return err;
+}
+
+static int __devexit sch311x_wdt_remove(struct platform_device *pdev)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ sch311x_wdt_stop();
+
+ /* Deregister */
+ misc_deregister(&sch311x_wdt_miscdev);
+ release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
+ release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+ release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+ sch311x_wdt_data.runtime_reg = 0;
+ return 0;
+}
+
+static void sch311x_wdt_shutdown(struct platform_device *dev)
+{
+ /* Turn the WDT off if we have a soft shutdown */
+ sch311x_wdt_stop();
+}
+
+#define sch311x_wdt_suspend NULL
+#define sch311x_wdt_resume NULL
+
+static struct platform_driver sch311x_wdt_driver = {
+ .probe = sch311x_wdt_probe,
+ .remove = __devexit_p(sch311x_wdt_remove),
+ .shutdown = sch311x_wdt_shutdown,
+ .suspend = sch311x_wdt_suspend,
+ .resume = sch311x_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init sch311x_detect(int sio_config_port, unsigned short *addr)
+{
+ int err = 0, reg;
+ unsigned short base_addr;
+ unsigned char dev_id;
+
+ sch311x_sio_enter(sio_config_port);
+
+ /* Check device ID. We currently know about:
+ * SCH3112 (0x7c), SCH3114 (0x7d), and SCH3116 (0x7f). */
+ reg = force_id ? force_id : sch311x_sio_inb(sio_config_port, 0x20);
+ if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
+ err = -ENODEV;
+ goto exit;
+ }
+ dev_id = reg == 0x7c ? 2 : reg == 0x7d ? 4 : 6;
+
+ /* Select logical device A (runtime registers) */
+ sch311x_sio_outb(sio_config_port, 0x07, 0x0a);
+
+ /* Check if Logical Device Register is currently active */
+ if (sch311x_sio_inb(sio_config_port, 0x30) && 0x01 == 0)
+ printk(KERN_INFO PFX "Seems that LDN 0x0a is not active...\n");
+
+ /* Get the base address of the runtime registers */
+ base_addr = (sch311x_sio_inb(sio_config_port, 0x60) << 8) |
+ sch311x_sio_inb(sio_config_port, 0x61);
+ if (!base_addr) {
+ printk(KERN_ERR PFX "Base address not set.\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ *addr = base_addr;
+
+ printk(KERN_INFO PFX "Found an SMSC SCH311%d chip at 0x%04x\n",
+ dev_id, base_addr);
+
+exit:
+ sch311x_sio_exit(sio_config_port);
+ return err;
+}
+
+static int __init sch311x_wdt_init(void)
+{
+ int err, i, found = 0;
+ unsigned short addr = 0;
+
+ for (i = 0; !found && sch311x_ioports[i]; i++)
+ if (sch311x_detect(sch311x_ioports[i], &addr) == 0)
+ found++;
+
+ if (!found)
+ return -ENODEV;
+
+ sch311x_wdt_data.runtime_reg = addr;
+
+ err = platform_driver_register(&sch311x_wdt_driver);
+ if (err)
+ return err;
+
+ sch311x_wdt_pdev = platform_device_register_simple(DRV_NAME, addr,
+ NULL, 0);
+
+ if (IS_ERR(sch311x_wdt_pdev)) {
+ err = PTR_ERR(sch311x_wdt_pdev);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&sch311x_wdt_driver);
+ return err;
+}
+
+static void __exit sch311x_wdt_exit(void)
+{
+ platform_device_unregister(sch311x_wdt_pdev);
+ platform_driver_unregister(&sch311x_wdt_driver);
+}
+
+module_init(sch311x_wdt_init);
+module_exit(sch311x_wdt_exit);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("SMSC SCH311x WatchDog Timer Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
new file mode 100644
index 00000000000..2bc0d4d4b41
--- /dev/null
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -0,0 +1,329 @@
+/*
+ * Watchdog driver for the wm8350
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.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
+ */
+
+#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/mfd/wm8350/core.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 wm8350_wdt_users;
+static struct miscdevice wm8350_wdt_miscdev;
+static int wm8350_wdt_expect_close;
+static DEFINE_MUTEX(wdt_mutex);
+
+static struct {
+ int time; /* Seconds */
+ u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
+} wm8350_wdt_cfgs[] = {
+ { 1, 0x02 },
+ { 2, 0x04 },
+ { 4, 0x05 },
+};
+
+static struct wm8350 *get_wm8350(void)
+{
+ return dev_get_drvdata(wm8350_wdt_miscdev.parent);
+}
+
+static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_TO_MASK;
+ reg |= value;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_start(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ reg |= 0x20;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_stop(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_kick(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_open(struct inode *inode, struct file *file)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ int ret;
+
+ if (!wm8350)
+ return -ENODEV;
+
+ if (test_and_set_bit(0, &wm8350_wdt_users))
+ return -EBUSY;
+
+ ret = wm8350_wdt_start(wm8350);
+ if (ret != 0)
+ return ret;
+
+ return nonseekable_open(inode, file);
+}
+
+static int wm8350_wdt_release(struct inode *inode, struct file *file)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+
+ if (wm8350_wdt_expect_close)
+ wm8350_wdt_stop(wm8350);
+ else {
+ dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n");
+ wm8350_wdt_kick(wm8350);
+ }
+
+ clear_bit(0, &wm8350_wdt_users);
+
+ return 0;
+}
+
+static ssize_t wm8350_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ size_t i;
+
+ if (count) {
+ wm8350_wdt_kick(wm8350);
+
+ if (!nowayout) {
+ /* In case it was set long ago */
+ wm8350_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')
+ wm8350_wdt_expect_close = 42;
+ }
+ }
+ }
+ return count;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "WM8350 Watchdog",
+};
+
+static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ 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 = wm8350_wdt_start(wm8350);
+
+ if (options == WDIOS_ENABLECARD)
+ ret = wm8350_wdt_stop(wm8350);
+ break;
+ }
+
+ case WDIOC_KEEPALIVE:
+ ret = wm8350_wdt_kick(wm8350);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ if (time == 0) {
+ if (nowayout)
+ ret = -EINVAL;
+ else
+ wm8350_wdt_stop(wm8350);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].time == time)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
+ ret = -EINVAL;
+ else
+ ret = wm8350_wdt_set_timeout(wm8350,
+ wm8350_wdt_cfgs[i].val);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= WM8350_WDOG_TO_MASK;
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].val == reg)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) {
+ dev_warn(wm8350->dev,
+ "Unknown watchdog configuration: %x\n", reg);
+ ret = -EINVAL;
+ } else
+ ret = put_user(wm8350_wdt_cfgs[i].time, p);
+
+ }
+
+ return ret;
+}
+
+static const struct file_operations wm8350_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wm8350_wdt_write,
+ .unlocked_ioctl = wm8350_wdt_ioctl,
+ .open = wm8350_wdt_open,
+ .release = wm8350_wdt_release,
+};
+
+static struct miscdevice wm8350_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wm8350_wdt_fops,
+};
+
+static int wm8350_wdt_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+
+ if (!wm8350) {
+ dev_err(wm8350->dev, "No driver data supplied\n");
+ return -ENODEV;
+ }
+
+ /* Default to 4s timeout */
+ wm8350_wdt_set_timeout(wm8350, 0x05);
+
+ wm8350_wdt_miscdev.parent = &pdev->dev;
+
+ return misc_register(&wm8350_wdt_miscdev);
+}
+
+static int __exit wm8350_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&wm8350_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver wm8350_wdt_driver = {
+ .probe = wm8350_wdt_probe,
+ .remove = wm8350_wdt_remove,
+ .driver = {
+ .name = "wm8350-wdt",
+ },
+};
+
+static int __init wm8350_wdt_init(void)
+{
+ return platform_driver_register(&wm8350_wdt_driver);
+}
+module_init(wm8350_wdt_init);
+
+static void __exit wm8350_wdt_exit(void)
+{
+ platform_driver_unregister(&wm8350_wdt_driver);
+}
+module_exit(wm8350_wdt_exit);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM8350 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-wdt");