PageRenderTime 49ms CodeModel.GetById 8ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/drivers/char/watchdog/advantechwdt.c

https://bitbucket.org/evzijst/gittest
C | 333 lines | 225 code | 50 blank | 58 comment | 37 complexity | f88a2a8162e8e140e110bba801fff67a MD5 | raw file
  1/*
  2 *	Advantech Single Board Computer WDT driver
  3 *
  4 *	(c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
  5 *
  6 *	Based on acquirewdt.c which is based on wdt.c.
  7 *	Original copyright messages:
  8 *
  9 *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
 10 *				http://www.redhat.com
 11 *
 12 *	This program is free software; you can redistribute it and/or
 13 *	modify it under the terms of the GNU General Public License
 14 *	as published by the Free Software Foundation; either version
 15 *	2 of the License, or (at your option) any later version.
 16 *
 17 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
 18 *	warranty for any of this software. This material is provided
 19 *	"AS-IS" and at no charge.
 20 *
 21 *	(c) Copyright 1995    Alan Cox <alan@redhat.com>
 22 *
 23 *	14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
 24 *	    Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
 25 *
 26 *	16-Oct-2002 Rob Radez <rob@osinvestor.com>
 27 *	    Clean up ioctls, clean up init + exit, add expect close support,
 28 *	    add wdt_start and wdt_stop as parameters.
 29 */
 30
 31#include <linux/module.h>
 32#include <linux/moduleparam.h>
 33#include <linux/types.h>
 34#include <linux/miscdevice.h>
 35#include <linux/watchdog.h>
 36#include <linux/fs.h>
 37#include <linux/ioport.h>
 38#include <linux/notifier.h>
 39#include <linux/reboot.h>
 40#include <linux/init.h>
 41
 42#include <asm/io.h>
 43#include <asm/uaccess.h>
 44#include <asm/system.h>
 45
 46#define WATCHDOG_NAME "Advantech WDT"
 47#define PFX WATCHDOG_NAME ": "
 48#define WATCHDOG_TIMEOUT 60		/* 60 sec default timeout */
 49
 50static unsigned long advwdt_is_open;
 51static char adv_expect_close;
 52
 53/*
 54 *	You must set these - there is no sane way to probe for this board.
 55 *
 56 *	To enable or restart, write the timeout value in seconds (1 to 63)
 57 *	to I/O port wdt_start.  To disable, read I/O port wdt_stop.
 58 *	Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
 59 *	check your manual (at least the PCA-6159 seems to be different -
 60 *	the manual says wdt_stop is 0x43, not 0x443).
 61 *	(0x43 is also a write-only control register for the 8254 timer!)
 62 */
 63
 64static int wdt_stop = 0x443;
 65module_param(wdt_stop, int, 0);
 66MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
 67
 68static int wdt_start = 0x443;
 69module_param(wdt_start, int, 0);
 70MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
 71
 72static int timeout = WATCHDOG_TIMEOUT;	/* in seconds */
 73module_param(timeout, int, 0);
 74MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
 75
 76#ifdef CONFIG_WATCHDOG_NOWAYOUT
 77static int nowayout = 1;
 78#else
 79static int nowayout = 0;
 80#endif
 81
 82module_param(nowayout, int, 0);
 83MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
 84
 85/*
 86 *	Kernel methods.
 87 */
 88
 89static void
 90advwdt_ping(void)
 91{
 92	/* Write a watchdog value */
 93	outb_p(timeout, wdt_start);
 94}
 95
 96static void
 97advwdt_disable(void)
 98{
 99	inb_p(wdt_stop);
100}
101
102static ssize_t
103advwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
104{
105	if (count) {
106		if (!nowayout) {
107			size_t i;
108
109			adv_expect_close = 0;
110
111			for (i = 0; i != count; i++) {
112				char c;
113				if (get_user(c, buf+i))
114					return -EFAULT;
115				if (c == 'V')
116					adv_expect_close = 42;
117			}
118		}
119		advwdt_ping();
120	}
121	return count;
122}
123
124static int
125advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
126	  unsigned long arg)
127{
128	int new_timeout;
129	void __user *argp = (void __user *)arg;
130	int __user *p = argp;
131	static struct watchdog_info ident = {
132		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
133		.firmware_version = 1,
134		.identity = "Advantech WDT",
135	};
136
137	switch (cmd) {
138	case WDIOC_GETSUPPORT:
139	  if (copy_to_user(argp, &ident, sizeof(ident)))
140	    return -EFAULT;
141	  break;
142
143	case WDIOC_GETSTATUS:
144	case WDIOC_GETBOOTSTATUS:
145	  return put_user(0, p);
146
147	case WDIOC_KEEPALIVE:
148	  advwdt_ping();
149	  break;
150
151	case WDIOC_SETTIMEOUT:
152	  if (get_user(new_timeout, p))
153		  return -EFAULT;
154	  if ((new_timeout < 1) || (new_timeout > 63))
155		  return -EINVAL;
156	  timeout = new_timeout;
157	  advwdt_ping();
158	  /* Fall */
159
160	case WDIOC_GETTIMEOUT:
161	  return put_user(timeout, p);
162
163	case WDIOC_SETOPTIONS:
164	{
165	  int options, retval = -EINVAL;
166
167	  if (get_user(options, p))
168	    return -EFAULT;
169
170	  if (options & WDIOS_DISABLECARD) {
171	    advwdt_disable();
172	    retval = 0;
173	  }
174
175	  if (options & WDIOS_ENABLECARD) {
176	    advwdt_ping();
177	    retval = 0;
178	  }
179
180	  return retval;
181	}
182
183	default:
184	  return -ENOIOCTLCMD;
185	}
186	return 0;
187}
188
189static int
190advwdt_open(struct inode *inode, struct file *file)
191{
192	if (test_and_set_bit(0, &advwdt_is_open))
193		return -EBUSY;
194	/*
195	 *	Activate
196	 */
197
198	advwdt_ping();
199	return nonseekable_open(inode, file);
200}
201
202static int
203advwdt_close(struct inode *inode, struct file *file)
204{
205	if (adv_expect_close == 42) {
206		advwdt_disable();
207	} else {
208		printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
209		advwdt_ping();
210	}
211	clear_bit(0, &advwdt_is_open);
212	adv_expect_close = 0;
213	return 0;
214}
215
216/*
217 *	Notifier for system down
218 */
219
220static int
221advwdt_notify_sys(struct notifier_block *this, unsigned long code,
222	void *unused)
223{
224	if (code == SYS_DOWN || code == SYS_HALT) {
225		/* Turn the WDT off */
226		advwdt_disable();
227	}
228	return NOTIFY_DONE;
229}
230
231/*
232 *	Kernel Interfaces
233 */
234
235static struct file_operations advwdt_fops = {
236	.owner		= THIS_MODULE,
237	.llseek		= no_llseek,
238	.write		= advwdt_write,
239	.ioctl		= advwdt_ioctl,
240	.open		= advwdt_open,
241	.release	= advwdt_close,
242};
243
244static struct miscdevice advwdt_miscdev = {
245	.minor = WATCHDOG_MINOR,
246	.name = "watchdog",
247	.fops = &advwdt_fops,
248};
249
250/*
251 *	The WDT needs to learn about soft shutdowns in order to
252 *	turn the timebomb registers off.
253 */
254
255static struct notifier_block advwdt_notifier = {
256	.notifier_call = advwdt_notify_sys,
257};
258
259static int __init
260advwdt_init(void)
261{
262	int ret;
263
264	printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n");
265
266	if (timeout < 1 || timeout > 63) {
267		timeout = WATCHDOG_TIMEOUT;
268		printk (KERN_INFO PFX "timeout value must be 1<=x<=63, using %d\n",
269			timeout);
270	}
271
272	if (wdt_stop != wdt_start) {
273		if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
274			printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
275				wdt_stop);
276			ret = -EIO;
277			goto out;
278		}
279	}
280
281	if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
282		printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
283			wdt_start);
284		ret = -EIO;
285		goto unreg_stop;
286	}
287
288	ret = register_reboot_notifier(&advwdt_notifier);
289	if (ret != 0) {
290		printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
291			ret);
292		goto unreg_regions;
293	}
294
295	ret = misc_register(&advwdt_miscdev);
296	if (ret != 0) {
297		printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
298			WATCHDOG_MINOR, ret);
299		goto unreg_reboot;
300	}
301
302	printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
303		timeout, nowayout);
304
305out:
306	return ret;
307unreg_reboot:
308	unregister_reboot_notifier(&advwdt_notifier);
309unreg_regions:
310	release_region(wdt_start, 1);
311unreg_stop:
312	if (wdt_stop != wdt_start)
313		release_region(wdt_stop, 1);
314	goto out;
315}
316
317static void __exit
318advwdt_exit(void)
319{
320	misc_deregister(&advwdt_miscdev);
321	unregister_reboot_notifier(&advwdt_notifier);
322	if(wdt_stop != wdt_start)
323		release_region(wdt_stop,1);
324	release_region(wdt_start,1);
325}
326
327module_init(advwdt_init);
328module_exit(advwdt_exit);
329
330MODULE_LICENSE("GPL");
331MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
332MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
333MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);