diff options
-rw-r--r-- | drivers/char/watchdog/Kconfig | 7 | ||||
-rw-r--r-- | drivers/char/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/watchdog/mtx-1_wdt.c | 238 |
3 files changed, 246 insertions, 0 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index e2adc7f57873..1cad32c62ed3 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig | |||
@@ -551,6 +551,13 @@ config INDYDOG | |||
551 | timer expired and no process has written to /dev/watchdog during | 551 | timer expired and no process has written to /dev/watchdog during |
552 | that time. | 552 | that time. |
553 | 553 | ||
554 | config WDT_MTX1 | ||
555 | tristate "MTX-1 Hardware Watchdog" | ||
556 | depends on MIPS_MTX1 | ||
557 | help | ||
558 | Hardware driver for the MTX-1 boards. This is a watchdog timer that | ||
559 | will reboot the machine after a 100 seconds timer expired. | ||
560 | |||
554 | config WDT_RM9K_GPI | 561 | config WDT_RM9K_GPI |
555 | tristate "RM9000/GPI hardware watchdog" | 562 | tristate "RM9000/GPI hardware watchdog" |
556 | depends on CPU_RM9000 | 563 | depends on CPU_RM9000 |
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 387eb80b404f..8bfc00cc7c2b 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile | |||
@@ -72,6 +72,7 @@ obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o | |||
72 | 72 | ||
73 | # MIPS Architecture | 73 | # MIPS Architecture |
74 | obj-$(CONFIG_INDYDOG) += indydog.o | 74 | obj-$(CONFIG_INDYDOG) += indydog.o |
75 | obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o | ||
75 | obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o | 76 | obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o |
76 | 77 | ||
77 | # S390 Architecture | 78 | # S390 Architecture |
diff --git a/drivers/char/watchdog/mtx-1_wdt.c b/drivers/char/watchdog/mtx-1_wdt.c new file mode 100644 index 000000000000..419ab445c944 --- /dev/null +++ b/drivers/char/watchdog/mtx-1_wdt.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * Driver for the MTX-1 Watchdog. | ||
3 | * | ||
4 | * (C) Copyright 2005 4G Systems <info@4g-systems.biz>, All Rights Reserved. | ||
5 | * http://www.4g-systems.biz | ||
6 | * | ||
7 | * (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * Neither Michael Stickel nor 4G Systems admit liability nor provide | ||
15 | * warranty for any of this software. This material is provided | ||
16 | * "AS-IS" and at no charge. | ||
17 | * | ||
18 | * (c) Copyright 2005 4G Systems <info@4g-systems.biz> | ||
19 | * | ||
20 | * Release 0.01. | ||
21 | * Author: Michael Stickel michael.stickel@4g-systems.biz | ||
22 | * | ||
23 | * Release 0.02. | ||
24 | * Author: Florian Fainelli florian@openwrt.org | ||
25 | * use the Linux watchdog/timer APIs | ||
26 | * | ||
27 | * The Watchdog is configured to reset the MTX-1 | ||
28 | * if it is not triggered for 100 seconds. | ||
29 | * It should not be triggered more often than 1.6 seconds. | ||
30 | * | ||
31 | * A timer triggers the watchdog every 5 seconds, until | ||
32 | * it is opened for the first time. After the first open | ||
33 | * it MUST be triggered every 2..95 seconds. | ||
34 | */ | ||
35 | |||
36 | #include <linux/module.h> | ||
37 | #include <linux/moduleparam.h> | ||
38 | #include <linux/types.h> | ||
39 | #include <linux/errno.h> | ||
40 | #include <linux/miscdevice.h> | ||
41 | #include <linux/fs.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/ioport.h> | ||
44 | #include <linux/timer.h> | ||
45 | #include <linux/completion.h> | ||
46 | #include <linux/jiffies.h> | ||
47 | #include <linux/watchdog.h> | ||
48 | #include <asm/io.h> | ||
49 | #include <asm/uaccess.h> | ||
50 | |||
51 | #include <asm/mach-au1x00/au1000.h> | ||
52 | |||
53 | #define MTX1_WDT_INTERVAL (5 * HZ) | ||
54 | |||
55 | static int ticks = 100 * HZ; | ||
56 | |||
57 | static struct { | ||
58 | struct completion stop; | ||
59 | volatile int running; | ||
60 | struct timer_list timer; | ||
61 | volatile int queue; | ||
62 | int default_ticks; | ||
63 | unsigned long inuse; | ||
64 | } mtx1_wdt_device; | ||
65 | |||
66 | static void mtx1_wdt_trigger(unsigned long unused) | ||
67 | { | ||
68 | u32 tmp; | ||
69 | |||
70 | if (mtx1_wdt_device.running) | ||
71 | ticks--; | ||
72 | /* | ||
73 | * toggle GPIO2_15 | ||
74 | */ | ||
75 | tmp = au_readl(GPIO2_DIR); | ||
76 | tmp = (tmp & ~(1<<15)) | ((~tmp) & (1<<15)); | ||
77 | au_writel (tmp, GPIO2_DIR); | ||
78 | |||
79 | if (mtx1_wdt_device.queue && ticks) | ||
80 | mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); | ||
81 | else { | ||
82 | complete(&mtx1_wdt_device.stop); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | static void mtx1_wdt_reset(void) | ||
87 | { | ||
88 | ticks = mtx1_wdt_device.default_ticks; | ||
89 | } | ||
90 | |||
91 | |||
92 | static void mtx1_wdt_start(void) | ||
93 | { | ||
94 | if (!mtx1_wdt_device.queue) { | ||
95 | mtx1_wdt_device.queue = 1; | ||
96 | au_writel (au_readl(GPIO2_DIR) | (u32)(1<<15), GPIO2_DIR); | ||
97 | mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); | ||
98 | } | ||
99 | mtx1_wdt_device.running++; | ||
100 | } | ||
101 | |||
102 | static int mtx1_wdt_stop(void) | ||
103 | { | ||
104 | if (mtx1_wdt_device.queue) { | ||
105 | mtx1_wdt_device.queue = 0; | ||
106 | au_writel (au_readl(GPIO2_DIR) & ~((u32)(1<<15)), GPIO2_DIR); | ||
107 | } | ||
108 | |||
109 | ticks = mtx1_wdt_device.default_ticks; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | /* Filesystem functions */ | ||
115 | |||
116 | static int mtx1_wdt_open(struct inode *inode, struct file *file) | ||
117 | { | ||
118 | if (test_and_set_bit(0, &mtx1_wdt_device.inuse)) | ||
119 | return -EBUSY; | ||
120 | |||
121 | return nonseekable_open(inode, file); | ||
122 | } | ||
123 | |||
124 | |||
125 | static int mtx1_wdt_release(struct inode *inode, struct file *file) | ||
126 | { | ||
127 | clear_bit(0, &mtx1_wdt_device.inuse); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int mtx1_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | ||
132 | { | ||
133 | void __user *argp = (void __user *)arg; | ||
134 | unsigned int value; | ||
135 | static struct watchdog_info ident = | ||
136 | { | ||
137 | .options = WDIOF_CARDRESET, | ||
138 | .identity = "MTX-1 WDT", | ||
139 | }; | ||
140 | |||
141 | switch(cmd) { | ||
142 | case WDIOC_KEEPALIVE: | ||
143 | mtx1_wdt_reset(); | ||
144 | break; | ||
145 | case WDIOC_GETSTATUS: | ||
146 | if ( copy_to_user(argp, &value, sizeof(int)) ) | ||
147 | return -EFAULT; | ||
148 | break; | ||
149 | case WDIOC_GETSUPPORT: | ||
150 | if ( copy_to_user(argp, &ident, sizeof(ident)) ) | ||
151 | return -EFAULT; | ||
152 | break; | ||
153 | case WDIOC_SETOPTIONS: | ||
154 | if ( copy_from_user(&value, argp, sizeof(int)) ) | ||
155 | return -EFAULT; | ||
156 | switch(value) { | ||
157 | case WDIOS_ENABLECARD: | ||
158 | mtx1_wdt_start(); | ||
159 | break; | ||
160 | case WDIOS_DISABLECARD: | ||
161 | return mtx1_wdt_stop(); | ||
162 | default: | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | break; | ||
166 | default: | ||
167 | return -ENOTTY; | ||
168 | } | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | |||
173 | static ssize_t mtx1_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) | ||
174 | { | ||
175 | if (!count) | ||
176 | return -EIO; | ||
177 | |||
178 | mtx1_wdt_reset(); | ||
179 | return count; | ||
180 | } | ||
181 | |||
182 | static struct file_operations mtx1_wdt_fops = { | ||
183 | .owner = THIS_MODULE, | ||
184 | .llseek = no_llseek, | ||
185 | .ioctl = mtx1_wdt_ioctl, | ||
186 | .open = mtx1_wdt_open, | ||
187 | .write = mtx1_wdt_write, | ||
188 | .release = mtx1_wdt_release | ||
189 | }; | ||
190 | |||
191 | |||
192 | static struct miscdevice mtx1_wdt_misc = { | ||
193 | .minor = WATCHDOG_MINOR, | ||
194 | .name = "watchdog", | ||
195 | .fops = &mtx1_wdt_fops | ||
196 | }; | ||
197 | |||
198 | |||
199 | static int __init mtx1_wdt_init(void) | ||
200 | { | ||
201 | int ret; | ||
202 | |||
203 | if ((ret = misc_register(&mtx1_wdt_misc)) < 0) { | ||
204 | printk(KERN_ERR " mtx-1_wdt : failed to register\n"); | ||
205 | return ret; | ||
206 | } | ||
207 | |||
208 | init_completion(&mtx1_wdt_device.stop); | ||
209 | mtx1_wdt_device.queue = 0; | ||
210 | |||
211 | clear_bit(0, &mtx1_wdt_device.inuse); | ||
212 | |||
213 | setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L); | ||
214 | |||
215 | mtx1_wdt_device.default_ticks = ticks; | ||
216 | |||
217 | mtx1_wdt_start(); | ||
218 | |||
219 | printk(KERN_INFO "MTX-1 Watchdog driver\n"); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static void __exit mtx1_wdt_exit(void) | ||
225 | { | ||
226 | if (mtx1_wdt_device.queue) { | ||
227 | mtx1_wdt_device.queue = 0; | ||
228 | wait_for_completion(&mtx1_wdt_device.stop); | ||
229 | } | ||
230 | misc_deregister(&mtx1_wdt_misc); | ||
231 | } | ||
232 | |||
233 | module_init(mtx1_wdt_init); | ||
234 | module_exit(mtx1_wdt_exit); | ||
235 | |||
236 | MODULE_AUTHOR("Michael Stickel, Florian Fainelli"); | ||
237 | MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); | ||
238 | MODULE_LICENSE("GPL"); | ||