diff options
Diffstat (limited to 'drivers/watchdog/mtx-1_wdt.c')
-rw-r--r-- | drivers/watchdog/mtx-1_wdt.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c new file mode 100644 index 000000000000..dcfd401a7ad7 --- /dev/null +++ b/drivers/watchdog/mtx-1_wdt.c | |||
@@ -0,0 +1,239 @@ | |||
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 | case WDIOC_GETBOOTSTATUS: | ||
147 | if ( copy_to_user(argp, &value, sizeof(int)) ) | ||
148 | return -EFAULT; | ||
149 | break; | ||
150 | case WDIOC_GETSUPPORT: | ||
151 | if ( copy_to_user(argp, &ident, sizeof(ident)) ) | ||
152 | return -EFAULT; | ||
153 | break; | ||
154 | case WDIOC_SETOPTIONS: | ||
155 | if ( copy_from_user(&value, argp, sizeof(int)) ) | ||
156 | return -EFAULT; | ||
157 | switch(value) { | ||
158 | case WDIOS_ENABLECARD: | ||
159 | mtx1_wdt_start(); | ||
160 | break; | ||
161 | case WDIOS_DISABLECARD: | ||
162 | return mtx1_wdt_stop(); | ||
163 | default: | ||
164 | return -EINVAL; | ||
165 | } | ||
166 | break; | ||
167 | default: | ||
168 | return -ENOTTY; | ||
169 | } | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | |||
174 | static ssize_t mtx1_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) | ||
175 | { | ||
176 | if (!count) | ||
177 | return -EIO; | ||
178 | |||
179 | mtx1_wdt_reset(); | ||
180 | return count; | ||
181 | } | ||
182 | |||
183 | static struct file_operations mtx1_wdt_fops = { | ||
184 | .owner = THIS_MODULE, | ||
185 | .llseek = no_llseek, | ||
186 | .ioctl = mtx1_wdt_ioctl, | ||
187 | .open = mtx1_wdt_open, | ||
188 | .write = mtx1_wdt_write, | ||
189 | .release = mtx1_wdt_release | ||
190 | }; | ||
191 | |||
192 | |||
193 | static struct miscdevice mtx1_wdt_misc = { | ||
194 | .minor = WATCHDOG_MINOR, | ||
195 | .name = "watchdog", | ||
196 | .fops = &mtx1_wdt_fops | ||
197 | }; | ||
198 | |||
199 | |||
200 | static int __init mtx1_wdt_init(void) | ||
201 | { | ||
202 | int ret; | ||
203 | |||
204 | if ((ret = misc_register(&mtx1_wdt_misc)) < 0) { | ||
205 | printk(KERN_ERR " mtx-1_wdt : failed to register\n"); | ||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | init_completion(&mtx1_wdt_device.stop); | ||
210 | mtx1_wdt_device.queue = 0; | ||
211 | |||
212 | clear_bit(0, &mtx1_wdt_device.inuse); | ||
213 | |||
214 | setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L); | ||
215 | |||
216 | mtx1_wdt_device.default_ticks = ticks; | ||
217 | |||
218 | mtx1_wdt_start(); | ||
219 | |||
220 | printk(KERN_INFO "MTX-1 Watchdog driver\n"); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static void __exit mtx1_wdt_exit(void) | ||
226 | { | ||
227 | if (mtx1_wdt_device.queue) { | ||
228 | mtx1_wdt_device.queue = 0; | ||
229 | wait_for_completion(&mtx1_wdt_device.stop); | ||
230 | } | ||
231 | misc_deregister(&mtx1_wdt_misc); | ||
232 | } | ||
233 | |||
234 | module_init(mtx1_wdt_init); | ||
235 | module_exit(mtx1_wdt_exit); | ||
236 | |||
237 | MODULE_AUTHOR("Michael Stickel, Florian Fainelli"); | ||
238 | MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); | ||
239 | MODULE_LICENSE("GPL"); | ||