xref: /openbmc/linux/drivers/watchdog/sbc_epx_c3.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b7e04f8cSWim Van Sebroeck /*
3b7e04f8cSWim Van Sebroeck  *	SBC EPX C3 0.1	A Hardware Watchdog Device for the Winsystems EPX-C3
4b7e04f8cSWim Van Sebroeck  *	single board computer
5b7e04f8cSWim Van Sebroeck  *
6b7e04f8cSWim Van Sebroeck  *	(c) Copyright 2006 Calin A. Culianu <calin@ajvar.org>, All Rights
7b7e04f8cSWim Van Sebroeck  *	Reserved.
8b7e04f8cSWim Van Sebroeck  *
929fa0586SAlan Cox  *	based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
10b7e04f8cSWim Van Sebroeck  */
11b7e04f8cSWim Van Sebroeck 
1227c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1327c766aaSJoe Perches 
14b7e04f8cSWim Van Sebroeck #include <linux/module.h>
15b7e04f8cSWim Van Sebroeck #include <linux/moduleparam.h>
16b7e04f8cSWim Van Sebroeck #include <linux/types.h>
17b7e04f8cSWim Van Sebroeck #include <linux/kernel.h>
18b7e04f8cSWim Van Sebroeck #include <linux/fs.h>
19b7e04f8cSWim Van Sebroeck #include <linux/mm.h>
20b7e04f8cSWim Van Sebroeck #include <linux/miscdevice.h>
21b7e04f8cSWim Van Sebroeck #include <linux/watchdog.h>
22b7e04f8cSWim Van Sebroeck #include <linux/notifier.h>
23b7e04f8cSWim Van Sebroeck #include <linux/reboot.h>
24b7e04f8cSWim Van Sebroeck #include <linux/init.h>
25b7e04f8cSWim Van Sebroeck #include <linux/ioport.h>
26f4f6f65aSAlan Cox #include <linux/uaccess.h>
27f4f6f65aSAlan Cox #include <linux/io.h>
28b7e04f8cSWim Van Sebroeck 
29b7e04f8cSWim Van Sebroeck static int epx_c3_alive;
30b7e04f8cSWim Van Sebroeck 
31b7e04f8cSWim Van Sebroeck #define WATCHDOG_TIMEOUT 1		/* 1 sec default timeout */
32b7e04f8cSWim Van Sebroeck 
3386a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
3486a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
35143a2e54SWim Van Sebroeck MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
36143a2e54SWim Van Sebroeck 					__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
37b7e04f8cSWim Van Sebroeck 
38b7e04f8cSWim Van Sebroeck #define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */
39b7e04f8cSWim Van Sebroeck #define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */
40b7e04f8cSWim Van Sebroeck 
epx_c3_start(void)41b7e04f8cSWim Van Sebroeck static void epx_c3_start(void)
42b7e04f8cSWim Van Sebroeck {
43b7e04f8cSWim Van Sebroeck 	outb(1, EPXC3_WATCHDOG_CTL_REG);
44b7e04f8cSWim Van Sebroeck }
45b7e04f8cSWim Van Sebroeck 
epx_c3_stop(void)46b7e04f8cSWim Van Sebroeck static void epx_c3_stop(void)
47b7e04f8cSWim Van Sebroeck {
48b7e04f8cSWim Van Sebroeck 
49b7e04f8cSWim Van Sebroeck 	outb(0, EPXC3_WATCHDOG_CTL_REG);
50b7e04f8cSWim Van Sebroeck 
5127c766aaSJoe Perches 	pr_info("Stopped watchdog timer\n");
52b7e04f8cSWim Van Sebroeck }
53b7e04f8cSWim Van Sebroeck 
epx_c3_pet(void)54b7e04f8cSWim Van Sebroeck static void epx_c3_pet(void)
55b7e04f8cSWim Van Sebroeck {
56b7e04f8cSWim Van Sebroeck 	outb(1, EPXC3_WATCHDOG_PET_REG);
57b7e04f8cSWim Van Sebroeck }
58b7e04f8cSWim Van Sebroeck 
59b7e04f8cSWim Van Sebroeck /*
60b7e04f8cSWim Van Sebroeck  *	Allow only one person to hold it open
61b7e04f8cSWim Van Sebroeck  */
epx_c3_open(struct inode * inode,struct file * file)62b7e04f8cSWim Van Sebroeck static int epx_c3_open(struct inode *inode, struct file *file)
63b7e04f8cSWim Van Sebroeck {
64b7e04f8cSWim Van Sebroeck 	if (epx_c3_alive)
65b7e04f8cSWim Van Sebroeck 		return -EBUSY;
66b7e04f8cSWim Van Sebroeck 
67b7e04f8cSWim Van Sebroeck 	if (nowayout)
68b7e04f8cSWim Van Sebroeck 		__module_get(THIS_MODULE);
69b7e04f8cSWim Van Sebroeck 
70b7e04f8cSWim Van Sebroeck 	/* Activate timer */
71b7e04f8cSWim Van Sebroeck 	epx_c3_start();
72b7e04f8cSWim Van Sebroeck 	epx_c3_pet();
73b7e04f8cSWim Van Sebroeck 
74b7e04f8cSWim Van Sebroeck 	epx_c3_alive = 1;
7527c766aaSJoe Perches 	pr_info("Started watchdog timer\n");
76b7e04f8cSWim Van Sebroeck 
77c5bf68feSKirill Smelkov 	return stream_open(inode, file);
78b7e04f8cSWim Van Sebroeck }
79b7e04f8cSWim Van Sebroeck 
epx_c3_release(struct inode * inode,struct file * file)80b7e04f8cSWim Van Sebroeck static int epx_c3_release(struct inode *inode, struct file *file)
81b7e04f8cSWim Van Sebroeck {
82b7e04f8cSWim Van Sebroeck 	/* Shut off the timer.
83b7e04f8cSWim Van Sebroeck 	 * Lock it in if it's a module and we defined ...NOWAYOUT */
84b7e04f8cSWim Van Sebroeck 	if (!nowayout)
85b7e04f8cSWim Van Sebroeck 		epx_c3_stop();		/* Turn the WDT off */
86b7e04f8cSWim Van Sebroeck 
87b7e04f8cSWim Van Sebroeck 	epx_c3_alive = 0;
88b7e04f8cSWim Van Sebroeck 
89b7e04f8cSWim Van Sebroeck 	return 0;
90b7e04f8cSWim Van Sebroeck }
91b7e04f8cSWim Van Sebroeck 
epx_c3_write(struct file * file,const char __user * data,size_t len,loff_t * ppos)92b7e04f8cSWim Van Sebroeck static ssize_t epx_c3_write(struct file *file, const char __user *data,
93b7e04f8cSWim Van Sebroeck 			size_t len, loff_t *ppos)
94b7e04f8cSWim Van Sebroeck {
95b7e04f8cSWim Van Sebroeck 	/* Refresh the timer. */
96b7e04f8cSWim Van Sebroeck 	if (len)
97b7e04f8cSWim Van Sebroeck 		epx_c3_pet();
98b7e04f8cSWim Van Sebroeck 	return len;
99b7e04f8cSWim Van Sebroeck }
100b7e04f8cSWim Van Sebroeck 
epx_c3_ioctl(struct file * file,unsigned int cmd,unsigned long arg)101f4f6f65aSAlan Cox static long epx_c3_ioctl(struct file *file, unsigned int cmd,
102f4f6f65aSAlan Cox 						unsigned long arg)
103b7e04f8cSWim Van Sebroeck {
104b7e04f8cSWim Van Sebroeck 	int options, retval = -EINVAL;
105b7e04f8cSWim Van Sebroeck 	int __user *argp = (void __user *)arg;
106f4f6f65aSAlan Cox 	static const struct watchdog_info ident = {
107e73a7802SWim Van Sebroeck 		.options		= WDIOF_KEEPALIVEPING,
108b7e04f8cSWim Van Sebroeck 		.firmware_version	= 0,
109b7e04f8cSWim Van Sebroeck 		.identity		= "Winsystems EPX-C3 H/W Watchdog",
110b7e04f8cSWim Van Sebroeck 	};
111b7e04f8cSWim Van Sebroeck 
112b7e04f8cSWim Van Sebroeck 	switch (cmd) {
113b7e04f8cSWim Van Sebroeck 	case WDIOC_GETSUPPORT:
114b7e04f8cSWim Van Sebroeck 		if (copy_to_user(argp, &ident, sizeof(ident)))
115b7e04f8cSWim Van Sebroeck 			return -EFAULT;
116b7e04f8cSWim Van Sebroeck 		return 0;
117b7e04f8cSWim Van Sebroeck 	case WDIOC_GETSTATUS:
118b7e04f8cSWim Van Sebroeck 	case WDIOC_GETBOOTSTATUS:
119b7e04f8cSWim Van Sebroeck 		return put_user(0, argp);
120b7e04f8cSWim Van Sebroeck 	case WDIOC_SETOPTIONS:
121b7e04f8cSWim Van Sebroeck 		if (get_user(options, argp))
122b7e04f8cSWim Van Sebroeck 			return -EFAULT;
123b7e04f8cSWim Van Sebroeck 
124b7e04f8cSWim Van Sebroeck 		if (options & WDIOS_DISABLECARD) {
125b7e04f8cSWim Van Sebroeck 			epx_c3_stop();
126b7e04f8cSWim Van Sebroeck 			retval = 0;
127b7e04f8cSWim Van Sebroeck 		}
128b7e04f8cSWim Van Sebroeck 
129b7e04f8cSWim Van Sebroeck 		if (options & WDIOS_ENABLECARD) {
130b7e04f8cSWim Van Sebroeck 			epx_c3_start();
131b7e04f8cSWim Van Sebroeck 			retval = 0;
132b7e04f8cSWim Van Sebroeck 		}
133b7e04f8cSWim Van Sebroeck 
134b7e04f8cSWim Van Sebroeck 		return retval;
1350c06090cSWim Van Sebroeck 	case WDIOC_KEEPALIVE:
1360c06090cSWim Van Sebroeck 		epx_c3_pet();
1370c06090cSWim Van Sebroeck 		return 0;
1380c06090cSWim Van Sebroeck 	case WDIOC_GETTIMEOUT:
1390c06090cSWim Van Sebroeck 		return put_user(WATCHDOG_TIMEOUT, argp);
140b7e04f8cSWim Van Sebroeck 	default:
141b7e04f8cSWim Van Sebroeck 		return -ENOTTY;
142b7e04f8cSWim Van Sebroeck 	}
143b7e04f8cSWim Van Sebroeck }
144b7e04f8cSWim Van Sebroeck 
epx_c3_notify_sys(struct notifier_block * this,unsigned long code,void * unused)145b7e04f8cSWim Van Sebroeck static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,
146b7e04f8cSWim Van Sebroeck 				void *unused)
147b7e04f8cSWim Van Sebroeck {
148b7e04f8cSWim Van Sebroeck 	if (code == SYS_DOWN || code == SYS_HALT)
149b7e04f8cSWim Van Sebroeck 		epx_c3_stop();		/* Turn the WDT off */
150b7e04f8cSWim Van Sebroeck 
151b7e04f8cSWim Van Sebroeck 	return NOTIFY_DONE;
152b7e04f8cSWim Van Sebroeck }
153b7e04f8cSWim Van Sebroeck 
154b7e04f8cSWim Van Sebroeck static const struct file_operations epx_c3_fops = {
155b7e04f8cSWim Van Sebroeck 	.owner		= THIS_MODULE,
156b7e04f8cSWim Van Sebroeck 	.llseek		= no_llseek,
157b7e04f8cSWim Van Sebroeck 	.write		= epx_c3_write,
158f4f6f65aSAlan Cox 	.unlocked_ioctl	= epx_c3_ioctl,
159*b6dfb247SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
160b7e04f8cSWim Van Sebroeck 	.open		= epx_c3_open,
161b7e04f8cSWim Van Sebroeck 	.release	= epx_c3_release,
162b7e04f8cSWim Van Sebroeck };
163b7e04f8cSWim Van Sebroeck 
164b7e04f8cSWim Van Sebroeck static struct miscdevice epx_c3_miscdev = {
165b7e04f8cSWim Van Sebroeck 	.minor		= WATCHDOG_MINOR,
166b7e04f8cSWim Van Sebroeck 	.name		= "watchdog",
167b7e04f8cSWim Van Sebroeck 	.fops		= &epx_c3_fops,
168b7e04f8cSWim Van Sebroeck };
169b7e04f8cSWim Van Sebroeck 
170b7e04f8cSWim Van Sebroeck static struct notifier_block epx_c3_notifier = {
171b7e04f8cSWim Van Sebroeck 	.notifier_call = epx_c3_notify_sys,
172b7e04f8cSWim Van Sebroeck };
173b7e04f8cSWim Van Sebroeck 
watchdog_init(void)174b7e04f8cSWim Van Sebroeck static int __init watchdog_init(void)
175b7e04f8cSWim Van Sebroeck {
176b7e04f8cSWim Van Sebroeck 	int ret;
177b7e04f8cSWim Van Sebroeck 
178b7e04f8cSWim Van Sebroeck 	if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
179b7e04f8cSWim Van Sebroeck 		return -EBUSY;
180b7e04f8cSWim Van Sebroeck 
181b7e04f8cSWim Van Sebroeck 	ret = register_reboot_notifier(&epx_c3_notifier);
182b7e04f8cSWim Van Sebroeck 	if (ret) {
18327c766aaSJoe Perches 		pr_err("cannot register reboot notifier (err=%d)\n", ret);
184b7e04f8cSWim Van Sebroeck 		goto out;
185b7e04f8cSWim Van Sebroeck 	}
186b7e04f8cSWim Van Sebroeck 
187b7e04f8cSWim Van Sebroeck 	ret = misc_register(&epx_c3_miscdev);
188b7e04f8cSWim Van Sebroeck 	if (ret) {
18927c766aaSJoe Perches 		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
19027c766aaSJoe Perches 		       WATCHDOG_MINOR, ret);
191b7e04f8cSWim Van Sebroeck 		unregister_reboot_notifier(&epx_c3_notifier);
192b7e04f8cSWim Van Sebroeck 		goto out;
193b7e04f8cSWim Van Sebroeck 	}
194b7e04f8cSWim Van Sebroeck 
19527c766aaSJoe Perches 	pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n");
196b7e04f8cSWim Van Sebroeck 
197b7e04f8cSWim Van Sebroeck 	return 0;
198b7e04f8cSWim Van Sebroeck 
199b7e04f8cSWim Van Sebroeck out:
200b7e04f8cSWim Van Sebroeck 	release_region(EPXC3_WATCHDOG_CTL_REG, 2);
201b7e04f8cSWim Van Sebroeck 	return ret;
202b7e04f8cSWim Van Sebroeck }
203b7e04f8cSWim Van Sebroeck 
watchdog_exit(void)204b7e04f8cSWim Van Sebroeck static void __exit watchdog_exit(void)
205b7e04f8cSWim Van Sebroeck {
206b7e04f8cSWim Van Sebroeck 	misc_deregister(&epx_c3_miscdev);
207b7e04f8cSWim Van Sebroeck 	unregister_reboot_notifier(&epx_c3_notifier);
208b7e04f8cSWim Van Sebroeck 	release_region(EPXC3_WATCHDOG_CTL_REG, 2);
209b7e04f8cSWim Van Sebroeck }
210b7e04f8cSWim Van Sebroeck 
211b7e04f8cSWim Van Sebroeck module_init(watchdog_init);
212b7e04f8cSWim Van Sebroeck module_exit(watchdog_exit);
213b7e04f8cSWim Van Sebroeck 
214b7e04f8cSWim Van Sebroeck MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");
215a77dba7eSWim Van Sebroeck MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC.  "
216a77dba7eSWim Van Sebroeck 	"Note that there is no way to probe for this device -- "
217ae0e47f0SJustin P. Mattock 	"so only use it if you are *sure* you are running on this specific "
218a77dba7eSWim Van Sebroeck 	"SBC system from Winsystems!  It writes to IO ports 0x1ee and 0x1ef!");
219b7e04f8cSWim Van Sebroeck MODULE_LICENSE("GPL");
220