brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 2911093 Raw
323 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *	ICP Wafer 5823 Single Board Computer WDT driver4 *	http://www.icpamerica.com/wafer_5823.php5 *	May also work on other similar models6 *7 *	(c) Copyright 2002 Justin Cormack <justin@street-vision.com>8 *9 *	Release 0.0210 *11 *	Based on advantechwdt.c which is based on wdt.c.12 *	Original copyright messages:13 *14 *	(c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,15 *						All Rights Reserved.16 *17 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide18 *	warranty for any of this software. This material is provided19 *	"AS-IS" and at no charge.20 *21 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>22 *23 */24 25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt26 27#include <linux/module.h>28#include <linux/moduleparam.h>29#include <linux/miscdevice.h>30#include <linux/watchdog.h>31#include <linux/fs.h>32#include <linux/ioport.h>33#include <linux/notifier.h>34#include <linux/reboot.h>35#include <linux/init.h>36#include <linux/spinlock.h>37#include <linux/io.h>38#include <linux/uaccess.h>39 40#define WATCHDOG_NAME "Wafer 5823 WDT"41#define PFX WATCHDOG_NAME ": "42#define WD_TIMO 60			/* 60 sec default timeout */43 44static unsigned long wafwdt_is_open;45static char expect_close;46static DEFINE_SPINLOCK(wafwdt_lock);47 48/*49 *	You must set these - there is no sane way to probe for this board.50 *51 *	To enable, write the timeout value in seconds (1 to 255) to I/O52 *	port WDT_START, then read the port to start the watchdog. To pat53 *	the dog, read port WDT_STOP to stop the timer, then read WDT_START54 *	to restart it again.55 */56 57static int wdt_stop = 0x843;58static int wdt_start = 0x443;59 60static int timeout = WD_TIMO;  /* in seconds */61module_param(timeout, int, 0);62MODULE_PARM_DESC(timeout,63		"Watchdog timeout in seconds. 1 <= timeout <= 255, default="64				__MODULE_STRING(WD_TIMO) ".");65 66static bool nowayout = WATCHDOG_NOWAYOUT;67module_param(nowayout, bool, 0);68MODULE_PARM_DESC(nowayout,69		"Watchdog cannot be stopped once started (default="70				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");71 72static void wafwdt_ping(void)73{74	/* pat watchdog */75	spin_lock(&wafwdt_lock);76	inb_p(wdt_stop);77	inb_p(wdt_start);78	spin_unlock(&wafwdt_lock);79}80 81static void wafwdt_start(void)82{83	/* start up watchdog */84	outb_p(timeout, wdt_start);85	inb_p(wdt_start);86}87 88static void wafwdt_stop(void)89{90	/* stop watchdog */91	inb_p(wdt_stop);92}93 94static ssize_t wafwdt_write(struct file *file, const char __user *buf,95						size_t count, loff_t *ppos)96{97	/* See if we got the magic character 'V' and reload the timer */98	if (count) {99		if (!nowayout) {100			size_t i;101 102			/* In case it was set long ago */103			expect_close = 0;104 105			/* scan to see whether or not we got the magic106			   character */107			for (i = 0; i != count; i++) {108				char c;109				if (get_user(c, buf + i))110					return -EFAULT;111				if (c == 'V')112					expect_close = 42;113			}114		}115		/* Well, anyhow someone wrote to us, we should116		   return that favour */117		wafwdt_ping();118	}119	return count;120}121 122static long wafwdt_ioctl(struct file *file, unsigned int cmd,123							unsigned long arg)124{125	int new_timeout;126	void __user *argp = (void __user *)arg;127	int __user *p = argp;128	static const struct watchdog_info ident = {129		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |130							WDIOF_MAGICCLOSE,131		.firmware_version = 1,132		.identity = "Wafer 5823 WDT",133	};134 135	switch (cmd) {136	case WDIOC_GETSUPPORT:137		if (copy_to_user(argp, &ident, sizeof(ident)))138			return -EFAULT;139		break;140 141	case WDIOC_GETSTATUS:142	case WDIOC_GETBOOTSTATUS:143		return put_user(0, p);144 145	case WDIOC_SETOPTIONS:146	{147		int options, retval = -EINVAL;148 149		if (get_user(options, p))150			return -EFAULT;151 152		if (options & WDIOS_DISABLECARD) {153			wafwdt_stop();154			retval = 0;155		}156 157		if (options & WDIOS_ENABLECARD) {158			wafwdt_start();159			retval = 0;160		}161 162		return retval;163	}164 165	case WDIOC_KEEPALIVE:166		wafwdt_ping();167		break;168 169	case WDIOC_SETTIMEOUT:170		if (get_user(new_timeout, p))171			return -EFAULT;172		if ((new_timeout < 1) || (new_timeout > 255))173			return -EINVAL;174		timeout = new_timeout;175		wafwdt_stop();176		wafwdt_start();177		fallthrough;178	case WDIOC_GETTIMEOUT:179		return put_user(timeout, p);180 181	default:182		return -ENOTTY;183	}184	return 0;185}186 187static int wafwdt_open(struct inode *inode, struct file *file)188{189	if (test_and_set_bit(0, &wafwdt_is_open))190		return -EBUSY;191 192	/*193	 *      Activate194	 */195	wafwdt_start();196	return stream_open(inode, file);197}198 199static int wafwdt_close(struct inode *inode, struct file *file)200{201	if (expect_close == 42)202		wafwdt_stop();203	else {204		pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");205		wafwdt_ping();206	}207	clear_bit(0, &wafwdt_is_open);208	expect_close = 0;209	return 0;210}211 212/*213 *	Notifier for system down214 */215 216static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code,217								void *unused)218{219	if (code == SYS_DOWN || code == SYS_HALT)220		wafwdt_stop();221	return NOTIFY_DONE;222}223 224/*225 *	Kernel Interfaces226 */227 228static const struct file_operations wafwdt_fops = {229	.owner		= THIS_MODULE,230	.write		= wafwdt_write,231	.unlocked_ioctl	= wafwdt_ioctl,232	.compat_ioctl	= compat_ptr_ioctl,233	.open		= wafwdt_open,234	.release	= wafwdt_close,235};236 237static struct miscdevice wafwdt_miscdev = {238	.minor	= WATCHDOG_MINOR,239	.name	= "watchdog",240	.fops	= &wafwdt_fops,241};242 243/*244 *	The WDT needs to learn about soft shutdowns in order to245 *	turn the timebomb registers off.246 */247 248static struct notifier_block wafwdt_notifier = {249	.notifier_call = wafwdt_notify_sys,250};251 252static int __init wafwdt_init(void)253{254	int ret;255 256	pr_info("WDT driver for Wafer 5823 single board computer initialising\n");257 258	if (timeout < 1 || timeout > 255) {259		timeout = WD_TIMO;260		pr_info("timeout value must be 1 <= x <= 255, using %d\n",261			timeout);262	}263 264	if (wdt_stop != wdt_start) {265		if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {266			pr_err("I/O address 0x%04x already in use\n", wdt_stop);267			ret = -EIO;268			goto error;269		}270	}271 272	if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {273		pr_err("I/O address 0x%04x already in use\n", wdt_start);274		ret = -EIO;275		goto error2;276	}277 278	ret = register_reboot_notifier(&wafwdt_notifier);279	if (ret != 0) {280		pr_err("cannot register reboot notifier (err=%d)\n", ret);281		goto error3;282	}283 284	ret = misc_register(&wafwdt_miscdev);285	if (ret != 0) {286		pr_err("cannot register miscdev on minor=%d (err=%d)\n",287		       WATCHDOG_MINOR, ret);288		goto error4;289	}290 291	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",292		timeout, nowayout);293 294	return ret;295error4:296	unregister_reboot_notifier(&wafwdt_notifier);297error3:298	release_region(wdt_start, 1);299error2:300	if (wdt_stop != wdt_start)301		release_region(wdt_stop, 1);302error:303	return ret;304}305 306static void __exit wafwdt_exit(void)307{308	misc_deregister(&wafwdt_miscdev);309	unregister_reboot_notifier(&wafwdt_notifier);310	if (wdt_stop != wdt_start)311		release_region(wdt_stop, 1);312	release_region(wdt_start, 1);313}314 315module_init(wafwdt_init);316module_exit(wafwdt_exit);317 318MODULE_AUTHOR("Justin Cormack");319MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");320MODULE_LICENSE("GPL");321 322/* end of wafer5823wdt.c */323