aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/watchdog/Kconfig43
-rw-r--r--drivers/watchdog/Makefile4
-rw-r--r--drivers/watchdog/at91sam9_wdt.c328
-rw-r--r--drivers/watchdog/it87_wdt.c725
-rw-r--r--drivers/watchdog/omap_wdt.c337
-rw-r--r--drivers/watchdog/omap_wdt.h28
-rw-r--r--drivers/watchdog/orion5x_wdt.c245
-rw-r--r--drivers/watchdog/w83697ug_wdt.c392
8 files changed, 1970 insertions, 132 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c51036716700..1a22fe782a27 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -66,6 +66,13 @@ config AT91RM9200_WATCHDOG
66 Watchdog timer embedded into AT91RM9200 chips. This will reboot your 66 Watchdog timer embedded into AT91RM9200 chips. This will reboot your
67 system when the timeout is reached. 67 system when the timeout is reached.
68 68
69config AT91SAM9X_WATCHDOG
70 tristate "AT91SAM9X watchdog"
71 depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261)
72 help
73 Watchdog timer embedded into AT91SAM9X chips. This will reboot your
74 system when the timeout is reached.
75
69config 21285_WATCHDOG 76config 21285_WATCHDOG
70 tristate "DC21285 watchdog" 77 tristate "DC21285 watchdog"
71 depends on FOOTBRIDGE 78 depends on FOOTBRIDGE
@@ -217,6 +224,15 @@ config DAVINCI_WATCHDOG
217 NOTE: once enabled, this timer cannot be disabled. 224 NOTE: once enabled, this timer cannot be disabled.
218 Say N if you are unsure. 225 Say N if you are unsure.
219 226
227config ORION5X_WATCHDOG
228 tristate "Orion5x watchdog"
229 depends on ARCH_ORION5X
230 help
231 Say Y here if to include support for the watchdog timer
232 in the Orion5x ARM SoCs.
233 To compile this driver as a module, choose M here: the
234 module will be called orion5x_wdt.
235
220# ARM26 Architecture 236# ARM26 Architecture
221 237
222# AVR32 Architecture 238# AVR32 Architecture
@@ -416,6 +432,18 @@ config IT8712F_WDT
416 To compile this driver as a module, choose M here: the 432 To compile this driver as a module, choose M here: the
417 module will be called it8712f_wdt. 433 module will be called it8712f_wdt.
418 434
435config IT87_WDT
436 tristate "IT87 Watchdog Timer"
437 depends on X86 && EXPERIMENTAL
438 ---help---
439 This is the driver for the hardware watchdog on the ITE IT8716,
440 IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog
441 simply watches your kernel to make sure it doesn't freeze, and if
442 it does, it reboots your computer after a certain amount of time.
443
444 To compile this driver as a module, choose M here: the module will
445 be called it87_wdt.
446
419config HP_WATCHDOG 447config HP_WATCHDOG
420 tristate "HP Proliant iLO 2 Hardware Watchdog Timer" 448 tristate "HP Proliant iLO 2 Hardware Watchdog Timer"
421 depends on X86 449 depends on X86
@@ -573,6 +601,21 @@ config W83697HF_WDT
573 601
574 Most people will say N. 602 Most people will say N.
575 603
604config W83697UG_WDT
605 tristate "W83697UG/W83697UF Watchdog Timer"
606 depends on X86
607 ---help---
608 This is the driver for the hardware watchdog on the W83697UG/UF
609 chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
610 This watchdog simply watches your kernel to make sure it doesn't
611 freeze, and if it does, it reboots your computer after a certain
612 amount of time.
613
614 To compile this driver as a module, choose M here: the
615 module will be called w83697ug_wdt.
616
617 Most people will say N.
618
576config W83877F_WDT 619config W83877F_WDT
577 tristate "W83877F (EMACS) Watchdog Timer" 620 tristate "W83877F (EMACS) Watchdog Timer"
578 depends on X86 621 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 6702d2ef0434..e352bbb7630b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
26 26
27# ARM Architecture 27# ARM Architecture
28obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o 28obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
29obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
29obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o 30obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
30obj-$(CONFIG_21285_WATCHDOG) += wdt285.o 31obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
31obj-$(CONFIG_977_WATCHDOG) += wdt977.o 32obj-$(CONFIG_977_WATCHDOG) += wdt977.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
39obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o 40obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
40obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o 41obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
41obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o 42obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
43obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
42 44
43# ARM26 Architecture 45# ARM26 Architecture
44 46
@@ -71,6 +73,7 @@ ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
71obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o 73obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
72endif 74endif
73obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o 75obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
76obj-$(CONFIG_IT87_WDT) += it87_wdt.o
74obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o 77obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
75obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o 78obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
76obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o 79obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
@@ -83,6 +86,7 @@ obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
83obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 86obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
84obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 87obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
85obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o 88obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
89obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
86obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o 90obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
87obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o 91obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
88obj-$(CONFIG_MACHZ_WDT) += machzwd.o 92obj-$(CONFIG_MACHZ_WDT) += machzwd.o
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
new file mode 100644
index 000000000000..b4babfc31586
--- /dev/null
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -0,0 +1,328 @@
1/*
2 * Watchdog driver for Atmel AT91SAM9x processors.
3 *
4 * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
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; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12/*
13 * The Watchdog Timer Mode Register can be only written to once. If the
14 * timeout need to be set from Linux, be sure that the bootstrap or the
15 * bootloader doesn't write to this register.
16 */
17
18#include <linux/errno.h>
19#include <linux/fs.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/miscdevice.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/platform_device.h>
26#include <linux/types.h>
27#include <linux/watchdog.h>
28#include <linux/jiffies.h>
29#include <linux/timer.h>
30#include <linux/bitops.h>
31#include <linux/uaccess.h>
32
33#include <asm/arch/at91_wdt.h>
34
35#define DRV_NAME "AT91SAM9 Watchdog"
36
37/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
38 * use this to convert a watchdog
39 * value from/to milliseconds.
40 */
41#define ms_to_ticks(t) (((t << 8) / 1000) - 1)
42#define ticks_to_ms(t) (((t + 1) * 1000) >> 8)
43
44/* Hardware timeout in seconds */
45#define WDT_HW_TIMEOUT 2
46
47/* Timer heartbeat (500ms) */
48#define WDT_TIMEOUT (HZ/2)
49
50/* User land timeout */
51#define WDT_HEARTBEAT 15
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
62static void at91_ping(unsigned long data);
63
64static struct {
65 unsigned long next_heartbeat; /* the next_heartbeat for the timer */
66 unsigned long open;
67 char expect_close;
68 struct timer_list timer; /* The timer that pings the watchdog */
69} at91wdt_private;
70
71/* ......................................................................... */
72
73
74/*
75 * Reload the watchdog timer. (ie, pat the watchdog)
76 */
77static inline void at91_wdt_reset(void)
78{
79 at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
80}
81
82/*
83 * Timer tick
84 */
85static void at91_ping(unsigned long data)
86{
87 if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
88 (!nowayout && !at91wdt_private.open)) {
89 at91_wdt_reset();
90 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
91 } else
92 printk(KERN_CRIT DRV_NAME": I will reset your machine !\n");
93}
94
95/*
96 * Watchdog device is opened, and watchdog starts running.
97 */
98static int at91_wdt_open(struct inode *inode, struct file *file)
99{
100 if (test_and_set_bit(0, &at91wdt_private.open))
101 return -EBUSY;
102
103 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
104 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
105
106 return nonseekable_open(inode, file);
107}
108
109/*
110 * Close the watchdog device.
111 */
112static int at91_wdt_close(struct inode *inode, struct file *file)
113{
114 clear_bit(0, &at91wdt_private.open);
115
116 /* stop internal ping */
117 if (!at91wdt_private.expect_close)
118 del_timer(&at91wdt_private.timer);
119
120 at91wdt_private.expect_close = 0;
121 return 0;
122}
123
124/*
125 * Set the watchdog time interval in 1/256Hz (write-once)
126 * Counter is 12 bit.
127 */
128static int at91_wdt_settimeout(unsigned int timeout)
129{
130 unsigned int reg;
131 unsigned int mr;
132
133 /* Check if disabled */
134 mr = at91_sys_read(AT91_WDT_MR);
135 if (mr & AT91_WDT_WDDIS) {
136 printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n");
137 return -EIO;
138 }
139
140 /*
141 * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
142 *
143 * Since WDV is a 12-bit counter, the maximum period is
144 * 4096 / 256 = 16 seconds.
145 */
146 reg = AT91_WDT_WDRSTEN /* causes watchdog reset */
147 /* | AT91_WDT_WDRPROC causes processor reset only */
148 | AT91_WDT_WDDBGHLT /* disabled in debug mode */
149 | AT91_WDT_WDD /* restart at any time */
150 | (timeout & AT91_WDT_WDV); /* timer value */
151 at91_sys_write(AT91_WDT_MR, reg);
152
153 return 0;
154}
155
156static const struct watchdog_info at91_wdt_info = {
157 .identity = DRV_NAME,
158 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
159};
160
161/*
162 * Handle commands from user-space.
163 */
164static long at91_wdt_ioctl(struct file *file,
165 unsigned int cmd, unsigned long arg)
166{
167 void __user *argp = (void __user *)arg;
168 int __user *p = argp;
169 int new_value;
170
171 switch (cmd) {
172 case WDIOC_GETSUPPORT:
173 return copy_to_user(argp, &at91_wdt_info,
174 sizeof(at91_wdt_info)) ? -EFAULT : 0;
175
176 case WDIOC_GETSTATUS:
177 case WDIOC_GETBOOTSTATUS:
178 return put_user(0, p);
179
180 case WDIOC_KEEPALIVE:
181 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
182 return 0;
183
184 case WDIOC_SETTIMEOUT:
185 if (get_user(new_value, p))
186 return -EFAULT;
187
188 heartbeat = new_value;
189 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
190
191 return put_user(new_value, p); /* return current value */
192
193 case WDIOC_GETTIMEOUT:
194 return put_user(heartbeat, p);
195 }
196 return -ENOTTY;
197}
198
199/*
200 * Pat the watchdog whenever device is written to.
201 */
202static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
203 loff_t *ppos)
204{
205 if (!len)
206 return 0;
207
208 /* Scan for magic character */
209 if (!nowayout) {
210 size_t i;
211
212 at91wdt_private.expect_close = 0;
213
214 for (i = 0; i < len; i++) {
215 char c;
216 if (get_user(c, data + i))
217 return -EFAULT;
218 if (c == 'V') {
219 at91wdt_private.expect_close = 42;
220 break;
221 }
222 }
223 }
224
225 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
226
227 return len;
228}
229
230/* ......................................................................... */
231
232static const struct file_operations at91wdt_fops = {
233 .owner = THIS_MODULE,
234 .llseek = no_llseek,
235 .unlocked_ioctl = at91_wdt_ioctl,
236 .open = at91_wdt_open,
237 .release = at91_wdt_close,
238 .write = at91_wdt_write,
239};
240
241static struct miscdevice at91wdt_miscdev = {
242 .minor = WATCHDOG_MINOR,
243 .name = "watchdog",
244 .fops = &at91wdt_fops,
245};
246
247static int __init at91wdt_probe(struct platform_device *pdev)
248{
249 int res;
250
251 if (at91wdt_miscdev.parent)
252 return -EBUSY;
253 at91wdt_miscdev.parent = &pdev->dev;
254
255 /* Set watchdog */
256 res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
257 if (res)
258 return res;
259
260 res = misc_register(&at91wdt_miscdev);
261 if (res)
262 return res;
263
264 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
265 setup_timer(&at91wdt_private.timer, at91_ping, 0);
266 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
267
268 printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n",
269 heartbeat, nowayout);
270
271 return 0;
272}
273
274static int __exit at91wdt_remove(struct platform_device *pdev)
275{
276 int res;
277
278 res = misc_deregister(&at91wdt_miscdev);
279 if (!res)
280 at91wdt_miscdev.parent = NULL;
281
282 return res;
283}
284
285#ifdef CONFIG_PM
286
287static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
288{
289 return 0;
290}
291
292static int at91wdt_resume(struct platform_device *pdev)
293{
294 return 0;
295}
296
297#else
298#define at91wdt_suspend NULL
299#define at91wdt_resume NULL
300#endif
301
302static struct platform_driver at91wdt_driver = {
303 .remove = __exit_p(at91wdt_remove),
304 .suspend = at91wdt_suspend,
305 .resume = at91wdt_resume,
306 .driver = {
307 .name = "at91_wdt",
308 .owner = THIS_MODULE,
309 },
310};
311
312static int __init at91sam_wdt_init(void)
313{
314 return platform_driver_probe(&at91wdt_driver, at91wdt_probe);
315}
316
317static void __exit at91sam_wdt_exit(void)
318{
319 platform_driver_unregister(&at91wdt_driver);
320}
321
322module_init(at91sam_wdt_init);
323module_exit(at91sam_wdt_exit);
324
325MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
326MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
327MODULE_LICENSE("GPL");
328MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
new file mode 100644
index 000000000000..afb8af397a9f
--- /dev/null
+++ b/drivers/watchdog/it87_wdt.c
@@ -0,0 +1,725 @@
1/*
2 * Watchdog Timer Driver
3 * for ITE IT87xx Environment Control - Low Pin Count Input / Output
4 *
5 * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com>
6 *
7 * Based on softdog.c by Alan Cox,
8 * 83977f_wdt.c by Jose Goncalves,
9 * it87.c by Chris Gauthron, Jean Delvare
10 *
11 * Data-sheets: Publicly available at the ITE website
12 * http://www.ite.com.tw/
13 *
14 * Support of the watchdog timers, which are available on
15 * IT8716, IT8718, IT8726 and IT8712 (J,K version).
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version
20 * 2 of the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 */
31
32#include <linux/module.h>
33#include <linux/moduleparam.h>
34#include <linux/types.h>
35#include <linux/kernel.h>
36#include <linux/fs.h>
37#include <linux/miscdevice.h>
38#include <linux/init.h>
39#include <linux/ioport.h>
40#include <linux/watchdog.h>
41#include <linux/notifier.h>
42#include <linux/reboot.h>
43#include <linux/uaccess.h>
44#include <linux/io.h>
45
46#include <asm/system.h>
47
48#define WATCHDOG_VERSION "1.12"
49#define WATCHDOG_NAME "IT87 WDT"
50#define PFX WATCHDOG_NAME ": "
51#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
52#define WD_MAGIC 'V'
53
54/* Defaults for Module Parameter */
55#define DEFAULT_NOGAMEPORT 0
56#define DEFAULT_EXCLUSIVE 1
57#define DEFAULT_TIMEOUT 60
58#define DEFAULT_TESTMODE 0
59#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
60
61/* IO Ports */
62#define REG 0x2e
63#define VAL 0x2f
64
65/* Logical device Numbers LDN */
66#define GPIO 0x07
67#define GAMEPORT 0x09
68#define CIR 0x0a
69
70/* Configuration Registers and Functions */
71#define LDNREG 0x07
72#define CHIPID 0x20
73#define CHIPREV 0x22
74#define ACTREG 0x30
75#define BASEREG 0x60
76
77/* Chip Id numbers */
78#define NO_DEV_ID 0xffff
79#define IT8705_ID 0x8705
80#define IT8712_ID 0x8712
81#define IT8716_ID 0x8716
82#define IT8718_ID 0x8718
83#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
84
85/* GPIO Configuration Registers LDN=0x07 */
86#define WDTCTRL 0x71
87#define WDTCFG 0x72
88#define WDTVALLSB 0x73
89#define WDTVALMSB 0x74
90
91/* GPIO Bits WDTCTRL */
92#define WDT_CIRINT 0x80
93#define WDT_MOUSEINT 0x40
94#define WDT_KYBINT 0x20
95#define WDT_GAMEPORT 0x10 /* not it8718 */
96#define WDT_FORCE 0x02
97#define WDT_ZERO 0x01
98
99/* GPIO Bits WDTCFG */
100#define WDT_TOV1 0x80
101#define WDT_KRST 0x40
102#define WDT_TOVE 0x20
103#define WDT_PWROK 0x10
104#define WDT_INT_MASK 0x0f
105
106/* CIR Configuration Register LDN=0x0a */
107#define CIR_ILS 0x70
108
109/* The default Base address is not always available, we use this */
110#define CIR_BASE 0x0208
111
112/* CIR Controller */
113#define CIR_DR(b) (b)
114#define CIR_IER(b) (b + 1)
115#define CIR_RCR(b) (b + 2)
116#define CIR_TCR1(b) (b + 3)
117#define CIR_TCR2(b) (b + 4)
118#define CIR_TSR(b) (b + 5)
119#define CIR_RSR(b) (b + 6)
120#define CIR_BDLR(b) (b + 5)
121#define CIR_BDHR(b) (b + 6)
122#define CIR_IIR(b) (b + 7)
123
124/* Default Base address of Game port */
125#define GP_BASE_DEFAULT 0x0201
126
127/* wdt_status */
128#define WDTS_TIMER_RUN 0
129#define WDTS_DEV_OPEN 1
130#define WDTS_KEEPALIVE 2
131#define WDTS_LOCKED 3
132#define WDTS_USE_GP 4
133#define WDTS_EXPECTED 5
134
135static unsigned int base, gpact, ciract;
136static unsigned long wdt_status;
137static DEFINE_SPINLOCK(spinlock);
138
139static int nogameport = DEFAULT_NOGAMEPORT;
140static int exclusive = DEFAULT_EXCLUSIVE;
141static int timeout = DEFAULT_TIMEOUT;
142static int testmode = DEFAULT_TESTMODE;
143static int nowayout = DEFAULT_NOWAYOUT;
144
145module_param(nogameport, int, 0);
146MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
147 __MODULE_STRING(DEFAULT_NOGAMEPORT));
148module_param(exclusive, int, 0);
149MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
150 __MODULE_STRING(DEFAULT_EXCLUSIVE));
151module_param(timeout, int, 0);
152MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
153 __MODULE_STRING(DEFAULT_TIMEOUT));
154module_param(testmode, int, 0);
155MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
156 __MODULE_STRING(DEFAULT_TESTMODE));
157module_param(nowayout, int, 0);
158MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
159 __MODULE_STRING(WATCHDOG_NOWAYOUT));
160
161/* Superio Chip */
162
163static inline void superio_enter(void)
164{
165 outb(0x87, REG);
166 outb(0x01, REG);
167 outb(0x55, REG);
168 outb(0x55, REG);
169}
170
171static inline void superio_exit(void)
172{
173 outb(0x02, REG);
174 outb(0x02, VAL);
175}
176
177static inline void superio_select(int ldn)
178{
179 outb(LDNREG, REG);
180 outb(ldn, VAL);
181}
182
183static inline int superio_inb(int reg)
184{
185 outb(reg, REG);
186 return inb(VAL);
187}
188
189static inline void superio_outb(int val, int reg)
190{
191 outb(reg, REG);
192 outb(val, VAL);
193}
194
195static inline int superio_inw(int reg)
196{
197 int val;
198 outb(reg++, REG);
199 val = inb(VAL) << 8;
200 outb(reg, REG);
201 val |= inb(VAL);
202 return val;
203}
204
205static inline void superio_outw(int val, int reg)
206{
207 outb(reg++, REG);
208 outb(val >> 8, VAL);
209 outb(reg, REG);
210 outb(val, VAL);
211}
212
213/* watchdog timer handling */
214
215static void wdt_keepalive(void)
216{
217 if (test_bit(WDTS_USE_GP, &wdt_status))
218 inb(base);
219 else
220 /* The timer reloads with around 5 msec delay */
221 outb(0x55, CIR_DR(base));
222 set_bit(WDTS_KEEPALIVE, &wdt_status);
223}
224
225static void wdt_start(void)
226{
227 unsigned long flags;
228
229 spin_lock_irqsave(&spinlock, flags);
230 superio_enter();
231
232 superio_select(GPIO);
233 if (test_bit(WDTS_USE_GP, &wdt_status))
234 superio_outb(WDT_GAMEPORT, WDTCTRL);
235 else
236 superio_outb(WDT_CIRINT, WDTCTRL);
237 if (!testmode)
238 superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG);
239 else
240 superio_outb(WDT_TOV1, WDTCFG);
241 superio_outb(timeout>>8, WDTVALMSB);
242 superio_outb(timeout, WDTVALLSB);
243
244 superio_exit();
245 spin_unlock_irqrestore(&spinlock, flags);
246}
247
248static void wdt_stop(void)
249{
250 unsigned long flags;
251
252 spin_lock_irqsave(&spinlock, flags);
253 superio_enter();
254
255 superio_select(GPIO);
256 superio_outb(0x00, WDTCTRL);
257 superio_outb(WDT_TOV1, WDTCFG);
258 superio_outb(0x00, WDTVALMSB);
259 superio_outb(0x00, WDTVALLSB);
260
261 superio_exit();
262 spin_unlock_irqrestore(&spinlock, flags);
263}
264
265/**
266 * wdt_set_timeout - set a new timeout value with watchdog ioctl
267 * @t: timeout value in seconds
268 *
269 * The hardware device has a 16 bit watchdog timer, thus the
270 * timeout time ranges between 1 and 65535 seconds.
271 *
272 * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
273 */
274
275static int wdt_set_timeout(int t)
276{
277 unsigned long flags;
278
279 if (t < 1 || t > 65535)
280 return -EINVAL;
281
282 timeout = t;
283
284 spin_lock_irqsave(&spinlock, flags);
285 if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
286 superio_enter();
287
288 superio_select(GPIO);
289 superio_outb(t>>8, WDTVALMSB);
290 superio_outb(t, WDTVALLSB);
291
292 superio_exit();
293 }
294 spin_unlock_irqrestore(&spinlock, flags);
295 return 0;
296}
297
298/**
299 * wdt_get_status - determines the status supported by watchdog ioctl
300 * @status: status returned to user space
301 *
302 * The status bit of the device does not allow to distinguish
303 * between a regular system reset and a watchdog forced reset.
304 * But, in test mode it is useful, so it is supported through
305 * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
306 * reports the keepalive signal and the acception of the magic.
307 *
308 * Used within WDIOC_GETSTATUS watchdog device ioctl.
309 */
310
311static int wdt_get_status(int *status)
312{
313 unsigned long flags;
314
315 *status = 0;
316 if (testmode) {
317 spin_lock_irqsave(&spinlock, flags);
318 superio_enter();
319 superio_select(GPIO);
320 if (superio_inb(WDTCTRL) & WDT_ZERO) {
321 superio_outb(0x00, WDTCTRL);
322 clear_bit(WDTS_TIMER_RUN, &wdt_status);
323 *status |= WDIOF_CARDRESET;
324 }
325
326 superio_exit();
327 spin_unlock_irqrestore(&spinlock, flags);
328 }
329 if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
330 *status |= WDIOF_KEEPALIVEPING;
331 if (test_bit(WDTS_EXPECTED, &wdt_status))
332 *status |= WDIOF_MAGICCLOSE;
333 return 0;
334}
335
336/* /dev/watchdog handling */
337
338/**
339 * wdt_open - watchdog file_operations .open
340 * @inode: inode of the device
341 * @file: file handle to the device
342 *
343 * The watchdog timer starts by opening the device.
344 *
345 * Used within the file operation of the watchdog device.
346 */
347
348static int wdt_open(struct inode *inode, struct file *file)
349{
350 if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
351 return -EBUSY;
352 if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
353 if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
354 __module_get(THIS_MODULE);
355 wdt_start();
356 }
357 return nonseekable_open(inode, file);
358}
359
360/**
361 * wdt_release - watchdog file_operations .release
362 * @inode: inode of the device
363 * @file: file handle to the device
364 *
365 * Closing the watchdog device either stops the watchdog timer
366 * or in the case, that nowayout is set or the magic character
367 * wasn't written, a critical warning about an running watchdog
368 * timer is given.
369 *
370 * Used within the file operation of the watchdog device.
371 */
372
373static int wdt_release(struct inode *inode, struct file *file)
374{
375 if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
376 if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
377 wdt_stop();
378 clear_bit(WDTS_TIMER_RUN, &wdt_status);
379 } else {
380 wdt_keepalive();
381 printk(KERN_CRIT PFX
382 "unexpected close, not stopping watchdog!\n");
383 }
384 }
385 clear_bit(WDTS_DEV_OPEN, &wdt_status);
386 return 0;
387}
388
389/**
390 * wdt_write - watchdog file_operations .write
391 * @file: file handle to the watchdog
392 * @buf: buffer to write
393 * @count: count of bytes
394 * @ppos: pointer to the position to write. No seeks allowed
395 *
396 * A write to a watchdog device is defined as a keepalive signal. Any
397 * write of data will do, as we don't define content meaning.
398 *
399 * Used within the file operation of the watchdog device.
400 */
401
402static ssize_t wdt_write(struct file *file, const char __user *buf,
403 size_t count, loff_t *ppos)
404{
405 if (count) {
406 clear_bit(WDTS_EXPECTED, &wdt_status);
407 wdt_keepalive();
408 }
409 if (!nowayout) {
410 size_t ofs;
411
412 /* note: just in case someone wrote the magic character long ago */
413 for (ofs = 0; ofs != count; ofs++) {
414 char c;
415 if (get_user(c, buf + ofs))
416 return -EFAULT;
417 if (c == WD_MAGIC)
418 set_bit(WDTS_EXPECTED, &wdt_status);
419 }
420 }
421 return count;
422}
423
424static struct watchdog_info ident = {
425 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
426 .firmware_version = 1,
427 .identity = WATCHDOG_NAME,
428};
429
430/**
431 * wdt_ioctl - watchdog file_operations .unlocked_ioctl
432 * @file: file handle to the device
433 * @cmd: watchdog command
434 * @arg: argument pointer
435 *
436 * The watchdog API defines a common set of functions for all watchdogs
437 * according to their available features.
438 *
439 * Used within the file operation of the watchdog device.
440 */
441
442static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
443{
444 int rc = 0, status, new_options, new_timeout;
445 union {
446 struct watchdog_info __user *ident;
447 int __user *i;
448 } uarg;
449
450 uarg.i = (int __user *)arg;
451
452 switch (cmd) {
453 case WDIOC_GETSUPPORT:
454 return copy_to_user(uarg.ident,
455 &ident, sizeof(ident)) ? -EFAULT : 0;
456
457 case WDIOC_GETSTATUS:
458 wdt_get_status(&status);
459 return put_user(status, uarg.i);
460
461 case WDIOC_GETBOOTSTATUS:
462 return put_user(0, uarg.i);
463
464 case WDIOC_KEEPALIVE:
465 wdt_keepalive();
466 return 0;
467
468 case WDIOC_SETOPTIONS:
469 if (get_user(new_options, uarg.i))
470 return -EFAULT;
471
472 switch (new_options) {
473 case WDIOS_DISABLECARD:
474 if (test_bit(WDTS_TIMER_RUN, &wdt_status))
475 wdt_stop();
476 clear_bit(WDTS_TIMER_RUN, &wdt_status);
477 return 0;
478
479 case WDIOS_ENABLECARD:
480 if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
481 wdt_start();
482 return 0;
483
484 default:
485 return -EFAULT;
486 }
487
488 case WDIOC_SETTIMEOUT:
489 if (get_user(new_timeout, uarg.i))
490 return -EFAULT;
491 rc = wdt_set_timeout(new_timeout);
492 case WDIOC_GETTIMEOUT:
493 if (put_user(timeout, uarg.i))
494 return -EFAULT;
495 return rc;
496
497 default:
498 return -ENOTTY;
499 }
500}
501
502static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
503 void *unused)
504{
505 if (code == SYS_DOWN || code == SYS_HALT)
506 wdt_stop();
507 return NOTIFY_DONE;
508}
509
510static const struct file_operations wdt_fops = {
511 .owner = THIS_MODULE,
512 .llseek = no_llseek,
513 .write = wdt_write,
514 .unlocked_ioctl = wdt_ioctl,
515 .open = wdt_open,
516 .release = wdt_release,
517};
518
519static struct miscdevice wdt_miscdev = {
520 .minor = WATCHDOG_MINOR,
521 .name = "watchdog",
522 .fops = &wdt_fops,
523};
524
525static struct notifier_block wdt_notifier = {
526 .notifier_call = wdt_notify_sys,
527};
528
529static int __init it87_wdt_init(void)
530{
531 int rc = 0;
532 u16 chip_type;
533 u8 chip_rev;
534 unsigned long flags;
535
536 spin_lock_irqsave(&spinlock, flags);
537 superio_enter();
538 chip_type = superio_inw(CHIPID);
539 chip_rev = superio_inb(CHIPREV) & 0x0f;
540 superio_exit();
541 spin_unlock_irqrestore(&spinlock, flags);
542
543 switch (chip_type) {
544 case IT8716_ID:
545 case IT8718_ID:
546 case IT8726_ID:
547 break;
548 case IT8712_ID:
549 if (chip_rev > 7)
550 break;
551 case IT8705_ID:
552 printk(KERN_ERR PFX
553 "Unsupported Chip found, Chip %04x Revision %02x\n",
554 chip_type, chip_rev);
555 return -ENODEV;
556 case NO_DEV_ID:
557 printk(KERN_ERR PFX "no device\n");
558 return -ENODEV;
559 default:
560 printk(KERN_ERR PFX
561 "Unknown Chip found, Chip %04x Revision %04x\n",
562 chip_type, chip_rev);
563 return -ENODEV;
564 }
565
566 spin_lock_irqsave(&spinlock, flags);
567 superio_enter();
568
569 superio_select(GPIO);
570 superio_outb(WDT_TOV1, WDTCFG);
571 superio_outb(0x00, WDTCTRL);
572
573 /* First try to get Gameport support */
574 if (chip_type != IT8718_ID && !nogameport) {
575 superio_select(GAMEPORT);
576 base = superio_inw(BASEREG);
577 if (!base) {
578 base = GP_BASE_DEFAULT;
579 superio_outw(base, BASEREG);
580 }
581 gpact = superio_inb(ACTREG);
582 superio_outb(0x01, ACTREG);
583 superio_exit();
584 spin_unlock_irqrestore(&spinlock, flags);
585 if (request_region(base, 1, WATCHDOG_NAME))
586 set_bit(WDTS_USE_GP, &wdt_status);
587 else
588 rc = -EIO;
589 } else {
590 superio_exit();
591 spin_unlock_irqrestore(&spinlock, flags);
592 }
593
594 /* If we haven't Gameport support, try to get CIR support */
595 if (!test_bit(WDTS_USE_GP, &wdt_status)) {
596 if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
597 if (rc == -EIO)
598 printk(KERN_ERR PFX
599 "I/O Address 0x%04x and 0x%04x"
600 " already in use\n", base, CIR_BASE);
601 else
602 printk(KERN_ERR PFX
603 "I/O Address 0x%04x already in use\n",
604 CIR_BASE);
605 rc = -EIO;
606 goto err_out;
607 }
608 base = CIR_BASE;
609 spin_lock_irqsave(&spinlock, flags);
610 superio_enter();
611
612 superio_select(CIR);
613 superio_outw(base, BASEREG);
614 superio_outb(0x00, CIR_ILS);
615 ciract = superio_inb(ACTREG);
616 superio_outb(0x01, ACTREG);
617 if (rc == -EIO) {
618 superio_select(GAMEPORT);
619 superio_outb(gpact, ACTREG);
620 }
621
622 superio_exit();
623 spin_unlock_irqrestore(&spinlock, flags);
624 }
625
626 if (timeout < 1 || timeout > 65535) {
627 timeout = DEFAULT_TIMEOUT;
628 printk(KERN_WARNING PFX
629 "Timeout value out of range, use default %d sec\n",
630 DEFAULT_TIMEOUT);
631 }
632
633 rc = register_reboot_notifier(&wdt_notifier);
634 if (rc) {
635 printk(KERN_ERR PFX
636 "Cannot register reboot notifier (err=%d)\n", rc);
637 goto err_out_region;
638 }
639
640 rc = misc_register(&wdt_miscdev);
641 if (rc) {
642 printk(KERN_ERR PFX
643 "Cannot register miscdev on minor=%d (err=%d)\n",
644 wdt_miscdev.minor, rc);
645 goto err_out_reboot;
646 }
647
648 /* Initialize CIR to use it as keepalive source */
649 if (!test_bit(WDTS_USE_GP, &wdt_status)) {
650 outb(0x00, CIR_RCR(base));
651 outb(0xc0, CIR_TCR1(base));
652 outb(0x5c, CIR_TCR2(base));
653 outb(0x10, CIR_IER(base));
654 outb(0x00, CIR_BDHR(base));
655 outb(0x01, CIR_BDLR(base));
656 outb(0x09, CIR_IER(base));
657 }
658
659 printk(KERN_INFO PFX "Chip it%04x revision %d initialized. "
660 "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d "
661 "nogameport=%d)\n", chip_type, chip_rev, timeout,
662 nowayout, testmode, exclusive, nogameport);
663
664 return 0;
665
666err_out_reboot:
667 unregister_reboot_notifier(&wdt_notifier);
668err_out_region:
669 release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
670 if (!test_bit(WDTS_USE_GP, &wdt_status)) {
671 spin_lock_irqsave(&spinlock, flags);
672 superio_enter();
673 superio_select(CIR);
674 superio_outb(ciract, ACTREG);
675 superio_exit();
676 spin_unlock_irqrestore(&spinlock, flags);
677 }
678err_out:
679 if (chip_type != IT8718_ID && !nogameport) {
680 spin_lock_irqsave(&spinlock, flags);
681 superio_enter();
682 superio_select(GAMEPORT);
683 superio_outb(gpact, ACTREG);
684 superio_exit();
685 spin_unlock_irqrestore(&spinlock, flags);
686 }
687
688 return rc;
689}
690
691static void __exit it87_wdt_exit(void)
692{
693 unsigned long flags;
694 int nolock;
695
696 nolock = !spin_trylock_irqsave(&spinlock, flags);
697 superio_enter();
698 superio_select(GPIO);
699 superio_outb(0x00, WDTCTRL);
700 superio_outb(0x00, WDTCFG);
701 superio_outb(0x00, WDTVALMSB);
702 superio_outb(0x00, WDTVALLSB);
703 if (test_bit(WDTS_USE_GP, &wdt_status)) {
704 superio_select(GAMEPORT);
705 superio_outb(gpact, ACTREG);
706 } else {
707 superio_select(CIR);
708 superio_outb(ciract, ACTREG);
709 }
710 superio_exit();
711 if (!nolock)
712 spin_unlock_irqrestore(&spinlock, flags);
713
714 misc_deregister(&wdt_miscdev);
715 unregister_reboot_notifier(&wdt_notifier);
716 release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
717}
718
719module_init(it87_wdt_init);
720module_exit(it87_wdt_exit);
721
722MODULE_AUTHOR("Oliver Schuster");
723MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
724MODULE_LICENSE("GPL");
725MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 3a11dadfd8e7..7bcbb7f4745f 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/char/watchdog/omap_wdt.c 2 * omap_wdt.c
3 * 3 *
4 * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog 4 * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
5 * 5 *
6 * Author: MontaVista Software, Inc. 6 * Author: MontaVista Software, Inc.
7 * <gdavis@mvista.com> or <source@mvista.com> 7 * <gdavis@mvista.com> or <source@mvista.com>
@@ -47,50 +47,68 @@
47 47
48#include "omap_wdt.h" 48#include "omap_wdt.h"
49 49
50static struct platform_device *omap_wdt_dev;
51
50static unsigned timer_margin; 52static unsigned timer_margin;
51module_param(timer_margin, uint, 0); 53module_param(timer_margin, uint, 0);
52MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); 54MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
53 55
54static int omap_wdt_users;
55static struct clk *armwdt_ck;
56static struct clk *mpu_wdt_ick;
57static struct clk *mpu_wdt_fck;
58
59static unsigned int wdt_trgr_pattern = 0x1234; 56static unsigned int wdt_trgr_pattern = 0x1234;
60static spinlock_t wdt_lock; 57static spinlock_t wdt_lock;
61 58
62static void omap_wdt_ping(void) 59struct omap_wdt_dev {
60 void __iomem *base; /* physical */
61 struct device *dev;
62 int omap_wdt_users;
63 struct clk *armwdt_ck;
64 struct clk *mpu_wdt_ick;
65 struct clk *mpu_wdt_fck;
66 struct resource *mem;
67 struct miscdevice omap_wdt_miscdev;
68};
69
70static void omap_wdt_ping(struct omap_wdt_dev *wdev)
63{ 71{
72 void __iomem *base = wdev->base;
73
64 /* wait for posted write to complete */ 74 /* wait for posted write to complete */
65 while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) 75 while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
66 cpu_relax(); 76 cpu_relax();
77
67 wdt_trgr_pattern = ~wdt_trgr_pattern; 78 wdt_trgr_pattern = ~wdt_trgr_pattern;
68 omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); 79 __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
80
69 /* wait for posted write to complete */ 81 /* wait for posted write to complete */
70 while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) 82 while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
71 cpu_relax(); 83 cpu_relax();
72 /* reloaded WCRR from WLDR */ 84 /* reloaded WCRR from WLDR */
73} 85}
74 86
75static void omap_wdt_enable(void) 87static void omap_wdt_enable(struct omap_wdt_dev *wdev)
76{ 88{
89 void __iomem *base = wdev->base;
90
77 /* Sequence to enable the watchdog */ 91 /* Sequence to enable the watchdog */
78 omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); 92 __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
79 while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) 93 while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
80 cpu_relax(); 94 cpu_relax();
81 omap_writel(0x4444, OMAP_WATCHDOG_SPR); 95
82 while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) 96 __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
97 while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
83 cpu_relax(); 98 cpu_relax();
84} 99}
85 100
86static void omap_wdt_disable(void) 101static void omap_wdt_disable(struct omap_wdt_dev *wdev)
87{ 102{
103 void __iomem *base = wdev->base;
104
88 /* sequence required to disable watchdog */ 105 /* sequence required to disable watchdog */
89 omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ 106 __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
90 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) 107 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
91 cpu_relax(); 108 cpu_relax();
92 omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ 109
93 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) 110 __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
111 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
94 cpu_relax(); 112 cpu_relax();
95} 113}
96 114
@@ -103,83 +121,90 @@ static void omap_wdt_adjust_timeout(unsigned new_timeout)
103 timer_margin = new_timeout; 121 timer_margin = new_timeout;
104} 122}
105 123
106static void omap_wdt_set_timeout(void) 124static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
107{ 125{
108 u32 pre_margin = GET_WLDR_VAL(timer_margin); 126 u32 pre_margin = GET_WLDR_VAL(timer_margin);
127 void __iomem *base = wdev->base;
109 128
110 /* just count up at 32 KHz */ 129 /* just count up at 32 KHz */
111 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) 130 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
112 cpu_relax(); 131 cpu_relax();
113 omap_writel(pre_margin, OMAP_WATCHDOG_LDR); 132
114 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) 133 __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
134 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
115 cpu_relax(); 135 cpu_relax();
116} 136}
117 137
118/* 138/*
119 * Allow only one task to hold it open 139 * Allow only one task to hold it open
120 */ 140 */
121
122static int omap_wdt_open(struct inode *inode, struct file *file) 141static int omap_wdt_open(struct inode *inode, struct file *file)
123{ 142{
124 if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) 143 struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
144 void __iomem *base = wdev->base;
145
146 if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
125 return -EBUSY; 147 return -EBUSY;
126 148
127 if (cpu_is_omap16xx()) 149 if (cpu_is_omap16xx())
128 clk_enable(armwdt_ck); /* Enable the clock */ 150 clk_enable(wdev->armwdt_ck); /* Enable the clock */
129 151
130 if (cpu_is_omap24xx()) { 152 if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
131 clk_enable(mpu_wdt_ick); /* Enable the interface clock */ 153 clk_enable(wdev->mpu_wdt_ick); /* Enable the interface clock */
132 clk_enable(mpu_wdt_fck); /* Enable the functional clock */ 154 clk_enable(wdev->mpu_wdt_fck); /* Enable the functional clock */
133 } 155 }
134 156
135 /* initialize prescaler */ 157 /* initialize prescaler */
136 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) 158 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
137 cpu_relax(); 159 cpu_relax();
138 omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); 160
139 while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) 161 __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
162 while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
140 cpu_relax(); 163 cpu_relax();
141 164
142 omap_wdt_set_timeout(); 165 file->private_data = (void *) wdev;
143 omap_wdt_enable(); 166
167 omap_wdt_set_timeout(wdev);
168 omap_wdt_enable(wdev);
169
144 return nonseekable_open(inode, file); 170 return nonseekable_open(inode, file);
145} 171}
146 172
147static int omap_wdt_release(struct inode *inode, struct file *file) 173static int omap_wdt_release(struct inode *inode, struct file *file)
148{ 174{
175 struct omap_wdt_dev *wdev = file->private_data;
176
149 /* 177 /*
150 * Shut off the timer unless NOWAYOUT is defined. 178 * Shut off the timer unless NOWAYOUT is defined.
151 */ 179 */
152#ifndef CONFIG_WATCHDOG_NOWAYOUT 180#ifndef CONFIG_WATCHDOG_NOWAYOUT
153 omap_wdt_disable();
154 181
155 if (cpu_is_omap16xx()) { 182 omap_wdt_disable(wdev);
156 clk_disable(armwdt_ck); /* Disable the clock */
157 clk_put(armwdt_ck);
158 armwdt_ck = NULL;
159 }
160 183
161 if (cpu_is_omap24xx()) { 184 if (cpu_is_omap16xx())
162 clk_disable(mpu_wdt_ick); /* Disable the clock */ 185 clk_disable(wdev->armwdt_ck); /* Disable the clock */
163 clk_disable(mpu_wdt_fck); /* Disable the clock */ 186
164 clk_put(mpu_wdt_ick); 187 if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
165 clk_put(mpu_wdt_fck); 188 clk_disable(wdev->mpu_wdt_ick); /* Disable the clock */
166 mpu_wdt_ick = NULL; 189 clk_disable(wdev->mpu_wdt_fck); /* Disable the clock */
167 mpu_wdt_fck = NULL;
168 } 190 }
169#else 191#else
170 printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); 192 printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
171#endif 193#endif
172 omap_wdt_users = 0; 194 wdev->omap_wdt_users = 0;
195
173 return 0; 196 return 0;
174} 197}
175 198
176static ssize_t omap_wdt_write(struct file *file, const char __user *data, 199static ssize_t omap_wdt_write(struct file *file, const char __user *data,
177 size_t len, loff_t *ppos) 200 size_t len, loff_t *ppos)
178{ 201{
202 struct omap_wdt_dev *wdev = file->private_data;
203
179 /* Refresh LOAD_TIME. */ 204 /* Refresh LOAD_TIME. */
180 if (len) { 205 if (len) {
181 spin_lock(&wdt_lock); 206 spin_lock(&wdt_lock);
182 omap_wdt_ping(); 207 omap_wdt_ping(wdev);
183 spin_unlock(&wdt_lock); 208 spin_unlock(&wdt_lock);
184 } 209 }
185 return len; 210 return len;
@@ -188,6 +213,7 @@ static ssize_t omap_wdt_write(struct file *file, const char __user *data,
188static long omap_wdt_ioctl(struct file *file, unsigned int cmd, 213static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
189 unsigned long arg) 214 unsigned long arg)
190{ 215{
216 struct omap_wdt_dev *wdev;
191 int new_margin; 217 int new_margin;
192 static const struct watchdog_info ident = { 218 static const struct watchdog_info ident = {
193 .identity = "OMAP Watchdog", 219 .identity = "OMAP Watchdog",
@@ -195,6 +221,8 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
195 .firmware_version = 0, 221 .firmware_version = 0,
196 }; 222 };
197 223
224 wdev = file->private_data;
225
198 switch (cmd) { 226 switch (cmd) {
199 case WDIOC_GETSUPPORT: 227 case WDIOC_GETSUPPORT:
200 return copy_to_user((struct watchdog_info __user *)arg, &ident, 228 return copy_to_user((struct watchdog_info __user *)arg, &ident,
@@ -203,14 +231,14 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
203 return put_user(0, (int __user *)arg); 231 return put_user(0, (int __user *)arg);
204 case WDIOC_GETBOOTSTATUS: 232 case WDIOC_GETBOOTSTATUS:
205 if (cpu_is_omap16xx()) 233 if (cpu_is_omap16xx())
206 return put_user(omap_readw(ARM_SYSST), 234 return put_user(__raw_readw(ARM_SYSST),
207 (int __user *)arg); 235 (int __user *)arg);
208 if (cpu_is_omap24xx()) 236 if (cpu_is_omap24xx())
209 return put_user(omap_prcm_get_reset_sources(), 237 return put_user(omap_prcm_get_reset_sources(),
210 (int __user *)arg); 238 (int __user *)arg);
211 case WDIOC_KEEPALIVE: 239 case WDIOC_KEEPALIVE:
212 spin_lock(&wdt_lock); 240 spin_lock(&wdt_lock);
213 omap_wdt_ping(); 241 omap_wdt_ping(wdev);
214 spin_unlock(&wdt_lock); 242 spin_unlock(&wdt_lock);
215 return 0; 243 return 0;
216 case WDIOC_SETTIMEOUT: 244 case WDIOC_SETTIMEOUT:
@@ -219,11 +247,11 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
219 omap_wdt_adjust_timeout(new_margin); 247 omap_wdt_adjust_timeout(new_margin);
220 248
221 spin_lock(&wdt_lock); 249 spin_lock(&wdt_lock);
222 omap_wdt_disable(); 250 omap_wdt_disable(wdev);
223 omap_wdt_set_timeout(); 251 omap_wdt_set_timeout(wdev);
224 omap_wdt_enable(); 252 omap_wdt_enable(wdev);
225 253
226 omap_wdt_ping(); 254 omap_wdt_ping(wdev);
227 spin_unlock(&wdt_lock); 255 spin_unlock(&wdt_lock);
228 /* Fall */ 256 /* Fall */
229 case WDIOC_GETTIMEOUT: 257 case WDIOC_GETTIMEOUT:
@@ -241,96 +269,173 @@ static const struct file_operations omap_wdt_fops = {
241 .release = omap_wdt_release, 269 .release = omap_wdt_release,
242}; 270};
243 271
244static struct miscdevice omap_wdt_miscdev = {
245 .minor = WATCHDOG_MINOR,
246 .name = "watchdog",
247 .fops = &omap_wdt_fops,
248};
249
250static int __init omap_wdt_probe(struct platform_device *pdev) 272static int __init omap_wdt_probe(struct platform_device *pdev)
251{ 273{
252 struct resource *res, *mem; 274 struct resource *res, *mem;
275 struct omap_wdt_dev *wdev;
253 int ret; 276 int ret;
254 277
255 /* reserve static register mappings */ 278 /* reserve static register mappings */
256 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 279 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
257 if (!res) 280 if (!res) {
258 return -ENOENT; 281 ret = -ENOENT;
282 goto err_get_resource;
283 }
284
285 if (omap_wdt_dev) {
286 ret = -EBUSY;
287 goto err_busy;
288 }
259 289
260 mem = request_mem_region(res->start, res->end - res->start + 1, 290 mem = request_mem_region(res->start, res->end - res->start + 1,
261 pdev->name); 291 pdev->name);
262 if (mem == NULL) 292 if (!mem) {
263 return -EBUSY; 293 ret = -EBUSY;
294 goto err_busy;
295 }
264 296
265 platform_set_drvdata(pdev, mem); 297 wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
298 if (!wdev) {
299 ret = -ENOMEM;
300 goto err_kzalloc;
301 }
266 302
267 omap_wdt_users = 0; 303 wdev->omap_wdt_users = 0;
304 wdev->mem = mem;
268 305
269 if (cpu_is_omap16xx()) { 306 if (cpu_is_omap16xx()) {
270 armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); 307 wdev->armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
271 if (IS_ERR(armwdt_ck)) { 308 if (IS_ERR(wdev->armwdt_ck)) {
272 ret = PTR_ERR(armwdt_ck); 309 ret = PTR_ERR(wdev->armwdt_ck);
273 armwdt_ck = NULL; 310 wdev->armwdt_ck = NULL;
274 goto fail; 311 goto err_clk;
275 } 312 }
276 } 313 }
277 314
278 if (cpu_is_omap24xx()) { 315 if (cpu_is_omap24xx()) {
279 mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); 316 wdev->mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
280 if (IS_ERR(mpu_wdt_ick)) { 317 if (IS_ERR(wdev->mpu_wdt_ick)) {
281 ret = PTR_ERR(mpu_wdt_ick); 318 ret = PTR_ERR(wdev->mpu_wdt_ick);
282 mpu_wdt_ick = NULL; 319 wdev->mpu_wdt_ick = NULL;
283 goto fail; 320 goto err_clk;
284 } 321 }
285 mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); 322 wdev->mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
286 if (IS_ERR(mpu_wdt_fck)) { 323 if (IS_ERR(wdev->mpu_wdt_fck)) {
287 ret = PTR_ERR(mpu_wdt_fck); 324 ret = PTR_ERR(wdev->mpu_wdt_fck);
288 mpu_wdt_fck = NULL; 325 wdev->mpu_wdt_fck = NULL;
289 goto fail; 326 goto err_clk;
290 } 327 }
291 } 328 }
292 329
293 omap_wdt_disable(); 330 if (cpu_is_omap34xx()) {
331 wdev->mpu_wdt_ick = clk_get(&pdev->dev, "wdt2_ick");
332 if (IS_ERR(wdev->mpu_wdt_ick)) {
333 ret = PTR_ERR(wdev->mpu_wdt_ick);
334 wdev->mpu_wdt_ick = NULL;
335 goto err_clk;
336 }
337 wdev->mpu_wdt_fck = clk_get(&pdev->dev, "wdt2_fck");
338 if (IS_ERR(wdev->mpu_wdt_fck)) {
339 ret = PTR_ERR(wdev->mpu_wdt_fck);
340 wdev->mpu_wdt_fck = NULL;
341 goto err_clk;
342 }
343 }
344 wdev->base = ioremap(res->start, res->end - res->start + 1);
345 if (!wdev->base) {
346 ret = -ENOMEM;
347 goto err_ioremap;
348 }
349
350 platform_set_drvdata(pdev, wdev);
351
352 omap_wdt_disable(wdev);
294 omap_wdt_adjust_timeout(timer_margin); 353 omap_wdt_adjust_timeout(timer_margin);
295 354
296 omap_wdt_miscdev.parent = &pdev->dev; 355 wdev->omap_wdt_miscdev.parent = &pdev->dev;
297 ret = misc_register(&omap_wdt_miscdev); 356 wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
357 wdev->omap_wdt_miscdev.name = "watchdog";
358 wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
359
360 ret = misc_register(&(wdev->omap_wdt_miscdev));
298 if (ret) 361 if (ret)
299 goto fail; 362 goto err_misc;
300 363
301 pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); 364 pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
365 __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
366 timer_margin);
302 367
303 /* autogate OCP interface clock */ 368 /* autogate OCP interface clock */
304 omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); 369 __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
370
371 omap_wdt_dev = pdev;
372
305 return 0; 373 return 0;
306 374
307fail: 375err_misc:
308 if (armwdt_ck) 376 platform_set_drvdata(pdev, NULL);
309 clk_put(armwdt_ck); 377 iounmap(wdev->base);
310 if (mpu_wdt_ick) 378
311 clk_put(mpu_wdt_ick); 379err_ioremap:
312 if (mpu_wdt_fck) 380 wdev->base = NULL;
313 clk_put(mpu_wdt_fck); 381
314 release_resource(mem); 382err_clk:
383 if (wdev->armwdt_ck)
384 clk_put(wdev->armwdt_ck);
385 if (wdev->mpu_wdt_ick)
386 clk_put(wdev->mpu_wdt_ick);
387 if (wdev->mpu_wdt_fck)
388 clk_put(wdev->mpu_wdt_fck);
389 kfree(wdev);
390
391err_kzalloc:
392 release_mem_region(res->start, res->end - res->start + 1);
393
394err_busy:
395err_get_resource:
396
315 return ret; 397 return ret;
316} 398}
317 399
318static void omap_wdt_shutdown(struct platform_device *pdev) 400static void omap_wdt_shutdown(struct platform_device *pdev)
319{ 401{
320 omap_wdt_disable(); 402 struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
403
404 if (wdev->omap_wdt_users)
405 omap_wdt_disable(wdev);
321} 406}
322 407
323static int omap_wdt_remove(struct platform_device *pdev) 408static int omap_wdt_remove(struct platform_device *pdev)
324{ 409{
325 struct resource *mem = platform_get_drvdata(pdev); 410 struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
326 misc_deregister(&omap_wdt_miscdev); 411 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
327 release_resource(mem); 412
328 if (armwdt_ck) 413 if (!res)
329 clk_put(armwdt_ck); 414 return -ENOENT;
330 if (mpu_wdt_ick) 415
331 clk_put(mpu_wdt_ick); 416 misc_deregister(&(wdev->omap_wdt_miscdev));
332 if (mpu_wdt_fck) 417 release_mem_region(res->start, res->end - res->start + 1);
333 clk_put(mpu_wdt_fck); 418 platform_set_drvdata(pdev, NULL);
419
420 if (wdev->armwdt_ck) {
421 clk_put(wdev->armwdt_ck);
422 wdev->armwdt_ck = NULL;
423 }
424
425 if (wdev->mpu_wdt_ick) {
426 clk_put(wdev->mpu_wdt_ick);
427 wdev->mpu_wdt_ick = NULL;
428 }
429
430 if (wdev->mpu_wdt_fck) {
431 clk_put(wdev->mpu_wdt_fck);
432 wdev->mpu_wdt_fck = NULL;
433 }
434 iounmap(wdev->base);
435
436 kfree(wdev);
437 omap_wdt_dev = NULL;
438
334 return 0; 439 return 0;
335} 440}
336 441
@@ -344,17 +449,23 @@ static int omap_wdt_remove(struct platform_device *pdev)
344 449
345static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) 450static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
346{ 451{
347 if (omap_wdt_users) 452 struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
348 omap_wdt_disable(); 453
454 if (wdev->omap_wdt_users)
455 omap_wdt_disable(wdev);
456
349 return 0; 457 return 0;
350} 458}
351 459
352static int omap_wdt_resume(struct platform_device *pdev) 460static int omap_wdt_resume(struct platform_device *pdev)
353{ 461{
354 if (omap_wdt_users) { 462 struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
355 omap_wdt_enable(); 463
356 omap_wdt_ping(); 464 if (wdev->omap_wdt_users) {
465 omap_wdt_enable(wdev);
466 omap_wdt_ping(wdev);
357 } 467 }
468
358 return 0; 469 return 0;
359} 470}
360 471
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h
index 52a532a5114a..fc02ec6a0386 100644
--- a/drivers/watchdog/omap_wdt.h
+++ b/drivers/watchdog/omap_wdt.h
@@ -30,25 +30,15 @@
30#ifndef _OMAP_WATCHDOG_H 30#ifndef _OMAP_WATCHDOG_H
31#define _OMAP_WATCHDOG_H 31#define _OMAP_WATCHDOG_H
32 32
33#define OMAP1610_WATCHDOG_BASE 0xfffeb000 33#define OMAP_WATCHDOG_REV (0x00)
34#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ 34#define OMAP_WATCHDOG_SYS_CONFIG (0x10)
35 35#define OMAP_WATCHDOG_STATUS (0x14)
36#ifdef CONFIG_ARCH_OMAP24XX 36#define OMAP_WATCHDOG_CNTRL (0x24)
37#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE 37#define OMAP_WATCHDOG_CRR (0x28)
38#else 38#define OMAP_WATCHDOG_LDR (0x2c)
39#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE 39#define OMAP_WATCHDOG_TGR (0x30)
40#define RM_RSTST_WKUP 0 40#define OMAP_WATCHDOG_WPS (0x34)
41#endif 41#define OMAP_WATCHDOG_SPR (0x48)
42
43#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00)
44#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10)
45#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14)
46#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24)
47#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28)
48#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c)
49#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30)
50#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34)
51#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48)
52 42
53/* Using the prescaler, the OMAP watchdog could go for many 43/* Using the prescaler, the OMAP watchdog could go for many
54 * months before firing. These limits work without scaling, 44 * months before firing. These limits work without scaling,
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c
new file mode 100644
index 000000000000..14a339f58b6a
--- /dev/null
+++ b/drivers/watchdog/orion5x_wdt.c
@@ -0,0 +1,245 @@
1/*
2 * drivers/watchdog/orion5x_wdt.c
3 *
4 * Watchdog driver for Orion5x processors
5 *
6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
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#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/fs.h>
18#include <linux/miscdevice.h>
19#include <linux/watchdog.h>
20#include <linux/init.h>
21#include <linux/uaccess.h>
22#include <linux/io.h>
23#include <linux/spinlock.h>
24
25/*
26 * Watchdog timer block registers.
27 */
28#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
29#define WDT_EN 0x0010
30#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
31
32#define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK)
33#define WDT_IN_USE 0
34#define WDT_OK_TO_CLOSE 1
35
36static int nowayout = WATCHDOG_NOWAYOUT;
37static int heartbeat = WDT_MAX_DURATION; /* (seconds) */
38static unsigned long wdt_status;
39static spinlock_t wdt_lock;
40
41static void wdt_enable(void)
42{
43 u32 reg;
44
45 spin_lock(&wdt_lock);
46
47 /* Set watchdog duration */
48 writel(ORION5X_TCLK * heartbeat, WDT_VAL);
49
50 /* Clear watchdog timer interrupt */
51 reg = readl(BRIDGE_CAUSE);
52 reg &= ~WDT_INT_REQ;
53 writel(reg, BRIDGE_CAUSE);
54
55 /* Enable watchdog timer */
56 reg = readl(TIMER_CTRL);
57 reg |= WDT_EN;
58 writel(reg, TIMER_CTRL);
59
60 /* Enable reset on watchdog */
61 reg = readl(CPU_RESET_MASK);
62 reg |= WDT_RESET;
63 writel(reg, CPU_RESET_MASK);
64
65 spin_unlock(&wdt_lock);
66}
67
68static void wdt_disable(void)
69{
70 u32 reg;
71
72 spin_lock(&wdt_lock);
73
74 /* Disable reset on watchdog */
75 reg = readl(CPU_RESET_MASK);
76 reg &= ~WDT_RESET;
77 writel(reg, CPU_RESET_MASK);
78
79 /* Disable watchdog timer */
80 reg = readl(TIMER_CTRL);
81 reg &= ~WDT_EN;
82 writel(reg, TIMER_CTRL);
83
84 spin_unlock(&wdt_lock);
85}
86
87static int orion5x_wdt_get_timeleft(int *time_left)
88{
89 spin_lock(&wdt_lock);
90 *time_left = readl(WDT_VAL) / ORION5X_TCLK;
91 spin_unlock(&wdt_lock);
92 return 0;
93}
94
95static int orion5x_wdt_open(struct inode *inode, struct file *file)
96{
97 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
98 return -EBUSY;
99 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
100 wdt_enable();
101 return nonseekable_open(inode, file);
102}
103
104static ssize_t orion5x_wdt_write(struct file *file, const char *data,
105 size_t len, loff_t *ppos)
106{
107 if (len) {
108 if (!nowayout) {
109 size_t i;
110
111 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
112 for (i = 0; i != len; i++) {
113 char c;
114
115 if (get_user(c, data + i))
116 return -EFAULT;
117 if (c == 'V')
118 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
119 }
120 }
121 wdt_enable();
122 }
123 return len;
124}
125
126static struct watchdog_info ident = {
127 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
128 WDIOF_KEEPALIVEPING,
129 .identity = "Orion5x Watchdog",
130};
131
132
133static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
134 unsigned long arg)
135{
136 int ret = -ENOTTY;
137 int time;
138
139 switch (cmd) {
140 case WDIOC_GETSUPPORT:
141 ret = copy_to_user((struct watchdog_info *)arg, &ident,
142 sizeof(ident)) ? -EFAULT : 0;
143 break;
144
145 case WDIOC_GETSTATUS:
146 case WDIOC_GETBOOTSTATUS:
147 ret = put_user(0, (int *)arg);
148 break;
149
150 case WDIOC_KEEPALIVE:
151 wdt_enable();
152 ret = 0;
153 break;
154
155 case WDIOC_SETTIMEOUT:
156 ret = get_user(time, (int *)arg);
157 if (ret)
158 break;
159
160 if (time <= 0 || time > WDT_MAX_DURATION) {
161 ret = -EINVAL;
162 break;
163 }
164 heartbeat = time;
165 wdt_enable();
166 /* Fall through */
167
168 case WDIOC_GETTIMEOUT:
169 ret = put_user(heartbeat, (int *)arg);
170 break;
171
172 case WDIOC_GETTIMELEFT:
173 if (orion5x_wdt_get_timeleft(&time)) {
174 ret = -EINVAL;
175 break;
176 }
177 ret = put_user(time, (int *)arg);
178 break;
179 }
180 return ret;
181}
182
183static int orion5x_wdt_release(struct inode *inode, struct file *file)
184{
185 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
186 wdt_disable();
187 else
188 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
189 "timer will not stop\n");
190 clear_bit(WDT_IN_USE, &wdt_status);
191 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
192
193 return 0;
194}
195
196
197static const struct file_operations orion5x_wdt_fops = {
198 .owner = THIS_MODULE,
199 .llseek = no_llseek,
200 .write = orion5x_wdt_write,
201 .unlocked_ioctl = orion5x_wdt_ioctl,
202 .open = orion5x_wdt_open,
203 .release = orion5x_wdt_release,
204};
205
206static struct miscdevice orion5x_wdt_miscdev = {
207 .minor = WATCHDOG_MINOR,
208 .name = "watchdog",
209 .fops = &orion5x_wdt_fops,
210};
211
212static int __init orion5x_wdt_init(void)
213{
214 int ret;
215
216 spin_lock_init(&wdt_lock);
217
218 ret = misc_register(&orion5x_wdt_miscdev);
219 if (ret == 0)
220 printk("Orion5x Watchdog Timer: heartbeat %d sec\n",
221 heartbeat);
222
223 return ret;
224}
225
226static void __exit orion5x_wdt_exit(void)
227{
228 misc_deregister(&orion5x_wdt_miscdev);
229}
230
231module_init(orion5x_wdt_init);
232module_exit(orion5x_wdt_exit);
233
234MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
235MODULE_DESCRIPTION("Orion5x Processor Watchdog");
236
237module_param(heartbeat, int, 0);
238MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is "
239 __MODULE_STRING(WDT_MAX_DURATION) ")");
240
241module_param(nowayout, int, 0);
242MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
243
244MODULE_LICENSE("GPL");
245MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
new file mode 100644
index 000000000000..c73b5e2919c6
--- /dev/null
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -0,0 +1,392 @@
1/*
2 * w83697ug/uf WDT driver
3 *
4 * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
5 * reused original code to supoprt w83697ug/uf.
6 *
7 * Based on w83627hf_wdt.c which is based on advantechwdt.c
8 * which is based on wdt.c.
9 * Original copyright messages:
10 *
11 * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
12 * added support for W83627THF.
13 *
14 * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
15 *
16 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
17 *
18 * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
19 * http://www.redhat.com
20 *
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version
24 * 2 of the License, or (at your option) any later version.
25 *
26 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
27 * warranty for any of this software. This material is provided
28 * "AS-IS" and at no charge.
29 *
30 * (c) Copyright 1995 Alan Cox <alan@redhat.com>
31 */
32
33#include <linux/module.h>
34#include <linux/moduleparam.h>
35#include <linux/types.h>
36#include <linux/miscdevice.h>
37#include <linux/watchdog.h>
38#include <linux/fs.h>
39#include <linux/ioport.h>
40#include <linux/notifier.h>
41#include <linux/reboot.h>
42#include <linux/init.h>
43#include <linux/spinlock.h>
44#include <linux/io.h>
45#include <linux/uaccess.h>
46
47#include <asm/system.h>
48
49#define WATCHDOG_NAME "w83697ug/uf WDT"
50#define PFX WATCHDOG_NAME ": "
51#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
52
53static unsigned long wdt_is_open;
54static char expect_close;
55static DEFINE_SPINLOCK(io_lock);
56
57static int wdt_io = 0x2e;
58module_param(wdt_io, int, 0);
59MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
60
61static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
62module_param(timeout, int, 0);
63MODULE_PARM_DESC(timeout,
64 "Watchdog timeout in seconds. 1<= timeout <=255 (default="
65 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
66
67static int nowayout = WATCHDOG_NOWAYOUT;
68module_param(nowayout, int, 0);
69MODULE_PARM_DESC(nowayout,
70 "Watchdog cannot be stopped once started (default="
71 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
72
73/*
74 * Kernel methods.
75 */
76
77#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
78#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
79 (same as EFER) */
80#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
81
82static void w83697ug_select_wd_register(void)
83{
84 unsigned char c;
85 unsigned char version;
86
87 outb_p(0x87, WDT_EFER); /* Enter extended function mode */
88 outb_p(0x87, WDT_EFER); /* Again according to manual */
89
90 outb(0x20, WDT_EFER); /* check chip version */
91 version = inb(WDT_EFDR);
92
93 if (version == 0x68) { /* W83697UG */
94 printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
95 "W83697UG/UF found at 0x%04x\n", version, wdt_io);
96
97 outb_p(0x2b, WDT_EFER);
98 c = inb_p(WDT_EFDR); /* select WDT0 */
99 c &= ~0x04;
100 outb_p(0x2b, WDT_EFER);
101 outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
102
103 } else {
104 printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
105 return -EIO;
106 }
107
108 outb_p(0x07, WDT_EFER); /* point to logical device number reg */
109 outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
110 outb_p(0x30, WDT_EFER); /* select CR30 */
111 c = inb_p(WDT_EFDR);
112 outb_p(c || 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
113}
114
115static void w83697ug_unselect_wd_register(void)
116{
117 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
118}
119
120static void w83697ug_init(void)
121{
122 unsigned char t;
123
124 w83697ug_select_wd_register();
125
126 outb_p(0xF6, WDT_EFER); /* Select CRF6 */
127 t = inb_p(WDT_EFDR); /* read CRF6 */
128 if (t != 0) {
129 printk(KERN_INFO PFX "Watchdog already running."
130 " Resetting timeout to %d sec\n", timeout);
131 outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
132 }
133 outb_p(0xF5, WDT_EFER); /* Select CRF5 */
134 t = inb_p(WDT_EFDR); /* read CRF5 */
135 t &= ~0x0C; /* set second mode &
136 disable keyboard turning off watchdog */
137 outb_p(t, WDT_EFDR); /* Write back to CRF5 */
138
139 w83697ug_unselect_wd_register();
140}
141
142static void wdt_ctrl(int timeout)
143{
144 spin_lock(&io_lock);
145
146 w83697ug_select_wd_register();
147
148 outb_p(0xF4, WDT_EFER); /* Select CRF4 */
149 outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
150
151 w83697ug_unselect_wd_register();
152
153 spin_unlock(&io_lock);
154}
155
156static int wdt_ping(void)
157{
158 wdt_ctrl(timeout);
159 return 0;
160}
161
162static int wdt_disable(void)
163{
164 wdt_ctrl(0);
165 return 0;
166}
167
168static int wdt_set_heartbeat(int t)
169{
170 if (t < 1 || t > 255)
171 return -EINVAL;
172
173 timeout = t;
174 return 0;
175}
176
177static ssize_t wdt_write(struct file *file, const char __user *buf,
178 size_t count, loff_t *ppos)
179{
180 if (count) {
181 if (!nowayout) {
182 size_t i;
183
184 expect_close = 0;
185
186 for (i = 0; i != count; i++) {
187 char c;
188 if (get_user(c, buf + i))
189 return -EFAULT;
190 if (c == 'V')
191 expect_close = 42;
192 }
193 }
194 wdt_ping();
195 }
196 return count;
197}
198
199static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
200{
201 void __user *argp = (void __user *)arg;
202 int __user *p = argp;
203 int new_timeout;
204 static const struct watchdog_info ident = {
205 .options = WDIOF_KEEPALIVEPING |
206 WDIOF_SETTIMEOUT |
207 WDIOF_MAGICCLOSE,
208 .firmware_version = 1,
209 .identity = "W83697UG WDT",
210 };
211
212 switch (cmd) {
213 case WDIOC_GETSUPPORT:
214 if (copy_to_user(argp, &ident, sizeof(ident)))
215 return -EFAULT;
216 break;
217
218 case WDIOC_GETSTATUS:
219 case WDIOC_GETBOOTSTATUS:
220 return put_user(0, p);
221
222 case WDIOC_SETOPTIONS:
223 {
224 int options, retval = -EINVAL;
225
226 if (get_user(options, p))
227 return -EFAULT;
228
229 if (options & WDIOS_DISABLECARD) {
230 wdt_disable();
231 retval = 0;
232 }
233
234 if (options & WDIOS_ENABLECARD) {
235 wdt_ping();
236 retval = 0;
237 }
238
239 return retval;
240 }
241
242 case WDIOC_KEEPALIVE:
243 wdt_ping();
244 break;
245
246 case WDIOC_SETTIMEOUT:
247 if (get_user(new_timeout, p))
248 return -EFAULT;
249 if (wdt_set_heartbeat(new_timeout))
250 return -EINVAL;
251 wdt_ping();
252 /* Fall */
253
254 case WDIOC_GETTIMEOUT:
255 return put_user(timeout, p);
256
257 default:
258 return -ENOTTY;
259 }
260 return 0;
261}
262
263static int wdt_open(struct inode *inode, struct file *file)
264{
265 if (test_and_set_bit(0, &wdt_is_open))
266 return -EBUSY;
267 /*
268 * Activate
269 */
270
271 wdt_ping();
272 return nonseekable_open(inode, file);
273}
274
275static int wdt_close(struct inode *inode, struct file *file)
276{
277 if (expect_close == 42)
278 wdt_disable();
279 else {
280 printk(KERN_CRIT PFX
281 "Unexpected close, not stopping watchdog!\n");
282 wdt_ping();
283 }
284 expect_close = 0;
285 clear_bit(0, &wdt_is_open);
286 return 0;
287}
288
289/*
290 * Notifier for system down
291 */
292
293static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
294 void *unused)
295{
296 if (code == SYS_DOWN || code == SYS_HALT)
297 wdt_disable(); /* Turn the WDT off */
298
299 return NOTIFY_DONE;
300}
301
302/*
303 * Kernel Interfaces
304 */
305
306static const struct file_operations wdt_fops = {
307 .owner = THIS_MODULE,
308 .llseek = no_llseek,
309 .write = wdt_write,
310 .unlocked_ioctl = wdt_ioctl,
311 .open = wdt_open,
312 .release = wdt_close,
313};
314
315static struct miscdevice wdt_miscdev = {
316 .minor = WATCHDOG_MINOR,
317 .name = "watchdog",
318 .fops = &wdt_fops,
319};
320
321/*
322 * The WDT needs to learn about soft shutdowns in order to
323 * turn the timebomb registers off.
324 */
325
326static struct notifier_block wdt_notifier = {
327 .notifier_call = wdt_notify_sys,
328};
329
330static int __init wdt_init(void)
331{
332 int ret;
333
334 printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n");
335
336 if (wdt_set_heartbeat(timeout)) {
337 wdt_set_heartbeat(WATCHDOG_TIMEOUT);
338 printk(KERN_INFO PFX
339 "timeout value must be 1<=timeout<=255, using %d\n",
340 WATCHDOG_TIMEOUT);
341 }
342
343 if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
344 printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
345 wdt_io);
346 ret = -EIO;
347 goto out;
348 }
349
350 w83697ug_init();
351
352 ret = register_reboot_notifier(&wdt_notifier);
353 if (ret != 0) {
354 printk(KERN_ERR PFX
355 "cannot register reboot notifier (err=%d)\n", ret);
356 goto unreg_regions;
357 }
358
359 ret = misc_register(&wdt_miscdev);
360 if (ret != 0) {
361 printk(KERN_ERR PFX
362 "cannot register miscdev on minor=%d (err=%d)\n",
363 WATCHDOG_MINOR, ret);
364 goto unreg_reboot;
365 }
366
367 printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
368 timeout, nowayout);
369
370out:
371 return ret;
372unreg_reboot:
373 unregister_reboot_notifier(&wdt_notifier);
374unreg_regions:
375 release_region(wdt_io, 1);
376 goto out;
377}
378
379static void __exit wdt_exit(void)
380{
381 misc_deregister(&wdt_miscdev);
382 unregister_reboot_notifier(&wdt_notifier);
383 release_region(wdt_io, 1);
384}
385
386module_init(wdt_init);
387module_exit(wdt_exit);
388
389MODULE_LICENSE("GPL");
390MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
391MODULE_DESCRIPTION("w83697ug/uf WDT driver");
392MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);