wafer5823wdt.c (7070B)
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 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/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 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 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 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 * Activate 194 */ 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 down 214 */ 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 Interfaces 226 */ 227 228static 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 238static 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 249static struct notifier_block wafwdt_notifier = { 250 .notifier_call = wafwdt_notify_sys, 251}; 252 253static 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; 296error4: 297 unregister_reboot_notifier(&wafwdt_notifier); 298error3: 299 release_region(wdt_start, 1); 300error2: 301 if (wdt_stop != wdt_start) 302 release_region(wdt_stop, 1); 303error: 304 return ret; 305} 306 307static 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 316module_init(wafwdt_init); 317module_exit(wafwdt_exit); 318 319MODULE_AUTHOR("Justin Cormack"); 320MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver"); 321MODULE_LICENSE("GPL"); 322 323/* end of wafer5823wdt.c */