1/*
2 *	Industrial Computer Source WDT500/501 driver for Linux 2.1.x
3 *
4 *	(c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
5 *				http://www.redhat.com
6 *
7 *	This program is free software; you can redistribute it and/or
8 *	modify it under the terms of the GNU General Public License
9 *	as published by the Free Software Foundation; either version
10 *	2 of the License, or (at your option) any later version.
11 *
12 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
13 *	warranty for any of this software. This material is provided
14 *	"AS-IS" and at no charge.
15 *
16 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
17 *
18 *	Release 0.08.
19 *
20 *	Fixes
21 *		Dave Gregorich	:	Modularisation and minor bugs
22 *		Alan Cox	:	Added the watchdog ioctl() stuff
23 *		Alan Cox	:	Fixed the reboot problem (as noted by
24 *					Matt Crocker).
25 *		Alan Cox	:	Added wdt= boot option
26 *		Alan Cox	:	Cleaned up copy/user stuff
27 *		Tim Hockin	:	Added insmod parameters, comment cleanup
28 *					Parameterized timeout
29 *		Tigran Aivazian	:	Restructured wdt_init() to handle failures
30 *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT
31 *		Matt Domsch	:	Added nowayout module option
32 */
33
34#include <linux/config.h>
35#include <linux/module.h>
36#include <linux/version.h>
37#include <linux/types.h>
38#include <linux/errno.h>
39#include <linux/kernel.h>
40#include <linux/sched.h>
41#include <linux/smp_lock.h>
42#include <linux/miscdevice.h>
43#include <linux/watchdog.h>
44#include "wd501p.h"
45#include <linux/slab.h>
46#include <linux/ioport.h>
47#include <linux/fcntl.h>
48#include <asm/io.h>
49#include <asm/uaccess.h>
50#include <asm/system.h>
51#include <linux/notifier.h>
52#include <linux/reboot.h>
53#include <linux/init.h>
54
55static unsigned long wdt_is_open;
56static int expect_close;
57
58/*
59 *	You must set these - there is no sane way to probe for this board.
60 *	You can use wdt=x,y to set these now.
61 */
62
63static int io=0x240;
64static int irq=11;
65
66/* Default margin */
67#define WD_TIMO (100*60)		/* 1 minute */
68
69static int wd_margin = WD_TIMO;
70
71#ifdef CONFIG_WATCHDOG_NOWAYOUT
72static int nowayout = 1;
73#else
74static int nowayout = 0;
75#endif
76
77MODULE_PARM(nowayout,"i");
78MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
79
80#ifndef MODULE
81
82/**
83 *	wdt_setup:
84 *	@str: command line string
85 *
86 *	Setup options. The board isn't really probe-able so we have to
87 *	get the user to tell us the configuration. Sane people build it
88 *	modular but the others come here.
89 */
90
91static int __init wdt_setup(char *str)
92{
93	int ints[4];
94
95	str = get_options (str, ARRAY_SIZE(ints), ints);
96
97	if (ints[0] > 0)
98	{
99		io = ints[1];
100		if(ints[0] > 1)
101			irq = ints[2];
102	}
103
104	return 1;
105}
106
107__setup("wdt=", wdt_setup);
108
109#endif /* !MODULE */
110
111MODULE_PARM(io, "i");
112MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
113MODULE_PARM(irq, "i");
114MODULE_PARM_DESC(irq, "WDT irq (default=11)");
115
116/*
117 *	Programming support
118 */
119
120static void wdt_ctr_mode(int ctr, int mode)
121{
122	ctr<<=6;
123	ctr|=0x30;
124	ctr|=(mode<<1);
125	outb_p(ctr, WDT_CR);
126}
127
128static void wdt_ctr_load(int ctr, int val)
129{
130	outb_p(val&0xFF, WDT_COUNT0+ctr);
131	outb_p(val>>8, WDT_COUNT0+ctr);
132}
133
134/*
135 *	Kernel methods.
136 */
137
138
139/**
140 *	wdt_status:
141 *
142 *	Extract the status information from a WDT watchdog device. There are
143 *	several board variants so we have to know which bits are valid. Some
144 *	bits default to one and some to zero in order to be maximally painful.
145 *
146 *	we then map the bits onto the status ioctl flags.
147 */
148
149static int wdt_status(void)
150{
151	/*
152	 *	Status register to bit flags
153	 */
154
155	int flag=0;
156	unsigned char status=inb_p(WDT_SR);
157	status|=FEATUREMAP1;
158	status&=~FEATUREMAP2;
159
160	if(!(status&WDC_SR_TGOOD))
161		flag|=WDIOF_OVERHEAT;
162	if(!(status&WDC_SR_PSUOVER))
163		flag|=WDIOF_POWEROVER;
164	if(!(status&WDC_SR_PSUUNDR))
165		flag|=WDIOF_POWERUNDER;
166	if(!(status&WDC_SR_FANGOOD))
167		flag|=WDIOF_FANFAULT;
168	if(status&WDC_SR_ISOI0)
169		flag|=WDIOF_EXTERN1;
170	if(status&WDC_SR_ISII1)
171		flag|=WDIOF_EXTERN2;
172	return flag;
173}
174
175/**
176 *	wdt_interrupt:
177 *	@irq:		Interrupt number
178 *	@dev_id:	Unused as we don't allow multiple devices.
179 *	@regs:		Unused.
180 *
181 *	Handle an interrupt from the board. These are raised when the status
182 *	map changes in what the board considers an interesting way. That means
183 *	a failure condition occuring.
184 */
185
186void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
187{
188	/*
189	 *	Read the status register see what is up and
190	 *	then printk it.
191	 */
192
193	unsigned char status=inb_p(WDT_SR);
194
195	status|=FEATUREMAP1;
196	status&=~FEATUREMAP2;
197
198	printk(KERN_CRIT "WDT status %d\n", status);
199
200	if(!(status&WDC_SR_TGOOD))
201		printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT));
202	if(!(status&WDC_SR_PSUOVER))
203		printk(KERN_CRIT "PSU over voltage.\n");
204	if(!(status&WDC_SR_PSUUNDR))
205		printk(KERN_CRIT "PSU under voltage.\n");
206	if(!(status&WDC_SR_FANGOOD))
207		printk(KERN_CRIT "Possible fan fault.\n");
208	if(!(status&WDC_SR_WCCR))
209#ifdef SOFTWARE_REBOOT
210#ifdef ONLY_TESTING
211		printk(KERN_CRIT "Would Reboot.\n");
212#else
213		printk(KERN_CRIT "Initiating system reboot.\n");
214		machine_restart(NULL);
215#endif
216#else
217		printk(KERN_CRIT "Reset in 5ms.\n");
218#endif
219}
220
221
222/**
223 *	wdt_ping:
224 *
225 *	Reload counter one with the watchdog timeout. We don't bother reloading
226 *	the cascade counter.
227 */
228
229static void wdt_ping(void)
230{
231	/* Write a watchdog value */
232	inb_p(WDT_DC);
233	wdt_ctr_mode(1,2);
234	wdt_ctr_load(1,wd_margin);		/* Timeout */
235	outb_p(0, WDT_DC);
236}
237
238/**
239 *	wdt_write:
240 *	@file: file handle to the watchdog
241 *	@buf: buffer to write (unused as data does not matter here
242 *	@count: count of bytes
243 *	@ppos: pointer to the position to write. No seeks allowed
244 *
245 *	A write to a watchdog device is defined as a keepalive signal. Any
246 *	write of data will do, as we we don't define content meaning.
247 */
248
249static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
250{
251	/*  Can't seek (pwrite) on this device  */
252	if (ppos != &file->f_pos)
253		return -ESPIPE;
254
255	if(count)
256	{
257		if (!nowayout) {
258			size_t i;
259
260			/* In case it was set long ago */
261			expect_close = 0;
262
263			for (i = 0; i != count; i++) {
264				char c;
265				if (get_user(c, buf + i))
266					return -EFAULT;
267				if (c == 'V')
268					expect_close = 1;
269			}
270		}
271		wdt_ping();
272		return 1;
273	}
274	return 0;
275}
276
277/**
278 *	wdt_read:
279 *	@file: file handle to the watchdog board
280 *	@buf: buffer to write 1 byte into
281 *	@count: length of buffer
282 *	@ptr: offset (no seek allowed)
283 *
284 *	Read reports the temperature in degrees Fahrenheit. The API is in
285 *	farenheit. It was designed by an imperial measurement luddite.
286 */
287
288static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr)
289{
290	unsigned short c=inb_p(WDT_RT);
291	unsigned char cp;
292
293	/*  Can't seek (pread) on this device  */
294	if (ptr != &file->f_pos)
295		return -ESPIPE;
296
297	switch(MINOR(file->f_dentry->d_inode->i_rdev))
298	{
299		case TEMP_MINOR:
300			c*=11;
301			c/=15;
302			cp=c+7;
303			if(copy_to_user(buf,&cp,1))
304				return -EFAULT;
305			return 1;
306		default:
307			return -EINVAL;
308	}
309}
310
311/**
312 *	wdt_ioctl:
313 *	@inode: inode of the device
314 *	@file: file handle to the device
315 *	@cmd: watchdog command
316 *	@arg: argument pointer
317 *
318 *	The watchdog API defines a common set of functions for all watchdogs
319 *	according to their available features. We only actually usefully support
320 *	querying capabilities and current status.
321 */
322
323static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
324	unsigned long arg)
325{
326	int new_margin;
327
328	static struct watchdog_info ident=
329	{
330		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
331			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
332			|WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
333		1,
334		"WDT500/501"
335	};
336
337	ident.options&=WDT_OPTION_MASK;	/* Mask down to the card we have */
338	switch(cmd)
339	{
340		default:
341			return -ENOTTY;
342		case WDIOC_GETSUPPORT:
343			return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
344
345		case WDIOC_GETSTATUS:
346			return put_user(wdt_status(),(int *)arg);
347		case WDIOC_GETBOOTSTATUS:
348			return put_user(0, (int *)arg);
349		case WDIOC_KEEPALIVE:
350			wdt_ping();
351			return 0;
352		case WDIOC_SETTIMEOUT:
353			if (get_user(new_margin, (int *)arg))
354				return -EFAULT;
355			/* Arbitrary, can't find the card's limits */
356			if ((new_margin < 0) || (new_margin > 60))
357				return -EINVAL;
358			wd_margin = new_margin * 100;
359			wdt_ping();
360			/* Fall */
361		case WDIOC_GETTIMEOUT:
362			return put_user(wd_margin / 100, (int *)arg);
363	}
364}
365
366/**
367 *	wdt_open:
368 *	@inode: inode of device
369 *	@file: file handle to device
370 *
371 *	One of our two misc devices has been opened. The watchdog device is
372 *	single open and on opening we load the counters. Counter zero is a
373 *	100Hz cascade, into counter 1 which downcounts to reboot. When the
374 *	counter triggers counter 2 downcounts the length of the reset pulse
375 *	which set set to be as long as possible.
376 */
377
378static int wdt_open(struct inode *inode, struct file *file)
379{
380	switch(MINOR(inode->i_rdev))
381	{
382		case WATCHDOG_MINOR:
383			if(test_and_set_bit(0, &wdt_is_open))
384				return -EBUSY;
385			/*
386			 *	Activate
387			 */
388
389			wdt_is_open=1;
390			inb_p(WDT_DC);		/* Disable */
391			wdt_ctr_mode(0,3);
392			wdt_ctr_mode(1,2);
393			wdt_ctr_mode(2,0);
394			wdt_ctr_load(0, 8948);		/* count at 100Hz */
395			wdt_ctr_load(1,wd_margin);	/* Timeout 120 seconds */
396			wdt_ctr_load(2,65535);
397			outb_p(0, WDT_DC);	/* Enable */
398			return 0;
399		case TEMP_MINOR:
400			return 0;
401		default:
402			return -ENODEV;
403	}
404}
405
406/**
407 *	wdt_close:
408 *	@inode: inode to board
409 *	@file: file handle to board
410 *
411 *	The watchdog has a configurable API. There is a religious dispute
412 *	between people who want their watchdog to be able to shut down and
413 *	those who want to be sure if the watchdog manager dies the machine
414 *	reboots. In the former case we disable the counters, in the latter
415 *	case you have to open it again very soon.
416 */
417
418static int wdt_release(struct inode *inode, struct file *file)
419{
420	if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
421	{
422		if (expect_close) {
423			inb_p(WDT_DC);		/* Disable counters */
424			wdt_ctr_load(2,0);	/* 0 length reset pulses now */
425		} else {
426			printk(KERN_CRIT "wdt: WDT device closed unexpectedly.  WDT will not stop!\n");
427		}
428		clear_bit(0, &wdt_is_open);
429	}
430	return 0;
431}
432
433/**
434 *	notify_sys:
435 *	@this: our notifier block
436 *	@code: the event being reported
437 *	@unused: unused
438 *
439 *	Our notifier is called on system shutdowns. We want to turn the card
440 *	off at reboot otherwise the machine will reboot again during memory
441 *	test or worse yet during the following fsck. This would suck, in fact
442 *	trust me - if it happens it does suck.
443 */
444
445static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
446	void *unused)
447{
448	if(code==SYS_DOWN || code==SYS_HALT)
449	{
450		/* Turn the card off */
451		inb_p(WDT_DC);
452		wdt_ctr_load(2,0);
453	}
454	return NOTIFY_DONE;
455}
456
457/*
458 *	Kernel Interfaces
459 */
460
461
462static struct file_operations wdt_fops = {
463	owner:		THIS_MODULE,
464	llseek:		no_llseek,
465	read:		wdt_read,
466	write:		wdt_write,
467	ioctl:		wdt_ioctl,
468	open:		wdt_open,
469	release:	wdt_release,
470};
471
472static struct miscdevice wdt_miscdev=
473{
474	WATCHDOG_MINOR,
475	"watchdog",
476	&wdt_fops
477};
478
479#ifdef CONFIG_WDT_501
480static struct miscdevice temp_miscdev=
481{
482	TEMP_MINOR,
483	"temperature",
484	&wdt_fops
485};
486#endif
487
488/*
489 *	The WDT card needs to learn about soft shutdowns in order to
490 *	turn the timebomb registers off.
491 */
492
493static struct notifier_block wdt_notifier=
494{
495	wdt_notify_sys,
496	NULL,
497	0
498};
499
500/**
501 *	cleanup_module:
502 *
503 *	Unload the watchdog. You cannot do this with any file handles open.
504 *	If your watchdog is set to continue ticking on close and you unload
505 *	it, well it keeps ticking. We won't get the interrupt but the board
506 *	will not touch PC memory so all is fine. You just have to load a new
507 *	module in 60 seconds or reboot.
508 */
509
510static void __exit wdt_exit(void)
511{
512	misc_deregister(&wdt_miscdev);
513#ifdef CONFIG_WDT_501
514	misc_deregister(&temp_miscdev);
515#endif
516	unregister_reboot_notifier(&wdt_notifier);
517	release_region(io,8);
518	free_irq(irq, NULL);
519}
520
521/**
522 * 	wdt_init:
523 *
524 *	Set up the WDT watchdog board. All we have to do is grab the
525 *	resources we require and bitch if anyone beat us to them.
526 *	The open() function will actually kick the board off.
527 */
528
529static int __init wdt_init(void)
530{
531	int ret;
532
533	ret = misc_register(&wdt_miscdev);
534	if (ret) {
535		printk(KERN_ERR "wdt: can't misc_register on minor=%d\n", WATCHDOG_MINOR);
536		goto out;
537	}
538	ret = request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL);
539	if(ret) {
540		printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq);
541		goto outmisc;
542	}
543	if (!request_region(io, 8, "wdt501p")) {
544		printk(KERN_ERR "wdt: IO %X is not free.\n", io);
545		ret = -EBUSY;
546		goto outirq;
547	}
548	ret = register_reboot_notifier(&wdt_notifier);
549	if(ret) {
550		printk(KERN_ERR "wdt: can't register reboot notifier (err=%d)\n", ret);
551		goto outreg;
552	}
553
554#ifdef CONFIG_WDT_501
555	ret = misc_register(&temp_miscdev);
556	if (ret) {
557		printk(KERN_ERR "wdt: can't misc_register (temp) on minor=%d\n", TEMP_MINOR);
558		goto outrbt;
559	}
560#endif
561
562	ret = 0;
563	printk(KERN_INFO "WDT500/501-P driver 0.07 at %X (Interrupt %d)\n", io, irq);
564out:
565	return ret;
566
567#ifdef CONFIG_WDT_501
568outrbt:
569	unregister_reboot_notifier(&wdt_notifier);
570#endif
571
572outreg:
573	release_region(io,8);
574outirq:
575	free_irq(irq, NULL);
576outmisc:
577	misc_deregister(&wdt_miscdev);
578	goto out;
579}
580
581module_init(wdt_init);
582module_exit(wdt_exit);
583
584MODULE_AUTHOR("Alan Cox");
585MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
586MODULE_LICENSE("GPL");
587EXPORT_NO_SYMBOLS;
588