Blame view

drivers/watchdog/mixcomwd.c 7.39 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   * MixCom Watchdog: A Simple Hardware Watchdog Device
   * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
   *
   * Author: Gergely Madarasz <gorgo@itc.hu>
   *
   * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   *
   * Version 0.1 (99/04/15):
   *		- first version
   *
   * Version 0.2 (99/06/16):
   *		- added kernel timer watchdog ping after close
   *		  since the hardware does not support watchdog shutdown
   *
   * Version 0.3 (99/06/21):
   *		- added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
   *
   * Version 0.3.1 (99/06/22):
   *		- allow module removal while internal timer is active,
   *		  print warning about probable reset
   *
   * Version 0.4 (99/11/15):
   *		- support for one more type board
   *
   * Version 0.5 (2001/12/14) Matt Domsch <Matt_Domsch@dell.com>
393096453   Alan Cox   [WATCHDOG 25/57] ...
32
33
   *		- added nowayout module option to override
   *		  CONFIG_WATCHDOG_NOWAYOUT
b10958d33   Wim Van Sebroeck   [WATCHDOG] Mixcom...
34
35
36
37
38
   *
   * Version 0.6 (2002/04/12): Rob Radez <rob@osinvestor.com>
   *		- make mixcomwd_opened unsigned,
   *		  removed lock_kernel/unlock_kernel from mixcomwd_release,
   *		  modified ioctl a bit to conform to API
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
   *
   */
b10958d33   Wim Van Sebroeck   [WATCHDOG] Mixcom...
41
  #define VERSION "0.6"
1c067318a   Wim Van Sebroeck   [WATCHDOG] Mixcom...
42
43
  #define WATCHDOG_NAME "mixcomwd"
  #define PFX WATCHDOG_NAME ": "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
  #include <linux/types.h>
  #include <linux/miscdevice.h>
  #include <linux/ioport.h>
  #include <linux/watchdog.h>
  #include <linux/fs.h>
  #include <linux/reboot.h>
  #include <linux/init.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
54
55
  #include <linux/jiffies.h>
  #include <linux/timer.h>
393096453   Alan Cox   [WATCHDOG 25/57] ...
56
57
  #include <linux/uaccess.h>
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58

63e6e17ea   Wim Van Sebroeck   [WATCHDOG] Mixcom...
59
60
61
62
63
64
65
66
67
68
  /*
   * We have two types of cards that can be probed:
   * 1) The Mixcom cards: these cards can be found at addresses
   *    0x180, 0x280, 0x380 with an additional offset of 0xc10.
   *    (Or 0xd90, 0xe90, 0xf90).
   * 2) The FlashCOM cards: these cards can be set up at
   *    0x300 -> 0x378, in 0x8 jumps with an offset of 0x04.
   *    (Or 0x304 -> 0x37c in 0x8 jumps).
   *    Each card has it's own ID.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  #define MIXCOM_ID 0x11
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  #define FLASHCOM_ID 0x18
21baf3c7c   Wim Van Sebroeck   [WATCHDOG] Mixcom...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  static struct {
  	int ioport;
  	int id;
  } mixcomwd_io_info[] __devinitdata = {
  	/* The Mixcom cards */
  	{0x0d90, MIXCOM_ID},
  	{0x0e90, MIXCOM_ID},
  	{0x0f90, MIXCOM_ID},
  	/* The FlashCOM cards */
  	{0x0304, FLASHCOM_ID},
  	{0x030c, FLASHCOM_ID},
  	{0x0314, FLASHCOM_ID},
  	{0x031c, FLASHCOM_ID},
  	{0x0324, FLASHCOM_ID},
  	{0x032c, FLASHCOM_ID},
  	{0x0334, FLASHCOM_ID},
  	{0x033c, FLASHCOM_ID},
  	{0x0344, FLASHCOM_ID},
  	{0x034c, FLASHCOM_ID},
  	{0x0354, FLASHCOM_ID},
  	{0x035c, FLASHCOM_ID},
  	{0x0364, FLASHCOM_ID},
  	{0x036c, FLASHCOM_ID},
  	{0x0374, FLASHCOM_ID},
  	{0x037c, FLASHCOM_ID},
  	/* The end of the list */
  	{0x0000, 0},
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99

82eb7c505   Jiri Slaby   [WATCHDOG] timers...
100
  static void mixcomwd_timerfun(unsigned long d);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
  static unsigned long mixcomwd_opened; /* long req'd for setbit --RR */
  
  static int watchdog_port;
  static int mixcomwd_timer_alive;
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
105
  static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  static char expect_close;
4bfdf3783   Andrey Panin   [PATCH] consolida...
107
  static int nowayout = WATCHDOG_NOWAYOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  module_param(nowayout, int, 0);
393096453   Alan Cox   [WATCHDOG 25/57] ...
109
110
111
  MODULE_PARM_DESC(nowayout,
  		"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
  
  static void mixcomwd_ping(void)
  {
393096453   Alan Cox   [WATCHDOG 25/57] ...
115
  	outb_p(55, watchdog_port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
  	return;
  }
  
  static void mixcomwd_timerfun(unsigned long d)
  {
  	mixcomwd_ping();
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
122
  	mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
126
127
128
129
130
  }
  
  /*
   *	Allow only one person to hold it open
   */
  
  static int mixcomwd_open(struct inode *inode, struct file *file)
  {
393096453   Alan Cox   [WATCHDOG 25/57] ...
131
  	if (test_and_set_bit(0, &mixcomwd_opened))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  		return -EBUSY;
393096453   Alan Cox   [WATCHDOG 25/57] ...
133

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  	mixcomwd_ping();
393096453   Alan Cox   [WATCHDOG 25/57] ...
135
  	if (nowayout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
  		/*
  		 * fops_get() code via open() has already done
  		 * a try_module_get() so it is safe to do the
  		 * __module_get().
  		 */
  		__module_get(THIS_MODULE);
393096453   Alan Cox   [WATCHDOG 25/57] ...
142
143
  	else {
  		if (mixcomwd_timer_alive) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  			del_timer(&mixcomwd_timer);
393096453   Alan Cox   [WATCHDOG 25/57] ...
145
  			mixcomwd_timer_alive = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
151
152
153
  		}
  	}
  	return nonseekable_open(inode, file);
  }
  
  static int mixcomwd_release(struct inode *inode, struct file *file)
  {
  	if (expect_close == 42) {
393096453   Alan Cox   [WATCHDOG 25/57] ...
154
155
156
  		if (mixcomwd_timer_alive) {
  			printk(KERN_ERR PFX
  				"release called while internal timer alive");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
  			return -EBUSY;
  		}
393096453   Alan Cox   [WATCHDOG 25/57] ...
159
  		mixcomwd_timer_alive = 1;
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
160
  		mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
393096453   Alan Cox   [WATCHDOG 25/57] ...
161
162
163
164
  	} else
  		printk(KERN_CRIT PFX
  		    "WDT device closed unexpectedly.  WDT will not stop!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165

393096453   Alan Cox   [WATCHDOG 25/57] ...
166
167
  	clear_bit(0, &mixcomwd_opened);
  	expect_close = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  	return 0;
  }
393096453   Alan Cox   [WATCHDOG 25/57] ...
170
171
  static ssize_t mixcomwd_write(struct file *file, const char __user *data,
  						size_t len, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  {
393096453   Alan Cox   [WATCHDOG 25/57] ...
173
  	if (len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  		if (!nowayout) {
  			size_t i;
  
  			/* In case it was set long ago */
  			expect_close = 0;
  
  			for (i = 0; i != len; i++) {
  				char c;
  				if (get_user(c, data + i))
  					return -EFAULT;
  				if (c == 'V')
  					expect_close = 42;
  			}
  		}
  		mixcomwd_ping();
  	}
  	return len;
  }
393096453   Alan Cox   [WATCHDOG 25/57] ...
192
193
  static long mixcomwd_ioctl(struct file *file,
  				unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
  {
  	void __user *argp = (void __user *)arg;
  	int __user *p = argp;
  	int status;
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
198
  	static const struct watchdog_info ident = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
  		.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  		.firmware_version = 1,
  		.identity = "MixCOM watchdog",
  	};
393096453   Alan Cox   [WATCHDOG 25/57] ...
203
  	switch (cmd) {
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
204
205
206
207
  	case WDIOC_GETSUPPORT:
  		if (copy_to_user(argp, &ident, sizeof(ident)))
  			return -EFAULT;
  		break;
393096453   Alan Cox   [WATCHDOG 25/57] ...
208
209
210
211
212
213
214
  	case WDIOC_GETSTATUS:
  		status = mixcomwd_opened;
  		if (!nowayout)
  			status |= mixcomwd_timer_alive;
  		return put_user(status, p);
  	case WDIOC_GETBOOTSTATUS:
  		return put_user(0, p);
393096453   Alan Cox   [WATCHDOG 25/57] ...
215
216
217
218
219
  	case WDIOC_KEEPALIVE:
  		mixcomwd_ping();
  		break;
  	default:
  		return -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
  	}
  	return 0;
  }
10a29304f   Wim Van Sebroeck   [WATCHDOG] Mixcom...
223
  static const struct file_operations mixcomwd_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
  	.write		= mixcomwd_write,
393096453   Alan Cox   [WATCHDOG 25/57] ...
227
  	.unlocked_ioctl	= mixcomwd_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
  	.open		= mixcomwd_open,
  	.release	= mixcomwd_release,
  };
10a29304f   Wim Van Sebroeck   [WATCHDOG] Mixcom...
231
  static struct miscdevice mixcomwd_miscdev = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
  	.minor	= WATCHDOG_MINOR,
  	.name	= "watchdog",
  	.fops	= &mixcomwd_fops,
  };
4194db10f   Wim Van Sebroeck   [WATCHDOG] Mixcom...
236
  static int __init checkcard(int port, int card_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
  {
  	int id;
393096453   Alan Cox   [WATCHDOG 25/57] ...
239
  	if (!request_region(port, 1, "MixCOM watchdog"))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241

393096453   Alan Cox   [WATCHDOG 25/57] ...
242
243
  	id = inb_p(port);
  	if (card_id == MIXCOM_ID)
4194db10f   Wim Van Sebroeck   [WATCHDOG] Mixcom...
244
  		id &= 0x3f;
393096453   Alan Cox   [WATCHDOG 25/57] ...
245
  	if (id != card_id) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
  		release_region(port, 1);
  		return 0;
  	}
4194db10f   Wim Van Sebroeck   [WATCHDOG] Mixcom...
249
250
  	return 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
  
  static int __init mixcomwd_init(void)
  {
393096453   Alan Cox   [WATCHDOG 25/57] ...
254
  	int i, ret, found = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255

21baf3c7c   Wim Van Sebroeck   [WATCHDOG] Mixcom...
256
257
258
  	for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) {
  		if (checkcard(mixcomwd_io_info[i].ioport,
  			      mixcomwd_io_info[i].id)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  			found = 1;
21baf3c7c   Wim Van Sebroeck   [WATCHDOG] Mixcom...
260
  			watchdog_port = mixcomwd_io_info[i].ioport;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
  		}
  	}
  
  	if (!found) {
393096453   Alan Cox   [WATCHDOG 25/57] ...
265
266
267
  		printk(KERN_ERR PFX
  			"No card detected, or port not available.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
270
271
  		return -ENODEV;
  	}
  
  	ret = misc_register(&mixcomwd_miscdev);
393096453   Alan Cox   [WATCHDOG 25/57] ...
272
273
274
275
276
  	if (ret) {
  		printk(KERN_ERR PFX
  			"cannot register miscdev on minor=%d (err=%d)
  ",
  					WATCHDOG_MINOR, ret);
27c7742e7   Wim Van Sebroeck   [WATCHDOG] Mixcom...
277
  		goto error_misc_register_watchdog;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  	}
393096453   Alan Cox   [WATCHDOG 25/57] ...
279
280
281
282
  	printk(KERN_INFO
  		"MixCOM watchdog driver v%s, watchdog port at 0x%3x
  ",
  					VERSION, watchdog_port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
  
  	return 0;
27c7742e7   Wim Van Sebroeck   [WATCHDOG] Mixcom...
285
286
287
288
289
  
  error_misc_register_watchdog:
  	release_region(watchdog_port, 1);
  	watchdog_port = 0x0000;
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
  }
  
  static void __exit mixcomwd_exit(void)
  {
  	if (!nowayout) {
393096453   Alan Cox   [WATCHDOG 25/57] ...
295
  		if (mixcomwd_timer_alive) {
1c067318a   Wim Van Sebroeck   [WATCHDOG] Mixcom...
296
  			printk(KERN_WARNING PFX "I quit now, hardware will"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
  			       " probably reboot!
  ");
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
299
  			del_timer_sync(&mixcomwd_timer);
393096453   Alan Cox   [WATCHDOG 25/57] ...
300
  			mixcomwd_timer_alive = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  	misc_deregister(&mixcomwd_miscdev);
393096453   Alan Cox   [WATCHDOG 25/57] ...
304
  	release_region(watchdog_port, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
307
308
309
310
311
  }
  
  module_init(mixcomwd_init);
  module_exit(mixcomwd_exit);
  
  MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
  MODULE_DESCRIPTION("MixCom Watchdog driver");
1c067318a   Wim Van Sebroeck   [WATCHDOG] Mixcom...
312
  MODULE_VERSION(VERSION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);