diff options
author | Denis Turischev <denis@compulab.co.il> | 2009-07-21 06:13:29 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2009-09-18 04:34:56 -0400 |
commit | 3a5f90002e9d08e5a6406fc650bfd838bf23bc1b (patch) | |
tree | 4c8865511780f18214bad55de595687e242305cb /drivers/watchdog/sbc_fitpc2_wdt.c | |
parent | df58bee21ed218cb7dfb561a590b1bd2a99531cf (diff) |
[WATCHDOG] add SBC-FITPC2 watchdog driver
Add support for watchdog found on SBC-FITPC2 board.
Signed-off-by: Denis Turischev <denis@compulab.co.il>
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers/watchdog/sbc_fitpc2_wdt.c')
-rw-r--r-- | drivers/watchdog/sbc_fitpc2_wdt.c | 267 |
1 files changed, 267 insertions, 0 deletions
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 | |||
30 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
31 | static unsigned int margin = 60; /* (secs) Default is 1 minute */ | ||
32 | static unsigned long wdt_status; | ||
33 | static 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 | |||
46 | static 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 | |||
54 | static 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 | |||
62 | static 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 | |||
70 | static 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 | |||
82 | static 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 | |||
107 | out: | ||
108 | wdt_enable(); | ||
109 | |||
110 | return len; | ||
111 | } | ||
112 | |||
113 | |||
114 | static struct watchdog_info ident = { | ||
115 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | | ||
116 | WDIOF_KEEPALIVEPING, | ||
117 | .identity = WATCHDOG_NAME, | ||
118 | }; | ||
119 | |||
120 | |||
121 | static 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 | |||
168 | static 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 | |||
186 | static 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 | |||
195 | static struct miscdevice fitpc2_wdt_miscdev = { | ||
196 | .minor = WATCHDOG_MINOR, | ||
197 | .name = "watchdog", | ||
198 | .fops = &fitpc2_wdt_fops, | ||
199 | }; | ||
200 | |||
201 | static 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 | |||
238 | err_margin: | ||
239 | release_region(DATA_PORT, 1); | ||
240 | err_data_port: | ||
241 | release_region(COMMAND_PORT, 1); | ||
242 | |||
243 | return err; | ||
244 | } | ||
245 | |||
246 | static 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 | |||
253 | module_init(fitpc2_wdt_init); | ||
254 | module_exit(fitpc2_wdt_exit); | ||
255 | |||
256 | MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); | ||
257 | MODULE_DESCRIPTION("SBC-FITPC2 Watchdog"); | ||
258 | |||
259 | module_param(margin, int, 0); | ||
260 | MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); | ||
261 | |||
262 | module_param(nowayout, int, 0); | ||
263 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | ||
264 | |||
265 | MODULE_LICENSE("GPL"); | ||
266 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
267 | |||