xref: /openbmc/linux/drivers/watchdog/indydog.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b7e04f8cSWim Van Sebroeck /*
3b7e04f8cSWim Van Sebroeck  *	IndyDog	0.3	A Hardware Watchdog Device for SGI IP22
4b7e04f8cSWim Van Sebroeck  *
59b9dbccaSAlan Cox  *	(c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>,
69b9dbccaSAlan Cox  *						All Rights Reserved.
7b7e04f8cSWim Van Sebroeck  *
829fa0586SAlan Cox  *	based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
9b7e04f8cSWim Van Sebroeck  */
10b7e04f8cSWim Van Sebroeck 
1127c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1227c766aaSJoe Perches 
13b7e04f8cSWim Van Sebroeck #include <linux/module.h>
14b7e04f8cSWim Van Sebroeck #include <linux/moduleparam.h>
15b7e04f8cSWim Van Sebroeck #include <linux/types.h>
16b7e04f8cSWim Van Sebroeck #include <linux/kernel.h>
17b7e04f8cSWim Van Sebroeck #include <linux/fs.h>
18b7e04f8cSWim Van Sebroeck #include <linux/mm.h>
19b7e04f8cSWim Van Sebroeck #include <linux/miscdevice.h>
20b7e04f8cSWim Van Sebroeck #include <linux/watchdog.h>
21b7e04f8cSWim Van Sebroeck #include <linux/notifier.h>
22b7e04f8cSWim Van Sebroeck #include <linux/reboot.h>
23b7e04f8cSWim Van Sebroeck #include <linux/init.h>
249b9dbccaSAlan Cox #include <linux/uaccess.h>
25b7e04f8cSWim Van Sebroeck #include <asm/sgi/mc.h>
26b7e04f8cSWim Van Sebroeck 
279b9dbccaSAlan Cox static unsigned long indydog_alive;
281334f329SAxel Lin static DEFINE_SPINLOCK(indydog_lock);
29b7e04f8cSWim Van Sebroeck 
30b7e04f8cSWim Van Sebroeck #define WATCHDOG_TIMEOUT 30		/* 30 sec default timeout */
31b7e04f8cSWim Van Sebroeck 
3286a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
3386a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
349b9dbccaSAlan Cox MODULE_PARM_DESC(nowayout,
359b9dbccaSAlan Cox 		"Watchdog cannot be stopped once started (default="
369b9dbccaSAlan Cox 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
37b7e04f8cSWim Van Sebroeck 
indydog_start(void)38b7e04f8cSWim Van Sebroeck static void indydog_start(void)
39b7e04f8cSWim Van Sebroeck {
409b9dbccaSAlan Cox 	spin_lock(&indydog_lock);
410c29c2e8SAlexander Shiyan 	sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG;
429b9dbccaSAlan Cox 	spin_unlock(&indydog_lock);
43b7e04f8cSWim Van Sebroeck }
44b7e04f8cSWim Van Sebroeck 
indydog_stop(void)45b7e04f8cSWim Van Sebroeck static void indydog_stop(void)
46b7e04f8cSWim Van Sebroeck {
479b9dbccaSAlan Cox 	spin_lock(&indydog_lock);
480c29c2e8SAlexander Shiyan 	sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG;
499b9dbccaSAlan Cox 	spin_unlock(&indydog_lock);
50b7e04f8cSWim Van Sebroeck 
5127c766aaSJoe Perches 	pr_info("Stopped watchdog timer\n");
52b7e04f8cSWim Van Sebroeck }
53b7e04f8cSWim Van Sebroeck 
indydog_ping(void)54b7e04f8cSWim Van Sebroeck static void indydog_ping(void)
55b7e04f8cSWim Van Sebroeck {
56b7e04f8cSWim Van Sebroeck 	sgimc->watchdogt = 0;
57b7e04f8cSWim Van Sebroeck }
58b7e04f8cSWim Van Sebroeck 
59b7e04f8cSWim Van Sebroeck /*
60b7e04f8cSWim Van Sebroeck  *	Allow only one person to hold it open
61b7e04f8cSWim Van Sebroeck  */
indydog_open(struct inode * inode,struct file * file)62b7e04f8cSWim Van Sebroeck static int indydog_open(struct inode *inode, struct file *file)
63b7e04f8cSWim Van Sebroeck {
649b9dbccaSAlan Cox 	if (test_and_set_bit(0, &indydog_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 	indydog_start();
72b7e04f8cSWim Van Sebroeck 	indydog_ping();
73b7e04f8cSWim Van Sebroeck 
7427c766aaSJoe Perches 	pr_info("Started watchdog timer\n");
75b7e04f8cSWim Van Sebroeck 
76c5bf68feSKirill Smelkov 	return stream_open(inode, file);
77b7e04f8cSWim Van Sebroeck }
78b7e04f8cSWim Van Sebroeck 
indydog_release(struct inode * inode,struct file * file)79b7e04f8cSWim Van Sebroeck static int indydog_release(struct inode *inode, struct file *file)
80b7e04f8cSWim Van Sebroeck {
81b7e04f8cSWim Van Sebroeck 	/* Shut off the timer.
82b7e04f8cSWim Van Sebroeck 	 * Lock it in if it's a module and we defined ...NOWAYOUT */
83b7e04f8cSWim Van Sebroeck 	if (!nowayout)
84b7e04f8cSWim Van Sebroeck 		indydog_stop();		/* Turn the WDT off */
859b9dbccaSAlan Cox 	clear_bit(0, &indydog_alive);
86b7e04f8cSWim Van Sebroeck 	return 0;
87b7e04f8cSWim Van Sebroeck }
88b7e04f8cSWim Van Sebroeck 
indydog_write(struct file * file,const char * data,size_t len,loff_t * ppos)899b9dbccaSAlan Cox static ssize_t indydog_write(struct file *file, const char *data,
909b9dbccaSAlan Cox 						size_t len, loff_t *ppos)
91b7e04f8cSWim Van Sebroeck {
92b7e04f8cSWim Van Sebroeck 	/* Refresh the timer. */
939b9dbccaSAlan Cox 	if (len)
94b7e04f8cSWim Van Sebroeck 		indydog_ping();
95b7e04f8cSWim Van Sebroeck 	return len;
96b7e04f8cSWim Van Sebroeck }
97b7e04f8cSWim Van Sebroeck 
indydog_ioctl(struct file * file,unsigned int cmd,unsigned long arg)989b9dbccaSAlan Cox static long indydog_ioctl(struct file *file, unsigned int cmd,
999b9dbccaSAlan Cox 							unsigned long arg)
100b7e04f8cSWim Van Sebroeck {
101b7e04f8cSWim Van Sebroeck 	int options, retval = -EINVAL;
10242747d71SWim Van Sebroeck 	static const struct watchdog_info ident = {
103e73a7802SWim Van Sebroeck 		.options		= WDIOF_KEEPALIVEPING,
104b7e04f8cSWim Van Sebroeck 		.firmware_version	= 0,
105b7e04f8cSWim Van Sebroeck 		.identity		= "Hardware Watchdog for SGI IP22",
106b7e04f8cSWim Van Sebroeck 	};
107b7e04f8cSWim Van Sebroeck 
108b7e04f8cSWim Van Sebroeck 	switch (cmd) {
109b7e04f8cSWim Van Sebroeck 	case WDIOC_GETSUPPORT:
110b7e04f8cSWim Van Sebroeck 		if (copy_to_user((struct watchdog_info *)arg,
111b7e04f8cSWim Van Sebroeck 				 &ident, sizeof(ident)))
112b7e04f8cSWim Van Sebroeck 			return -EFAULT;
113b7e04f8cSWim Van Sebroeck 		return 0;
114b7e04f8cSWim Van Sebroeck 	case WDIOC_GETSTATUS:
115b7e04f8cSWim Van Sebroeck 	case WDIOC_GETBOOTSTATUS:
116b7e04f8cSWim Van Sebroeck 		return put_user(0, (int *)arg);
117b7e04f8cSWim Van Sebroeck 	case WDIOC_SETOPTIONS:
118b7e04f8cSWim Van Sebroeck 	{
119b7e04f8cSWim Van Sebroeck 		if (get_user(options, (int *)arg))
120b7e04f8cSWim Van Sebroeck 			return -EFAULT;
121b7e04f8cSWim Van Sebroeck 		if (options & WDIOS_DISABLECARD) {
122b7e04f8cSWim Van Sebroeck 			indydog_stop();
123b7e04f8cSWim Van Sebroeck 			retval = 0;
124b7e04f8cSWim Van Sebroeck 		}
125b7e04f8cSWim Van Sebroeck 		if (options & WDIOS_ENABLECARD) {
126b7e04f8cSWim Van Sebroeck 			indydog_start();
127b7e04f8cSWim Van Sebroeck 			retval = 0;
128b7e04f8cSWim Van Sebroeck 		}
129b7e04f8cSWim Van Sebroeck 		return retval;
130b7e04f8cSWim Van Sebroeck 	}
1310c06090cSWim Van Sebroeck 	case WDIOC_KEEPALIVE:
1320c06090cSWim Van Sebroeck 		indydog_ping();
1330c06090cSWim Van Sebroeck 		return 0;
1340c06090cSWim Van Sebroeck 	case WDIOC_GETTIMEOUT:
1350c06090cSWim Van Sebroeck 		return put_user(WATCHDOG_TIMEOUT, (int *)arg);
1369b9dbccaSAlan Cox 	default:
1379b9dbccaSAlan Cox 		return -ENOTTY;
138b7e04f8cSWim Van Sebroeck 	}
139b7e04f8cSWim Van Sebroeck }
140b7e04f8cSWim Van Sebroeck 
indydog_notify_sys(struct notifier_block * this,unsigned long code,void * unused)1419b9dbccaSAlan Cox static int indydog_notify_sys(struct notifier_block *this,
1429b9dbccaSAlan Cox 					unsigned long code, void *unused)
143b7e04f8cSWim Van Sebroeck {
144b7e04f8cSWim Van Sebroeck 	if (code == SYS_DOWN || code == SYS_HALT)
145b7e04f8cSWim Van Sebroeck 		indydog_stop();		/* Turn the WDT off */
146b7e04f8cSWim Van Sebroeck 
147b7e04f8cSWim Van Sebroeck 	return NOTIFY_DONE;
148b7e04f8cSWim Van Sebroeck }
149b7e04f8cSWim Van Sebroeck 
150b7e04f8cSWim Van Sebroeck static const struct file_operations indydog_fops = {
151b7e04f8cSWim Van Sebroeck 	.owner		= THIS_MODULE,
152b7e04f8cSWim Van Sebroeck 	.llseek		= no_llseek,
153b7e04f8cSWim Van Sebroeck 	.write		= indydog_write,
1549b9dbccaSAlan Cox 	.unlocked_ioctl	= indydog_ioctl,
155*b6dfb247SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
156b7e04f8cSWim Van Sebroeck 	.open		= indydog_open,
157b7e04f8cSWim Van Sebroeck 	.release	= indydog_release,
158b7e04f8cSWim Van Sebroeck };
159b7e04f8cSWim Van Sebroeck 
160b7e04f8cSWim Van Sebroeck static struct miscdevice indydog_miscdev = {
161b7e04f8cSWim Van Sebroeck 	.minor		= WATCHDOG_MINOR,
162b7e04f8cSWim Van Sebroeck 	.name		= "watchdog",
163b7e04f8cSWim Van Sebroeck 	.fops		= &indydog_fops,
164b7e04f8cSWim Van Sebroeck };
165b7e04f8cSWim Van Sebroeck 
166b7e04f8cSWim Van Sebroeck static struct notifier_block indydog_notifier = {
167b7e04f8cSWim Van Sebroeck 	.notifier_call = indydog_notify_sys,
168b7e04f8cSWim Van Sebroeck };
169b7e04f8cSWim Van Sebroeck 
watchdog_init(void)170b7e04f8cSWim Van Sebroeck static int __init watchdog_init(void)
171b7e04f8cSWim Van Sebroeck {
172b7e04f8cSWim Van Sebroeck 	int ret;
173b7e04f8cSWim Van Sebroeck 
174b7e04f8cSWim Van Sebroeck 	ret = register_reboot_notifier(&indydog_notifier);
175b7e04f8cSWim Van Sebroeck 	if (ret) {
17627c766aaSJoe Perches 		pr_err("cannot register reboot notifier (err=%d)\n", ret);
177b7e04f8cSWim Van Sebroeck 		return ret;
178b7e04f8cSWim Van Sebroeck 	}
179b7e04f8cSWim Van Sebroeck 
180b7e04f8cSWim Van Sebroeck 	ret = misc_register(&indydog_miscdev);
181b7e04f8cSWim Van Sebroeck 	if (ret) {
18227c766aaSJoe Perches 		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
183b7e04f8cSWim Van Sebroeck 		       WATCHDOG_MINOR, ret);
184b7e04f8cSWim Van Sebroeck 		unregister_reboot_notifier(&indydog_notifier);
185b7e04f8cSWim Van Sebroeck 		return ret;
186b7e04f8cSWim Van Sebroeck 	}
187b7e04f8cSWim Van Sebroeck 
18827c766aaSJoe Perches 	pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n");
189b7e04f8cSWim Van Sebroeck 
190b7e04f8cSWim Van Sebroeck 	return 0;
191b7e04f8cSWim Van Sebroeck }
192b7e04f8cSWim Van Sebroeck 
watchdog_exit(void)193b7e04f8cSWim Van Sebroeck static void __exit watchdog_exit(void)
194b7e04f8cSWim Van Sebroeck {
195b7e04f8cSWim Van Sebroeck 	misc_deregister(&indydog_miscdev);
196b7e04f8cSWim Van Sebroeck 	unregister_reboot_notifier(&indydog_notifier);
197b7e04f8cSWim Van Sebroeck }
198b7e04f8cSWim Van Sebroeck 
199b7e04f8cSWim Van Sebroeck module_init(watchdog_init);
200b7e04f8cSWim Van Sebroeck module_exit(watchdog_exit);
201b7e04f8cSWim Van Sebroeck 
202b7e04f8cSWim Van Sebroeck MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
203b7e04f8cSWim Van Sebroeck MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
204b7e04f8cSWim Van Sebroeck MODULE_LICENSE("GPL");
205