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