aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--include/linux/mfd/wm831x/watchdog.h52
14 files changed, 1293 insertions, 73 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b1ccc04f3c9a..ff3eb8ff6bd7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -55,6 +55,13 @@ config SOFT_WATCHDOG
55 To compile this driver as a module, choose M here: the 55 To compile this driver as a module, choose M here: the
56 module will be called softdog. 56 module will be called softdog.
57 57
58config WM831X_WATCHDOG
59 tristate "WM831x watchdog"
60 depends on MFD_WM831X
61 help
62 Support for the watchdog in the WM831x AudioPlus PMICs. When
63 the watchdog triggers the system will be reset.
64
58config WM8350_WATCHDOG 65config WM8350_WATCHDOG
59 tristate "WM8350 watchdog" 66 tristate "WM8350 watchdog"
60 depends on MFD_WM8350 67 depends on MFD_WM8350
@@ -266,6 +273,15 @@ config STMP3XXX_WATCHDOG
266 To compile this driver as a module, choose M here: the 273 To compile this driver as a module, choose M here: the
267 module will be called stmp3xxx_wdt. 274 module will be called stmp3xxx_wdt.
268 275
276config NUC900_WATCHDOG
277 tristate "Nuvoton NUC900 watchdog"
278 depends on ARCH_W90X900
279 help
280 Say Y here if to include support for the watchdog timer
281 for the Nuvoton NUC900 series SoCs.
282 To compile this driver as a module, choose M here: the
283 module will be called nuc900_wdt.
284
269# AVR32 Architecture 285# AVR32 Architecture
270 286
271config AT32AP700X_WDT 287config AT32AP700X_WDT
@@ -369,6 +385,28 @@ config SC520_WDT
369 You can compile this driver directly into the kernel, or use 385 You can compile this driver directly into the kernel, or use
370 it as a module. The module will be called sc520_wdt. 386 it as a module. The module will be called sc520_wdt.
371 387
388config SBC_FITPC2_WATCHDOG
389 tristate "Compulab SBC-FITPC2 watchdog"
390 depends on X86
391 ---help---
392 This is the driver for the built-in watchdog timer on the fit-PC2
393 Single-board computer made by Compulab.
394
395 It`s possible to enable watchdog timer either from BIOS (F2) or from booted Linux.
396 When "Watchdog Timer Value" enabled one can set 31-255 s operational range.
397
398 Entering BIOS setup temporary disables watchdog operation regardless to current state,
399 so system will not be restarted while user in BIOS setup.
400
401 Once watchdog was enabled the system will be restarted every
402 "Watchdog Timer Value" period, so to prevent it user can restart or
403 disable the watchdog.
404
405 To compile this driver as a module, choose M here: the
406 module will be called sbc_fitpc2_wdt.
407
408 Most people will say N.
409
372config EUROTECH_WDT 410config EUROTECH_WDT
373 tristate "Eurotech CPU-1220/1410 Watchdog Timer" 411 tristate "Eurotech CPU-1220/1410 Watchdog Timer"
374 depends on X86 412 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 3d774294a2b7..348b3b862c99 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
44obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o 44obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
45obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o 45obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
46obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o 46obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
47obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
47 48
48# AVR32 Architecture 49# AVR32 Architecture
49obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o 50obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -64,6 +65,7 @@ obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
64obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o 65obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
65obj-$(CONFIG_GEODE_WDT) += geodewdt.o 66obj-$(CONFIG_GEODE_WDT) += geodewdt.o
66obj-$(CONFIG_SC520_WDT) += sc520_wdt.o 67obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
68obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
67obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o 69obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
68obj-$(CONFIG_IB700_WDT) += ib700wdt.o 70obj-$(CONFIG_IB700_WDT) += ib700wdt.o
69obj-$(CONFIG_IBMASR) += ibmasr.o 71obj-$(CONFIG_IBMASR) += ibmasr.o
@@ -139,5 +141,6 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
139# XTENSA Architecture 141# XTENSA Architecture
140 142
141# Architecture Independant 143# Architecture Independant
144obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
142obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 145obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
143obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 146obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 2f8643efe92c..2e94b71b20d9 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -28,9 +28,8 @@
28#include <linux/errno.h> 28#include <linux/errno.h>
29#include <linux/init.h> 29#include <linux/init.h>
30#include <linux/miscdevice.h> 30#include <linux/miscdevice.h>
31#include <linux/platform_device.h>
31#include <linux/watchdog.h> 32#include <linux/watchdog.h>
32#include <linux/notifier.h>
33#include <linux/reboot.h>
34#include <linux/fs.h> 33#include <linux/fs.h>
35#include <linux/ioport.h> 34#include <linux/ioport.h>
36#include <linux/io.h> 35#include <linux/io.h>
@@ -76,24 +75,10 @@ static unsigned expect_close;
76/* XXX currently fixed, allows max margin ~68.72 secs */ 75/* XXX currently fixed, allows max margin ~68.72 secs */
77#define prescale_value 0xffff 76#define prescale_value 0xffff
78 77
79/* Offset of the WDT registers */ 78/* Resource of the WDT registers */
80static unsigned long ar7_regs_wdt; 79static struct resource *ar7_regs_wdt;
81/* Pointer to the remapped WDT IO space */ 80/* Pointer to the remapped WDT IO space */
82static struct ar7_wdt *ar7_wdt; 81static struct ar7_wdt *ar7_wdt;
83static void ar7_wdt_get_regs(void)
84{
85 u16 chip_id = ar7_chip_id();
86 switch (chip_id) {
87 case AR7_CHIP_7100:
88 case AR7_CHIP_7200:
89 ar7_regs_wdt = AR7_REGS_WDT;
90 break;
91 default:
92 ar7_regs_wdt = UR8_REGS_WDT;
93 break;
94 }
95}
96
97 82
98static void ar7_wdt_kick(u32 value) 83static void ar7_wdt_kick(u32 value)
99{ 84{
@@ -202,20 +187,6 @@ static int ar7_wdt_release(struct inode *inode, struct file *file)
202 return 0; 187 return 0;
203} 188}
204 189
205static int ar7_wdt_notify_sys(struct notifier_block *this,
206 unsigned long code, void *unused)
207{
208 if (code == SYS_HALT || code == SYS_POWER_OFF)
209 if (!nowayout)
210 ar7_wdt_disable_wdt();
211
212 return NOTIFY_DONE;
213}
214
215static struct notifier_block ar7_wdt_notifier = {
216 .notifier_call = ar7_wdt_notify_sys,
217};
218
219static ssize_t ar7_wdt_write(struct file *file, const char *data, 190static ssize_t ar7_wdt_write(struct file *file, const char *data,
220 size_t len, loff_t *ppos) 191 size_t len, loff_t *ppos)
221{ 192{
@@ -299,56 +270,86 @@ static struct miscdevice ar7_wdt_miscdev = {
299 .fops = &ar7_wdt_fops, 270 .fops = &ar7_wdt_fops,
300}; 271};
301 272
302static int __init ar7_wdt_init(void) 273static int __devinit ar7_wdt_probe(struct platform_device *pdev)
303{ 274{
304 int rc; 275 int rc;
305 276
306 spin_lock_init(&wdt_lock); 277 spin_lock_init(&wdt_lock);
307 278
308 ar7_wdt_get_regs(); 279 ar7_regs_wdt =
280 platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
281 if (!ar7_regs_wdt) {
282 printk(KERN_ERR DRVNAME ": could not get registers resource\n");
283 rc = -ENODEV;
284 goto out;
285 }
309 286
310 if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt), 287 if (!request_mem_region(ar7_regs_wdt->start,
311 LONGNAME)) { 288 resource_size(ar7_regs_wdt), LONGNAME)) {
312 printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n"); 289 printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
313 return -EBUSY; 290 rc = -EBUSY;
291 goto out;
314 } 292 }
315 293
316 ar7_wdt = (struct ar7_wdt *) 294 ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
317 ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt)); 295 if (!ar7_wdt) {
296 printk(KERN_ERR DRVNAME ": could not ioremap registers\n");
297 rc = -ENXIO;
298 goto out_mem_region;
299 }
318 300
319 ar7_wdt_disable_wdt(); 301 ar7_wdt_disable_wdt();
320 ar7_wdt_prescale(prescale_value); 302 ar7_wdt_prescale(prescale_value);
321 ar7_wdt_update_margin(margin); 303 ar7_wdt_update_margin(margin);
322 304
323 rc = register_reboot_notifier(&ar7_wdt_notifier);
324 if (rc) {
325 printk(KERN_ERR DRVNAME
326 ": unable to register reboot notifier\n");
327 goto out_alloc;
328 }
329
330 rc = misc_register(&ar7_wdt_miscdev); 305 rc = misc_register(&ar7_wdt_miscdev);
331 if (rc) { 306 if (rc) {
332 printk(KERN_ERR DRVNAME ": unable to register misc device\n"); 307 printk(KERN_ERR DRVNAME ": unable to register misc device\n");
333 goto out_register; 308 goto out_alloc;
334 } 309 }
335 goto out; 310 goto out;
336 311
337out_register:
338 unregister_reboot_notifier(&ar7_wdt_notifier);
339out_alloc: 312out_alloc:
340 iounmap(ar7_wdt); 313 iounmap(ar7_wdt);
341 release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt)); 314out_mem_region:
315 release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
342out: 316out:
343 return rc; 317 return rc;
344} 318}
345 319
346static void __exit ar7_wdt_cleanup(void) 320static int __devexit ar7_wdt_remove(struct platform_device *pdev)
347{ 321{
348 misc_deregister(&ar7_wdt_miscdev); 322 misc_deregister(&ar7_wdt_miscdev);
349 unregister_reboot_notifier(&ar7_wdt_notifier);
350 iounmap(ar7_wdt); 323 iounmap(ar7_wdt);
351 release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt)); 324 release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
325
326 return 0;
327}
328
329static void ar7_wdt_shutdown(struct platform_device *pdev)
330{
331 if (!nowayout)
332 ar7_wdt_disable_wdt();
333}
334
335static struct platform_driver ar7_wdt_driver = {
336 .probe = ar7_wdt_probe,
337 .remove = __devexit_p(ar7_wdt_remove),
338 .shutdown = ar7_wdt_shutdown,
339 .driver = {
340 .owner = THIS_MODULE,
341 .name = "ar7_wdt",
342 },
343};
344
345static int __init ar7_wdt_init(void)
346{
347 return platform_driver_register(&ar7_wdt_driver);
348}
349
350static void __exit ar7_wdt_cleanup(void)
351{
352 platform_driver_unregister(&ar7_wdt_driver);
352} 353}
353 354
354module_init(ar7_wdt_init); 355module_init(ar7_wdt_init);
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 225398fd5049..e8380ef65c1c 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -22,6 +22,8 @@
22 22
23#include <asm/reg_booke.h> 23#include <asm/reg_booke.h>
24#include <asm/system.h> 24#include <asm/system.h>
25#include <asm/time.h>
26#include <asm/div64.h>
25 27
26/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. 28/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
27 * Also, the wdt_period sets the watchdog timer period timeout. 29 * Also, the wdt_period sets the watchdog timer period timeout.
@@ -32,7 +34,7 @@
32 */ 34 */
33 35
34#ifdef CONFIG_FSL_BOOKE 36#ifdef CONFIG_FSL_BOOKE
35#define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */ 37#define WDT_PERIOD_DEFAULT 38 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
36#else 38#else
37#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */ 39#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */
38#endif /* for timing information */ 40#endif /* for timing information */
@@ -41,7 +43,7 @@ u32 booke_wdt_enabled;
41u32 booke_wdt_period = WDT_PERIOD_DEFAULT; 43u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
42 44
43#ifdef CONFIG_FSL_BOOKE 45#ifdef CONFIG_FSL_BOOKE
44#define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) 46#define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
45#define WDTP_MASK (WDTP(0)) 47#define WDTP_MASK (WDTP(0))
46#else 48#else
47#define WDTP(x) (TCR_WP(x)) 49#define WDTP(x) (TCR_WP(x))
@@ -50,6 +52,45 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
50 52
51static DEFINE_SPINLOCK(booke_wdt_lock); 53static DEFINE_SPINLOCK(booke_wdt_lock);
52 54
55/* For the specified period, determine the number of seconds
56 * corresponding to the reset time. There will be a watchdog
57 * exception at approximately 3/5 of this time.
58 *
59 * The formula to calculate this is given by:
60 * 2.5 * (2^(63-period+1)) / timebase_freq
61 *
62 * In order to simplify things, we assume that period is
63 * at least 1. This will still result in a very long timeout.
64 */
65static unsigned long long period_to_sec(unsigned int period)
66{
67 unsigned long long tmp = 1ULL << (64 - period);
68 unsigned long tmp2 = ppc_tb_freq;
69
70 /* tmp may be a very large number and we don't want to overflow,
71 * so divide the timebase freq instead of multiplying tmp
72 */
73 tmp2 = tmp2 / 5 * 2;
74
75 do_div(tmp, tmp2);
76 return tmp;
77}
78
79/*
80 * This procedure will find the highest period which will give a timeout
81 * greater than the one required. e.g. for a bus speed of 66666666 and
82 * and a parameter of 2 secs, then this procedure will return a value of 38.
83 */
84static unsigned int sec_to_period(unsigned int secs)
85{
86 unsigned int period;
87 for (period = 63; period > 0; period--) {
88 if (period_to_sec(period) >= secs)
89 return period;
90 }
91 return 0;
92}
93
53static void __booke_wdt_ping(void *data) 94static void __booke_wdt_ping(void *data)
54{ 95{
55 mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); 96 mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
@@ -93,7 +134,7 @@ static long booke_wdt_ioctl(struct file *file,
93 134
94 switch (cmd) { 135 switch (cmd) {
95 case WDIOC_GETSUPPORT: 136 case WDIOC_GETSUPPORT:
96 if (copy_to_user(arg, &ident, sizeof(struct watchdog_info))) 137 if (copy_to_user((void *)arg, &ident, sizeof(ident)))
97 return -EFAULT; 138 return -EFAULT;
98 case WDIOC_GETSTATUS: 139 case WDIOC_GETSTATUS:
99 return put_user(ident.options, p); 140 return put_user(ident.options, p);
@@ -115,8 +156,16 @@ static long booke_wdt_ioctl(struct file *file,
115 booke_wdt_ping(); 156 booke_wdt_ping();
116 return 0; 157 return 0;
117 case WDIOC_SETTIMEOUT: 158 case WDIOC_SETTIMEOUT:
118 if (get_user(booke_wdt_period, p)) 159 if (get_user(tmp, p))
119 return -EFAULT; 160 return -EFAULT;
161#ifdef CONFIG_FSL_BOOKE
162 /* period of 1 gives the largest possible timeout */
163 if (tmp > period_to_sec(1))
164 return -EINVAL;
165 booke_wdt_period = sec_to_period(tmp);
166#else
167 booke_wdt_period = tmp;
168#endif
120 mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) | 169 mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
121 WDTP(booke_wdt_period)); 170 WDTP(booke_wdt_period));
122 return 0; 171 return 0;
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index aec7cefdef21..381026c0bd7b 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -110,7 +110,7 @@ static void coh901327_enable(u16 timeout)
110 * Wait 3 32 kHz cycles for it to take effect 110 * Wait 3 32 kHz cycles for it to take effect
111 */ 111 */
112 freq = clk_get_rate(clk); 112 freq = clk_get_rate(clk);
113 delay_ns = (1000000000 + freq - 1) / freq; /* Freq to ns and round up */ 113 delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */
114 delay_ns = 3 * delay_ns; /* Wait 3 cycles */ 114 delay_ns = 3 * delay_ns; /* Wait 3 cycles */
115 ndelay(delay_ns); 115 ndelay(delay_ns);
116 /* Enable the watchdog interrupt */ 116 /* Enable the watchdog interrupt */
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index 83e22e7ea4a2..9d7520fa9e9c 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -25,6 +25,7 @@
25#include <linux/uaccess.h> 25#include <linux/uaccess.h>
26#include <linux/io.h> 26#include <linux/io.h>
27#include <linux/device.h> 27#include <linux/device.h>
28#include <linux/clk.h>
28 29
29#define MODULE_NAME "DAVINCI-WDT: " 30#define MODULE_NAME "DAVINCI-WDT: "
30 31
@@ -69,6 +70,7 @@ static unsigned long wdt_status;
69 70
70static struct resource *wdt_mem; 71static struct resource *wdt_mem;
71static void __iomem *wdt_base; 72static void __iomem *wdt_base;
73struct clk *wdt_clk;
72 74
73static void wdt_service(void) 75static void wdt_service(void)
74{ 76{
@@ -86,6 +88,9 @@ static void wdt_enable(void)
86{ 88{
87 u32 tgcr; 89 u32 tgcr;
88 u32 timer_margin; 90 u32 timer_margin;
91 unsigned long wdt_freq;
92
93 wdt_freq = clk_get_rate(wdt_clk);
89 94
90 spin_lock(&io_lock); 95 spin_lock(&io_lock);
91 96
@@ -99,9 +104,9 @@ static void wdt_enable(void)
99 iowrite32(0, wdt_base + TIM12); 104 iowrite32(0, wdt_base + TIM12);
100 iowrite32(0, wdt_base + TIM34); 105 iowrite32(0, wdt_base + TIM34);
101 /* set timeout period */ 106 /* set timeout period */
102 timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff); 107 timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
103 iowrite32(timer_margin, wdt_base + PRD12); 108 iowrite32(timer_margin, wdt_base + PRD12);
104 timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32); 109 timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
105 iowrite32(timer_margin, wdt_base + PRD34); 110 iowrite32(timer_margin, wdt_base + PRD34);
106 /* enable run continuously */ 111 /* enable run continuously */
107 iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR); 112 iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
@@ -199,6 +204,12 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev)
199 struct resource *res; 204 struct resource *res;
200 struct device *dev = &pdev->dev; 205 struct device *dev = &pdev->dev;
201 206
207 wdt_clk = clk_get(dev, NULL);
208 if (WARN_ON(IS_ERR(wdt_clk)))
209 return PTR_ERR(wdt_clk);
210
211 clk_enable(wdt_clk);
212
202 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) 213 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
203 heartbeat = DEFAULT_HEARTBEAT; 214 heartbeat = DEFAULT_HEARTBEAT;
204 215
@@ -245,6 +256,10 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev)
245 kfree(wdt_mem); 256 kfree(wdt_mem);
246 wdt_mem = NULL; 257 wdt_mem = NULL;
247 } 258 }
259
260 clk_disable(wdt_clk);
261 clk_put(wdt_clk);
262
248 return 0; 263 return 0;
249} 264}
250 265
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
index 0c9059676690..aef94789019f 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,
139 139
140 switch (cmd) { 140 switch (cmd) {
141 case WDIOC_GETSUPPORT: 141 case WDIOC_GETSUPPORT:
142 if (copy_to_user(argp, &ident, sizeof ident)) 142 if (copy_to_user(argp, &ident, sizeof(ident)))
143 ret = -EFAULT; 143 ret = -EFAULT;
144 else 144 else
145 ret = 0; 145 ret = 0;
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
new file mode 100644
index 000000000000..adefe3a9d510
--- /dev/null
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -0,0 +1,353 @@
1/*
2 * Copyright (c) 2009 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#include <linux/bitops.h>
13#include <linux/errno.h>
14#include <linux/fs.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/clk.h>
18#include <linux/kernel.h>
19#include <linux/miscdevice.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/platform_device.h>
23#include <linux/interrupt.h>
24#include <linux/types.h>
25#include <linux/watchdog.h>
26#include <linux/uaccess.h>
27
28#define REG_WTCR 0x1c
29#define WTCLK (0x01 << 10)
30#define WTE (0x01 << 7) /*wdt enable*/
31#define WTIS (0x03 << 4)
32#define WTIF (0x01 << 3)
33#define WTRF (0x01 << 2)
34#define WTRE (0x01 << 1)
35#define WTR (0x01 << 0)
36/*
37 * The watchdog time interval can be calculated via following formula:
38 * WTIS real time interval (formula)
39 * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
40 * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
41 * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
42 * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
43 *
44 * The external crystal freq is 15Mhz in the nuc900 evaluation board.
45 * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
46 * 0x03 = +- 16.92 seconds..
47 */
48#define WDT_HW_TIMEOUT 0x02
49#define WDT_TIMEOUT (HZ/2)
50#define WDT_HEARTBEAT 15
51
52static int heartbeat = WDT_HEARTBEAT;
53module_param(heartbeat, int, 0);
54MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
55 "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
56
57static int nowayout = WATCHDOG_NOWAYOUT;
58module_param(nowayout, int, 0);
59MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
60 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
61
62struct nuc900_wdt {
63 struct resource *res;
64 struct clk *wdt_clock;
65 struct platform_device *pdev;
66 void __iomem *wdt_base;
67 char expect_close;
68 struct timer_list timer;
69 spinlock_t wdt_lock;
70 unsigned long next_heartbeat;
71};
72
73static unsigned long nuc900wdt_busy;
74struct nuc900_wdt *nuc900_wdt;
75
76static inline void nuc900_wdt_keepalive(void)
77{
78 unsigned int val;
79
80 spin_lock(&nuc900_wdt->wdt_lock);
81
82 val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
83 val |= (WTR | WTIF);
84 __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
85
86 spin_unlock(&nuc900_wdt->wdt_lock);
87}
88
89static inline void nuc900_wdt_start(void)
90{
91 unsigned int val;
92
93 spin_lock(&nuc900_wdt->wdt_lock);
94
95 val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
96 val |= (WTRE | WTE | WTR | WTCLK | WTIF);
97 val &= ~WTIS;
98 val |= (WDT_HW_TIMEOUT << 0x04);
99 __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
100
101 spin_unlock(&nuc900_wdt->wdt_lock);
102
103 nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
104 mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
105}
106
107static inline void nuc900_wdt_stop(void)
108{
109 unsigned int val;
110
111 del_timer(&nuc900_wdt->timer);
112
113 spin_lock(&nuc900_wdt->wdt_lock);
114
115 val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
116 val &= ~WTE;
117 __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
118
119 spin_unlock(&nuc900_wdt->wdt_lock);
120}
121
122static inline void nuc900_wdt_ping(void)
123{
124 nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
125}
126
127static int nuc900_wdt_open(struct inode *inode, struct file *file)
128{
129
130 if (test_and_set_bit(0, &nuc900wdt_busy))
131 return -EBUSY;
132
133 nuc900_wdt_start();
134
135 return nonseekable_open(inode, file);
136}
137
138static int nuc900_wdt_close(struct inode *inode, struct file *file)
139{
140 if (nuc900_wdt->expect_close == 42)
141 nuc900_wdt_stop();
142 else {
143 dev_crit(&nuc900_wdt->pdev->dev,
144 "Unexpected close, not stopping watchdog!\n");
145 nuc900_wdt_ping();
146 }
147
148 nuc900_wdt->expect_close = 0;
149 clear_bit(0, &nuc900wdt_busy);
150 return 0;
151}
152
153static const struct watchdog_info nuc900_wdt_info = {
154 .identity = "nuc900 watchdog",
155 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
156 WDIOF_MAGICCLOSE,
157};
158
159static long nuc900_wdt_ioctl(struct file *file,
160 unsigned int cmd, unsigned long arg)
161{
162 void __user *argp = (void __user *)arg;
163 int __user *p = argp;
164 int new_value;
165
166 switch (cmd) {
167 case WDIOC_GETSUPPORT:
168 return copy_to_user(argp, &nuc900_wdt_info,
169 sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
170 case WDIOC_GETSTATUS:
171 case WDIOC_GETBOOTSTATUS:
172 return put_user(0, p);
173
174 case WDIOC_KEEPALIVE:
175 nuc900_wdt_ping();
176 return 0;
177
178 case WDIOC_SETTIMEOUT:
179 if (get_user(new_value, p))
180 return -EFAULT;
181
182 heartbeat = new_value;
183 nuc900_wdt_ping();
184
185 return put_user(new_value, p);
186 case WDIOC_GETTIMEOUT:
187 return put_user(heartbeat, p);
188 default:
189 return -ENOTTY;
190 }
191}
192
193static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
194 size_t len, loff_t *ppos)
195{
196 if (!len)
197 return 0;
198
199 /* Scan for magic character */
200 if (!nowayout) {
201 size_t i;
202
203 nuc900_wdt->expect_close = 0;
204
205 for (i = 0; i < len; i++) {
206 char c;
207 if (get_user(c, data + i))
208 return -EFAULT;
209 if (c == 'V') {
210 nuc900_wdt->expect_close = 42;
211 break;
212 }
213 }
214 }
215
216 nuc900_wdt_ping();
217 return len;
218}
219
220static void nuc900_wdt_timer_ping(unsigned long data)
221{
222 if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
223 nuc900_wdt_keepalive();
224 mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
225 } else
226 dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
227}
228
229static const struct file_operations nuc900wdt_fops = {
230 .owner = THIS_MODULE,
231 .llseek = no_llseek,
232 .unlocked_ioctl = nuc900_wdt_ioctl,
233 .open = nuc900_wdt_open,
234 .release = nuc900_wdt_close,
235 .write = nuc900_wdt_write,
236};
237
238static struct miscdevice nuc900wdt_miscdev = {
239 .minor = WATCHDOG_MINOR,
240 .name = "watchdog",
241 .fops = &nuc900wdt_fops,
242};
243
244static int __devinit nuc900wdt_probe(struct platform_device *pdev)
245{
246 int ret = 0;
247
248 nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
249 if (!nuc900_wdt)
250 return -ENOMEM;
251
252 nuc900_wdt->pdev = pdev;
253
254 spin_lock_init(&nuc900_wdt->wdt_lock);
255
256 nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
257 if (nuc900_wdt->res == NULL) {
258 dev_err(&pdev->dev, "no memory resource specified\n");
259 ret = -ENOENT;
260 goto err_get;
261 }
262
263 if (!request_mem_region(nuc900_wdt->res->start,
264 resource_size(nuc900_wdt->res), pdev->name)) {
265 dev_err(&pdev->dev, "failed to get memory region\n");
266 ret = -ENOENT;
267 goto err_get;
268 }
269
270 nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
271 resource_size(nuc900_wdt->res));
272 if (nuc900_wdt->wdt_base == NULL) {
273 dev_err(&pdev->dev, "failed to ioremap() region\n");
274 ret = -EINVAL;
275 goto err_req;
276 }
277
278 nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
279 if (IS_ERR(nuc900_wdt->wdt_clock)) {
280 dev_err(&pdev->dev, "failed to find watchdog clock source\n");
281 ret = PTR_ERR(nuc900_wdt->wdt_clock);
282 goto err_map;
283 }
284
285 clk_enable(nuc900_wdt->wdt_clock);
286
287 setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
288
289 if (misc_register(&nuc900wdt_miscdev)) {
290 dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
291 WATCHDOG_MINOR, ret);
292 goto err_clk;
293 }
294
295 return 0;
296
297err_clk:
298 clk_disable(nuc900_wdt->wdt_clock);
299 clk_put(nuc900_wdt->wdt_clock);
300err_map:
301 iounmap(nuc900_wdt->wdt_base);
302err_req:
303 release_mem_region(nuc900_wdt->res->start,
304 resource_size(nuc900_wdt->res));
305err_get:
306 kfree(nuc900_wdt);
307 return ret;
308}
309
310static int __devexit nuc900wdt_remove(struct platform_device *pdev)
311{
312 misc_deregister(&nuc900wdt_miscdev);
313
314 clk_disable(nuc900_wdt->wdt_clock);
315 clk_put(nuc900_wdt->wdt_clock);
316
317 iounmap(nuc900_wdt->wdt_base);
318
319 release_mem_region(nuc900_wdt->res->start,
320 resource_size(nuc900_wdt->res));
321
322 kfree(nuc900_wdt);
323
324 return 0;
325}
326
327static struct platform_driver nuc900wdt_driver = {
328 .probe = nuc900wdt_probe,
329 .remove = __devexit_p(nuc900wdt_remove),
330 .driver = {
331 .name = "nuc900-wdt",
332 .owner = THIS_MODULE,
333 },
334};
335
336static int __init nuc900_wdt_init(void)
337{
338 return platform_driver_register(&nuc900wdt_driver);
339}
340
341static void __exit nuc900_wdt_exit(void)
342{
343 platform_driver_unregister(&nuc900wdt_driver);
344}
345
346module_init(nuc900_wdt_init);
347module_exit(nuc900_wdt_exit);
348
349MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
350MODULE_DESCRIPTION("Watchdog driver for NUC900");
351MODULE_LICENSE("GPL");
352MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
353MODULE_ALIAS("platform:nuc900-wdt");
diff --git a/drivers/watchdog/rm9k_wdt.c b/drivers/watchdog/rm9k_wdt.c
index 2e4442642262..bb66958b9433 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,
340 const char *name, unsigned int type) 340 const char *name, unsigned int type)
341{ 341{
342 char buf[80]; 342 char buf[80];
343 if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf) 343 if (snprintf(buf, sizeof(buf), "%s_0", name) >= sizeof(buf))
344 return NULL; 344 return NULL;
345 return platform_get_resource_byname(pdv, type, buf); 345 return platform_get_resource_byname(pdv, type, buf);
346} 346}
diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c
new file mode 100644
index 000000000000..852ca1977917
--- /dev/null
+++ b/drivers/watchdog/sbc_fitpc2_wdt.c
@@ -0,0 +1,267 @@
1/*
2 * Watchdog driver for SBC-FITPC2 board
3 *
4 * Author: Denis Turischev <denis@compulab.co.il>
5 *
6 * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
14
15#include <linux/module.h>
16#include <linux/types.h>
17#include <linux/miscdevice.h>
18#include <linux/watchdog.h>
19#include <linux/ioport.h>
20#include <linux/delay.h>
21#include <linux/fs.h>
22#include <linux/init.h>
23#include <linux/moduleparam.h>
24#include <linux/dmi.h>
25#include <linux/io.h>
26#include <linux/uaccess.h>
27
28#include <asm/system.h>
29
30static int nowayout = WATCHDOG_NOWAYOUT;
31static unsigned int margin = 60; /* (secs) Default is 1 minute */
32static unsigned long wdt_status;
33static DEFINE_SPINLOCK(wdt_lock);
34
35#define WDT_IN_USE 0
36#define WDT_OK_TO_CLOSE 1
37
38#define COMMAND_PORT 0x4c
39#define DATA_PORT 0x48
40
41#define IFACE_ON_COMMAND 1
42#define REBOOT_COMMAND 2
43
44#define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
45
46static void wdt_send_data(unsigned char command, unsigned char data)
47{
48 outb(command, COMMAND_PORT);
49 mdelay(100);
50 outb(data, DATA_PORT);
51 mdelay(200);
52}
53
54static void wdt_enable(void)
55{
56 spin_lock(&wdt_lock);
57 wdt_send_data(IFACE_ON_COMMAND, 1);
58 wdt_send_data(REBOOT_COMMAND, margin);
59 spin_unlock(&wdt_lock);
60}
61
62static void wdt_disable(void)
63{
64 spin_lock(&wdt_lock);
65 wdt_send_data(IFACE_ON_COMMAND, 0);
66 wdt_send_data(REBOOT_COMMAND, 0);
67 spin_unlock(&wdt_lock);
68}
69
70static int fitpc2_wdt_open(struct inode *inode, struct file *file)
71{
72 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
73 return -EBUSY;
74
75 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
76
77 wdt_enable();
78
79 return nonseekable_open(inode, file);
80}
81
82static ssize_t fitpc2_wdt_write(struct file *file, const char *data,
83 size_t len, loff_t *ppos)
84{
85 size_t i;
86
87 if (!len)
88 return 0;
89
90 if (nowayout) {
91 len = 0;
92 goto out;
93 }
94
95 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
96
97 for (i = 0; i != len; i++) {
98 char c;
99
100 if (get_user(c, data + i))
101 return -EFAULT;
102
103 if (c == 'V')
104 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
105 }
106
107out:
108 wdt_enable();
109
110 return len;
111}
112
113
114static struct watchdog_info ident = {
115 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
116 WDIOF_KEEPALIVEPING,
117 .identity = WATCHDOG_NAME,
118};
119
120
121static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd,
122 unsigned long arg)
123{
124 int ret = -ENOTTY;
125 int time;
126
127 switch (cmd) {
128 case WDIOC_GETSUPPORT:
129 ret = copy_to_user((struct watchdog_info *)arg, &ident,
130 sizeof(ident)) ? -EFAULT : 0;
131 break;
132
133 case WDIOC_GETSTATUS:
134 ret = put_user(0, (int *)arg);
135 break;
136
137 case WDIOC_GETBOOTSTATUS:
138 ret = put_user(0, (int *)arg);
139 break;
140
141 case WDIOC_KEEPALIVE:
142 wdt_enable();
143 ret = 0;
144 break;
145
146 case WDIOC_SETTIMEOUT:
147 ret = get_user(time, (int *)arg);
148 if (ret)
149 break;
150
151 if (time < 31 || time > 255) {
152 ret = -EINVAL;
153 break;
154 }
155
156 margin = time;
157 wdt_enable();
158 /* Fall through */
159
160 case WDIOC_GETTIMEOUT:
161 ret = put_user(margin, (int *)arg);
162 break;
163 }
164
165 return ret;
166}
167
168static int fitpc2_wdt_release(struct inode *inode, struct file *file)
169{
170 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
171 wdt_disable();
172 pr_info("Device disabled\n");
173 } else {
174 pr_warning("Device closed unexpectedly -"
175 " timer will not stop\n");
176 wdt_enable();
177 }
178
179 clear_bit(WDT_IN_USE, &wdt_status);
180 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
181
182 return 0;
183}
184
185
186static const struct file_operations fitpc2_wdt_fops = {
187 .owner = THIS_MODULE,
188 .llseek = no_llseek,
189 .write = fitpc2_wdt_write,
190 .unlocked_ioctl = fitpc2_wdt_ioctl,
191 .open = fitpc2_wdt_open,
192 .release = fitpc2_wdt_release,
193};
194
195static struct miscdevice fitpc2_wdt_miscdev = {
196 .minor = WATCHDOG_MINOR,
197 .name = "watchdog",
198 .fops = &fitpc2_wdt_fops,
199};
200
201static int __init fitpc2_wdt_init(void)
202{
203 int err;
204
205 if (strcmp("SBC-FITPC2", dmi_get_system_info(DMI_BOARD_NAME))) {
206 pr_info("board name is: %s. Should be SBC-FITPC2\n",
207 dmi_get_system_info(DMI_BOARD_NAME));
208 return -ENODEV;
209 }
210
211 if (!request_region(COMMAND_PORT, 1, WATCHDOG_NAME)) {
212 pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT);
213 return -EIO;
214 }
215
216 if (!request_region(DATA_PORT, 1, WATCHDOG_NAME)) {
217 pr_err("I/O address 0x%04x already in use\n", DATA_PORT);
218 err = -EIO;
219 goto err_data_port;
220 }
221
222 if (margin < 31 || margin > 255) {
223 pr_err("margin must be in range 31 - 255"
224 " seconds, you tried to set %d\n", margin);
225 err = -EINVAL;
226 goto err_margin;
227 }
228
229 err = misc_register(&fitpc2_wdt_miscdev);
230 if (!err) {
231 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
232 WATCHDOG_MINOR, err);
233 goto err_margin;
234 }
235
236 return 0;
237
238err_margin:
239 release_region(DATA_PORT, 1);
240err_data_port:
241 release_region(COMMAND_PORT, 1);
242
243 return err;
244}
245
246static void __exit fitpc2_wdt_exit(void)
247{
248 misc_deregister(&fitpc2_wdt_miscdev);
249 release_region(DATA_PORT, 1);
250 release_region(COMMAND_PORT, 1);
251}
252
253module_init(fitpc2_wdt_init);
254module_exit(fitpc2_wdt_exit);
255
256MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
257MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
258
259module_param(margin, int, 0);
260MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
261
262module_param(nowayout, int, 0);
263MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
264
265MODULE_LICENSE("GPL");
266MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
267
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index b5e19c1820a2..c01daca8405a 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,
197 197
198 switch (cmd) { 198 switch (cmd) {
199 case WDIOC_GETSUPPORT: 199 case WDIOC_GETSUPPORT:
200 if (copy_to_user(argp, &ident, sizeof ident)) 200 if (copy_to_user(argp, &ident, sizeof(ident)))
201 return -EFAULT; 201 return -EFAULT;
202 return 0; 202 return 0;
203 203
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index 7a1bdc7c95a9..f368dd87083a 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -80,7 +80,7 @@ static unsigned long open_lock;
80static DEFINE_SPINLOCK(wdtpci_lock); 80static DEFINE_SPINLOCK(wdtpci_lock);
81static char expect_close; 81static char expect_close;
82 82
83static int io; 83static resource_size_t io;
84static int irq; 84static int irq;
85 85
86/* Default timeout */ 86/* Default timeout */
@@ -647,14 +647,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
647 goto out_pci; 647 goto out_pci;
648 } 648 }
649 649
650 irq = dev->irq; 650 if (pci_request_region(dev, 2, "wdt_pci")) {
651 io = pci_resource_start(dev, 2); 651 printk(KERN_ERR PFX "I/O address 0x%llx already in use\n",
652 652 (unsigned long long)pci_resource_start(dev, 2));
653 if (request_region(io, 16, "wdt_pci") == NULL) {
654 printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", io);
655 goto out_pci; 653 goto out_pci;
656 } 654 }
657 655
656 irq = dev->irq;
657 io = pci_resource_start(dev, 2);
658
658 if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED, 659 if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED,
659 "wdt_pci", &wdtpci_miscdev)) { 660 "wdt_pci", &wdtpci_miscdev)) {
660 printk(KERN_ERR PFX "IRQ %d is not free\n", irq); 661 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,
662 } 663 }
663 664
664 printk(KERN_INFO 665 printk(KERN_INFO
665 "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n", 666 "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
666 io, irq); 667 (unsigned long long)io, irq);
667 668
668 /* Check that the heartbeat value is within its range; 669 /* Check that the heartbeat value is within its range;
669 if not reset to the default */ 670 if not reset to the default */
@@ -717,7 +718,7 @@ out_rbt:
717out_irq: 718out_irq:
718 free_irq(irq, &wdtpci_miscdev); 719 free_irq(irq, &wdtpci_miscdev);
719out_reg: 720out_reg:
720 release_region(io, 16); 721 pci_release_region(dev, 2);
721out_pci: 722out_pci:
722 pci_disable_device(dev); 723 pci_disable_device(dev);
723 goto out; 724 goto out;
@@ -733,7 +734,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
733 misc_deregister(&temp_miscdev); 734 misc_deregister(&temp_miscdev);
734 unregister_reboot_notifier(&wdtpci_notifier); 735 unregister_reboot_notifier(&wdtpci_notifier);
735 free_irq(irq, &wdtpci_miscdev); 736 free_irq(irq, &wdtpci_miscdev);
736 release_region(io, 16); 737 pci_release_region(pdev, 2);
737 pci_disable_device(pdev); 738 pci_disable_device(pdev);
738 dev_count--; 739 dev_count--;
739} 740}
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
new file mode 100644
index 000000000000..775bcd807f31
--- /dev/null
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -0,0 +1,441 @@
1/*
2 * Watchdog driver for the wm831x PMICs
3 *
4 * Copyright (C) 2009 Wolfson Microelectronics
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation
9 */
10
11#include <linux/module.h>
12#include <linux/moduleparam.h>
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/fs.h>
16#include <linux/miscdevice.h>
17#include <linux/platform_device.h>
18#include <linux/watchdog.h>
19#include <linux/uaccess.h>
20#include <linux/gpio.h>
21
22#include <linux/mfd/wm831x/core.h>
23#include <linux/mfd/wm831x/pdata.h>
24#include <linux/mfd/wm831x/watchdog.h>
25
26static int nowayout = WATCHDOG_NOWAYOUT;
27module_param(nowayout, int, 0);
28MODULE_PARM_DESC(nowayout,
29 "Watchdog cannot be stopped once started (default="
30 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
31
32static unsigned long wm831x_wdt_users;
33static struct miscdevice wm831x_wdt_miscdev;
34static int wm831x_wdt_expect_close;
35static DEFINE_MUTEX(wdt_mutex);
36static struct wm831x *wm831x;
37static unsigned int update_gpio;
38static unsigned int update_state;
39
40/* We can't use the sub-second values here but they're included
41 * for completeness. */
42static struct {
43 int time; /* Seconds */
44 u16 val; /* WDOG_TO value */
45} wm831x_wdt_cfgs[] = {
46 { 1, 2 },
47 { 2, 3 },
48 { 4, 4 },
49 { 8, 5 },
50 { 16, 6 },
51 { 32, 7 },
52 { 33, 7 }, /* Actually 32.768s so include both, others round down */
53};
54
55static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value)
56{
57 int ret;
58
59 mutex_lock(&wdt_mutex);
60
61 ret = wm831x_reg_unlock(wm831x);
62 if (ret == 0) {
63 ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
64 WM831X_WDOG_TO_MASK, value);
65 wm831x_reg_lock(wm831x);
66 } else {
67 dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
68 ret);
69 }
70
71 mutex_unlock(&wdt_mutex);
72
73 return ret;
74}
75
76static int wm831x_wdt_start(struct wm831x *wm831x)
77{
78 int ret;
79
80 mutex_lock(&wdt_mutex);
81
82 ret = wm831x_reg_unlock(wm831x);
83 if (ret == 0) {
84 ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
85 WM831X_WDOG_ENA, WM831X_WDOG_ENA);
86 wm831x_reg_lock(wm831x);
87 } else {
88 dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
89 ret);
90 }
91
92 mutex_unlock(&wdt_mutex);
93
94 return ret;
95}
96
97static int wm831x_wdt_stop(struct wm831x *wm831x)
98{
99 int ret;
100
101 mutex_lock(&wdt_mutex);
102
103 ret = wm831x_reg_unlock(wm831x);
104 if (ret == 0) {
105 ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
106 WM831X_WDOG_ENA, 0);
107 wm831x_reg_lock(wm831x);
108 } else {
109 dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
110 ret);
111 }
112
113 mutex_unlock(&wdt_mutex);
114
115 return ret;
116}
117
118static int wm831x_wdt_kick(struct wm831x *wm831x)
119{
120 int ret;
121 u16 reg;
122
123 mutex_lock(&wdt_mutex);
124
125 if (update_gpio) {
126 gpio_set_value_cansleep(update_gpio, update_state);
127 update_state = !update_state;
128 ret = 0;
129 goto out;
130 }
131
132
133 reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
134
135 if (!(reg & WM831X_WDOG_RST_SRC)) {
136 dev_err(wm831x->dev, "Hardware watchdog update unsupported\n");
137 ret = -EINVAL;
138 goto out;
139 }
140
141 reg |= WM831X_WDOG_RESET;
142
143 ret = wm831x_reg_unlock(wm831x);
144 if (ret == 0) {
145 ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
146 wm831x_reg_lock(wm831x);
147 } else {
148 dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
149 ret);
150 }
151
152out:
153 mutex_unlock(&wdt_mutex);
154
155 return ret;
156}
157
158static int wm831x_wdt_open(struct inode *inode, struct file *file)
159{
160 int ret;
161
162 if (!wm831x)
163 return -ENODEV;
164
165 if (test_and_set_bit(0, &wm831x_wdt_users))
166 return -EBUSY;
167
168 ret = wm831x_wdt_start(wm831x);
169 if (ret != 0)
170 return ret;
171
172 return nonseekable_open(inode, file);
173}
174
175static int wm831x_wdt_release(struct inode *inode, struct file *file)
176{
177 if (wm831x_wdt_expect_close)
178 wm831x_wdt_stop(wm831x);
179 else {
180 dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n");
181 wm831x_wdt_kick(wm831x);
182 }
183
184 clear_bit(0, &wm831x_wdt_users);
185
186 return 0;
187}
188
189static ssize_t wm831x_wdt_write(struct file *file,
190 const char __user *data, size_t count,
191 loff_t *ppos)
192{
193 size_t i;
194
195 if (count) {
196 wm831x_wdt_kick(wm831x);
197
198 if (!nowayout) {
199 /* In case it was set long ago */
200 wm831x_wdt_expect_close = 0;
201
202 /* scan to see whether or not we got the magic
203 character */
204 for (i = 0; i != count; i++) {
205 char c;
206 if (get_user(c, data + i))
207 return -EFAULT;
208 if (c == 'V')
209 wm831x_wdt_expect_close = 42;
210 }
211 }
212 }
213 return count;
214}
215
216static struct watchdog_info ident = {
217 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
218 .identity = "WM831x Watchdog",
219};
220
221static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd,
222 unsigned long arg)
223{
224 int ret = -ENOTTY, time, i;
225 void __user *argp = (void __user *)arg;
226 int __user *p = argp;
227 u16 reg;
228
229 switch (cmd) {
230 case WDIOC_GETSUPPORT:
231 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
232 break;
233
234 case WDIOC_GETSTATUS:
235 case WDIOC_GETBOOTSTATUS:
236 ret = put_user(0, p);
237 break;
238
239 case WDIOC_SETOPTIONS:
240 {
241 int options;
242
243 if (get_user(options, p))
244 return -EFAULT;
245
246 ret = -EINVAL;
247
248 /* Setting both simultaneously means at least one must fail */
249 if (options == WDIOS_DISABLECARD)
250 ret = wm831x_wdt_start(wm831x);
251
252 if (options == WDIOS_ENABLECARD)
253 ret = wm831x_wdt_stop(wm831x);
254 break;
255 }
256
257 case WDIOC_KEEPALIVE:
258 ret = wm831x_wdt_kick(wm831x);
259 break;
260
261 case WDIOC_SETTIMEOUT:
262 ret = get_user(time, p);
263 if (ret)
264 break;
265
266 if (time == 0) {
267 if (nowayout)
268 ret = -EINVAL;
269 else
270 wm831x_wdt_stop(wm831x);
271 break;
272 }
273
274 for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
275 if (wm831x_wdt_cfgs[i].time == time)
276 break;
277 if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
278 ret = -EINVAL;
279 else
280 ret = wm831x_wdt_set_timeout(wm831x,
281 wm831x_wdt_cfgs[i].val);
282 break;
283
284 case WDIOC_GETTIMEOUT:
285 reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
286 reg &= WM831X_WDOG_TO_MASK;
287 for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
288 if (wm831x_wdt_cfgs[i].val == reg)
289 break;
290 if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) {
291 dev_warn(wm831x->dev,
292 "Unknown watchdog configuration: %x\n", reg);
293 ret = -EINVAL;
294 } else
295 ret = put_user(wm831x_wdt_cfgs[i].time, p);
296
297 }
298
299 return ret;
300}
301
302static const struct file_operations wm831x_wdt_fops = {
303 .owner = THIS_MODULE,
304 .llseek = no_llseek,
305 .write = wm831x_wdt_write,
306 .unlocked_ioctl = wm831x_wdt_ioctl,
307 .open = wm831x_wdt_open,
308 .release = wm831x_wdt_release,
309};
310
311static struct miscdevice wm831x_wdt_miscdev = {
312 .minor = WATCHDOG_MINOR,
313 .name = "watchdog",
314 .fops = &wm831x_wdt_fops,
315};
316
317static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
318{
319 struct wm831x_pdata *chip_pdata;
320 struct wm831x_watchdog_pdata *pdata;
321 int reg, ret;
322
323 wm831x = dev_get_drvdata(pdev->dev.parent);
324
325 ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
326 if (ret < 0) {
327 dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
328 ret);
329 goto err;
330 }
331 reg = ret;
332
333 if (reg & WM831X_WDOG_DEBUG)
334 dev_warn(wm831x->dev, "Watchdog is paused\n");
335
336 /* Apply any configuration */
337 if (pdev->dev.parent->platform_data) {
338 chip_pdata = pdev->dev.parent->platform_data;
339 pdata = chip_pdata->watchdog;
340 } else {
341 pdata = NULL;
342 }
343
344 if (pdata) {
345 reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
346 WM831X_WDOG_RST_SRC);
347
348 reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
349 reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
350 reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
351
352 if (pdata->update_gpio) {
353 ret = gpio_request(pdata->update_gpio,
354 "Watchdog update");
355 if (ret < 0) {
356 dev_err(wm831x->dev,
357 "Failed to request update GPIO: %d\n",
358 ret);
359 goto err;
360 }
361
362 ret = gpio_direction_output(pdata->update_gpio, 0);
363 if (ret != 0) {
364 dev_err(wm831x->dev,
365 "gpio_direction_output returned: %d\n",
366 ret);
367 goto err_gpio;
368 }
369
370 update_gpio = pdata->update_gpio;
371
372 /* Make sure the watchdog takes hardware updates */
373 reg |= WM831X_WDOG_RST_SRC;
374 }
375
376 ret = wm831x_reg_unlock(wm831x);
377 if (ret == 0) {
378 ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
379 wm831x_reg_lock(wm831x);
380 } else {
381 dev_err(wm831x->dev,
382 "Failed to unlock security key: %d\n", ret);
383 goto err_gpio;
384 }
385 }
386
387 wm831x_wdt_miscdev.parent = &pdev->dev;
388
389 ret = misc_register(&wm831x_wdt_miscdev);
390 if (ret != 0) {
391 dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret);
392 goto err_gpio;
393 }
394
395 return 0;
396
397err_gpio:
398 if (update_gpio) {
399 gpio_free(update_gpio);
400 update_gpio = 0;
401 }
402err:
403 return ret;
404}
405
406static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
407{
408 if (update_gpio) {
409 gpio_free(update_gpio);
410 update_gpio = 0;
411 }
412
413 misc_deregister(&wm831x_wdt_miscdev);
414
415 return 0;
416}
417
418static struct platform_driver wm831x_wdt_driver = {
419 .probe = wm831x_wdt_probe,
420 .remove = __devexit_p(wm831x_wdt_remove),
421 .driver = {
422 .name = "wm831x-watchdog",
423 },
424};
425
426static int __init wm831x_wdt_init(void)
427{
428 return platform_driver_register(&wm831x_wdt_driver);
429}
430module_init(wm831x_wdt_init);
431
432static void __exit wm831x_wdt_exit(void)
433{
434 platform_driver_unregister(&wm831x_wdt_driver);
435}
436module_exit(wm831x_wdt_exit);
437
438MODULE_AUTHOR("Mark Brown");
439MODULE_DESCRIPTION("WM831x Watchdog");
440MODULE_LICENSE("GPL");
441MODULE_ALIAS("platform:wm831x-watchdog");
diff --git a/include/linux/mfd/wm831x/watchdog.h b/include/linux/mfd/wm831x/watchdog.h
new file mode 100644
index 000000000000..97a99b52956f
--- /dev/null
+++ b/include/linux/mfd/wm831x/watchdog.h
@@ -0,0 +1,52 @@
1/*
2 * include/linux/mfd/wm831x/watchdog.h -- Watchdog for WM831x
3 *
4 * Copyright 2009 Wolfson Microelectronics PLC.
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#ifndef __MFD_WM831X_WATCHDOG_H__
16#define __MFD_WM831X_WATCHDOG_H__
17
18
19/*
20 * R16388 (0x4004) - Watchdog
21 */
22#define WM831X_WDOG_ENA 0x8000 /* WDOG_ENA */
23#define WM831X_WDOG_ENA_MASK 0x8000 /* WDOG_ENA */
24#define WM831X_WDOG_ENA_SHIFT 15 /* WDOG_ENA */
25#define WM831X_WDOG_ENA_WIDTH 1 /* WDOG_ENA */
26#define WM831X_WDOG_DEBUG 0x4000 /* WDOG_DEBUG */
27#define WM831X_WDOG_DEBUG_MASK 0x4000 /* WDOG_DEBUG */
28#define WM831X_WDOG_DEBUG_SHIFT 14 /* WDOG_DEBUG */
29#define WM831X_WDOG_DEBUG_WIDTH 1 /* WDOG_DEBUG */
30#define WM831X_WDOG_RST_SRC 0x2000 /* WDOG_RST_SRC */
31#define WM831X_WDOG_RST_SRC_MASK 0x2000 /* WDOG_RST_SRC */
32#define WM831X_WDOG_RST_SRC_SHIFT 13 /* WDOG_RST_SRC */
33#define WM831X_WDOG_RST_SRC_WIDTH 1 /* WDOG_RST_SRC */
34#define WM831X_WDOG_SLPENA 0x1000 /* WDOG_SLPENA */
35#define WM831X_WDOG_SLPENA_MASK 0x1000 /* WDOG_SLPENA */
36#define WM831X_WDOG_SLPENA_SHIFT 12 /* WDOG_SLPENA */
37#define WM831X_WDOG_SLPENA_WIDTH 1 /* WDOG_SLPENA */
38#define WM831X_WDOG_RESET 0x0800 /* WDOG_RESET */
39#define WM831X_WDOG_RESET_MASK 0x0800 /* WDOG_RESET */
40#define WM831X_WDOG_RESET_SHIFT 11 /* WDOG_RESET */
41#define WM831X_WDOG_RESET_WIDTH 1 /* WDOG_RESET */
42#define WM831X_WDOG_SECACT_MASK 0x0300 /* WDOG_SECACT - [9:8] */
43#define WM831X_WDOG_SECACT_SHIFT 8 /* WDOG_SECACT - [9:8] */
44#define WM831X_WDOG_SECACT_WIDTH 2 /* WDOG_SECACT - [9:8] */
45#define WM831X_WDOG_PRIMACT_MASK 0x0030 /* WDOG_PRIMACT - [5:4] */
46#define WM831X_WDOG_PRIMACT_SHIFT 4 /* WDOG_PRIMACT - [5:4] */
47#define WM831X_WDOG_PRIMACT_WIDTH 2 /* WDOG_PRIMACT - [5:4] */
48#define WM831X_WDOG_TO_MASK 0x0007 /* WDOG_TO - [2:0] */
49#define WM831X_WDOG_TO_SHIFT 0 /* WDOG_TO - [2:0] */
50#define WM831X_WDOG_TO_WIDTH 3 /* WDOG_TO - [2:0] */
51
52#endif