Merge tag 'linux-kselftest-kunit-5.11-rc1' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / drivers / watchdog / wafer5823wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *      ICP Wafer 5823 Single Board Computer WDT driver
4  *      http://www.icpamerica.com/wafer_5823.php
5  *      May also work on other similar models
6  *
7  *      (c) Copyright 2002 Justin Cormack <justin@street-vision.com>
8  *
9  *      Release 0.02
10  *
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 provide
18  *      warranty for any of this software. This material is provided
19  *      "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 ": " fmt
26
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
44 static unsigned long wafwdt_is_open;
45 static char expect_close;
46 static 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/O
52  *      port WDT_START, then read the port to start the watchdog. To pat
53  *      the dog, read port WDT_STOP to stop the timer, then read WDT_START
54  *      to restart it again.
55  */
56
57 static int wdt_stop = 0x843;
58 static int wdt_start = 0x443;
59
60 static int timeout = WD_TIMO;  /* in seconds */
61 module_param(timeout, int, 0);
62 MODULE_PARM_DESC(timeout,
63                 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
64                                 __MODULE_STRING(WD_TIMO) ".");
65
66 static bool nowayout = WATCHDOG_NOWAYOUT;
67 module_param(nowayout, bool, 0);
68 MODULE_PARM_DESC(nowayout,
69                 "Watchdog cannot be stopped once started (default="
70                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
71
72 static 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
81 static void wafwdt_start(void)
82 {
83         /* start up watchdog */
84         outb_p(timeout, wdt_start);
85         inb_p(wdt_start);
86 }
87
88 static void wafwdt_stop(void)
89 {
90         /* stop watchdog */
91         inb_p(wdt_stop);
92 }
93
94 static 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 magic
106                            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 should
116                    return that favour */
117                 wafwdt_ping();
118         }
119         return count;
120 }
121
122 static 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
187 static 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          *      Activate
194          */
195         wafwdt_start();
196         return stream_open(inode, file);
197 }
198
199 static 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 down
214  */
215
216 static 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 Interfaces
226  */
227
228 static const struct file_operations wafwdt_fops = {
229         .owner          = THIS_MODULE,
230         .llseek         = no_llseek,
231         .write          = wafwdt_write,
232         .unlocked_ioctl = wafwdt_ioctl,
233         .compat_ioctl   = compat_ptr_ioctl,
234         .open           = wafwdt_open,
235         .release        = wafwdt_close,
236 };
237
238 static struct miscdevice wafwdt_miscdev = {
239         .minor  = WATCHDOG_MINOR,
240         .name   = "watchdog",
241         .fops   = &wafwdt_fops,
242 };
243
244 /*
245  *      The WDT needs to learn about soft shutdowns in order to
246  *      turn the timebomb registers off.
247  */
248
249 static struct notifier_block wafwdt_notifier = {
250         .notifier_call = wafwdt_notify_sys,
251 };
252
253 static int __init wafwdt_init(void)
254 {
255         int ret;
256
257         pr_info("WDT driver for Wafer 5823 single board computer initialising\n");
258
259         if (timeout < 1 || timeout > 255) {
260                 timeout = WD_TIMO;
261                 pr_info("timeout value must be 1 <= x <= 255, using %d\n",
262                         timeout);
263         }
264
265         if (wdt_stop != wdt_start) {
266                 if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
267                         pr_err("I/O address 0x%04x already in use\n", wdt_stop);
268                         ret = -EIO;
269                         goto error;
270                 }
271         }
272
273         if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
274                 pr_err("I/O address 0x%04x already in use\n", wdt_start);
275                 ret = -EIO;
276                 goto error2;
277         }
278
279         ret = register_reboot_notifier(&wafwdt_notifier);
280         if (ret != 0) {
281                 pr_err("cannot register reboot notifier (err=%d)\n", ret);
282                 goto error3;
283         }
284
285         ret = misc_register(&wafwdt_miscdev);
286         if (ret != 0) {
287                 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
288                        WATCHDOG_MINOR, ret);
289                 goto error4;
290         }
291
292         pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
293                 timeout, nowayout);
294
295         return ret;
296 error4:
297         unregister_reboot_notifier(&wafwdt_notifier);
298 error3:
299         release_region(wdt_start, 1);
300 error2:
301         if (wdt_stop != wdt_start)
302                 release_region(wdt_stop, 1);
303 error:
304         return ret;
305 }
306
307 static void __exit wafwdt_exit(void)
308 {
309         misc_deregister(&wafwdt_miscdev);
310         unregister_reboot_notifier(&wafwdt_notifier);
311         if (wdt_stop != wdt_start)
312                 release_region(wdt_stop, 1);
313         release_region(wdt_start, 1);
314 }
315
316 module_init(wafwdt_init);
317 module_exit(wafwdt_exit);
318
319 MODULE_AUTHOR("Justin Cormack");
320 MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
321 MODULE_LICENSE("GPL");
322
323 /* end of wafer5823wdt.c */