1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * NANO7240 SBC Watchdog device driver 4 * 5 * Based on w83877f.c by Scott Jennings, 6 * 7 * (c) Copyright 2007 Gilles GIGAN <gilles.gigan@jcu.edu.au> 8 */ 9 10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12 #include <linux/fs.h> 13 #include <linux/init.h> 14 #include <linux/ioport.h> 15 #include <linux/jiffies.h> 16 #include <linux/module.h> 17 #include <linux/moduleparam.h> 18 #include <linux/miscdevice.h> 19 #include <linux/notifier.h> 20 #include <linux/reboot.h> 21 #include <linux/types.h> 22 #include <linux/watchdog.h> 23 #include <linux/io.h> 24 #include <linux/uaccess.h> 25 #include <linux/atomic.h> 26 27 #define SBC7240_ENABLE_PORT 0x443 28 #define SBC7240_DISABLE_PORT 0x043 29 #define SBC7240_SET_TIMEOUT_PORT SBC7240_ENABLE_PORT 30 #define SBC7240_MAGIC_CHAR 'V' 31 32 #define SBC7240_TIMEOUT 30 33 #define SBC7240_MAX_TIMEOUT 255 34 static int timeout = SBC7240_TIMEOUT; /* in seconds */ 35 module_param(timeout, int, 0); 36 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=" 37 __MODULE_STRING(SBC7240_MAX_TIMEOUT) ", default=" 38 __MODULE_STRING(SBC7240_TIMEOUT) ")"); 39 40 static bool nowayout = WATCHDOG_NOWAYOUT; 41 module_param(nowayout, bool, 0); 42 MODULE_PARM_DESC(nowayout, "Disable watchdog when closing device file"); 43 44 #define SBC7240_OPEN_STATUS_BIT 0 45 #define SBC7240_ENABLED_STATUS_BIT 1 46 #define SBC7240_EXPECT_CLOSE_STATUS_BIT 2 47 static unsigned long wdt_status; 48 49 /* 50 * Utility routines 51 */ 52 53 static void wdt_disable(void) 54 { 55 /* disable the watchdog */ 56 if (test_and_clear_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) { 57 inb_p(SBC7240_DISABLE_PORT); 58 pr_info("Watchdog timer is now disabled\n"); 59 } 60 } 61 62 static void wdt_enable(void) 63 { 64 /* enable the watchdog */ 65 if (!test_and_set_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) { 66 inb_p(SBC7240_ENABLE_PORT); 67 pr_info("Watchdog timer is now enabled\n"); 68 } 69 } 70 71 static int wdt_set_timeout(int t) 72 { 73 if (t < 1 || t > SBC7240_MAX_TIMEOUT) { 74 pr_err("timeout value must be 1<=x<=%d\n", SBC7240_MAX_TIMEOUT); 75 return -1; 76 } 77 /* set the timeout */ 78 outb_p((unsigned)t, SBC7240_SET_TIMEOUT_PORT); 79 timeout = t; 80 pr_info("timeout set to %d seconds\n", t); 81 return 0; 82 } 83 84 /* Whack the dog */ 85 static inline void wdt_keepalive(void) 86 { 87 if (test_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) 88 inb_p(SBC7240_ENABLE_PORT); 89 } 90 91 /* 92 * /dev/watchdog handling 93 */ 94 static ssize_t fop_write(struct file *file, const char __user *buf, 95 size_t count, loff_t *ppos) 96 { 97 size_t i; 98 char c; 99 100 if (count) { 101 if (!nowayout) { 102 clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, 103 &wdt_status); 104 105 /* is there a magic char ? */ 106 for (i = 0; i != count; i++) { 107 if (get_user(c, buf + i)) 108 return -EFAULT; 109 if (c == SBC7240_MAGIC_CHAR) { 110 set_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, 111 &wdt_status); 112 break; 113 } 114 } 115 } 116 117 wdt_keepalive(); 118 } 119 120 return count; 121 } 122 123 static int fop_open(struct inode *inode, struct file *file) 124 { 125 if (test_and_set_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status)) 126 return -EBUSY; 127 128 wdt_enable(); 129 130 return stream_open(inode, file); 131 } 132 133 static int fop_close(struct inode *inode, struct file *file) 134 { 135 if (test_and_clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, &wdt_status) 136 || !nowayout) { 137 wdt_disable(); 138 } else { 139 pr_crit("Unexpected close, not stopping watchdog!\n"); 140 wdt_keepalive(); 141 } 142 143 clear_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status); 144 return 0; 145 } 146 147 static const struct watchdog_info ident = { 148 .options = WDIOF_KEEPALIVEPING| 149 WDIOF_SETTIMEOUT| 150 WDIOF_MAGICCLOSE, 151 .firmware_version = 1, 152 .identity = "SBC7240", 153 }; 154 155 156 static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 157 { 158 switch (cmd) { 159 case WDIOC_GETSUPPORT: 160 return copy_to_user((void __user *)arg, &ident, sizeof(ident)) 161 ? -EFAULT : 0; 162 case WDIOC_GETSTATUS: 163 case WDIOC_GETBOOTSTATUS: 164 return put_user(0, (int __user *)arg); 165 case WDIOC_SETOPTIONS: 166 { 167 int options; 168 int retval = -EINVAL; 169 170 if (get_user(options, (int __user *)arg)) 171 return -EFAULT; 172 173 if (options & WDIOS_DISABLECARD) { 174 wdt_disable(); 175 retval = 0; 176 } 177 178 if (options & WDIOS_ENABLECARD) { 179 wdt_enable(); 180 retval = 0; 181 } 182 183 return retval; 184 } 185 case WDIOC_KEEPALIVE: 186 wdt_keepalive(); 187 return 0; 188 case WDIOC_SETTIMEOUT: 189 { 190 int new_timeout; 191 192 if (get_user(new_timeout, (int __user *)arg)) 193 return -EFAULT; 194 195 if (wdt_set_timeout(new_timeout)) 196 return -EINVAL; 197 } 198 fallthrough; 199 case WDIOC_GETTIMEOUT: 200 return put_user(timeout, (int __user *)arg); 201 default: 202 return -ENOTTY; 203 } 204 } 205 206 static const struct file_operations wdt_fops = { 207 .owner = THIS_MODULE, 208 .llseek = no_llseek, 209 .write = fop_write, 210 .open = fop_open, 211 .release = fop_close, 212 .unlocked_ioctl = fop_ioctl, 213 .compat_ioctl = compat_ptr_ioctl, 214 }; 215 216 static struct miscdevice wdt_miscdev = { 217 .minor = WATCHDOG_MINOR, 218 .name = "watchdog", 219 .fops = &wdt_fops, 220 }; 221 222 /* 223 * Notifier for system down 224 */ 225 226 static int wdt_notify_sys(struct notifier_block *this, unsigned long code, 227 void *unused) 228 { 229 if (code == SYS_DOWN || code == SYS_HALT) 230 wdt_disable(); 231 return NOTIFY_DONE; 232 } 233 234 static struct notifier_block wdt_notifier = { 235 .notifier_call = wdt_notify_sys, 236 }; 237 238 static void __exit sbc7240_wdt_unload(void) 239 { 240 pr_info("Removing watchdog\n"); 241 misc_deregister(&wdt_miscdev); 242 243 unregister_reboot_notifier(&wdt_notifier); 244 release_region(SBC7240_ENABLE_PORT, 1); 245 } 246 247 static int __init sbc7240_wdt_init(void) 248 { 249 int rc = -EBUSY; 250 251 if (!request_region(SBC7240_ENABLE_PORT, 1, "SBC7240 WDT")) { 252 pr_err("I/O address 0x%04x already in use\n", 253 SBC7240_ENABLE_PORT); 254 rc = -EIO; 255 goto err_out; 256 } 257 258 /* The IO port 0x043 used to disable the watchdog 259 * is already claimed by the system timer, so we 260 * can't request_region() it ...*/ 261 262 if (timeout < 1 || timeout > SBC7240_MAX_TIMEOUT) { 263 timeout = SBC7240_TIMEOUT; 264 pr_info("timeout value must be 1<=x<=%d, using %d\n", 265 SBC7240_MAX_TIMEOUT, timeout); 266 } 267 wdt_set_timeout(timeout); 268 wdt_disable(); 269 270 rc = register_reboot_notifier(&wdt_notifier); 271 if (rc) { 272 pr_err("cannot register reboot notifier (err=%d)\n", rc); 273 goto err_out_region; 274 } 275 276 rc = misc_register(&wdt_miscdev); 277 if (rc) { 278 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 279 wdt_miscdev.minor, rc); 280 goto err_out_reboot_notifier; 281 } 282 283 pr_info("Watchdog driver for SBC7240 initialised (nowayout=%d)\n", 284 nowayout); 285 286 return 0; 287 288 err_out_reboot_notifier: 289 unregister_reboot_notifier(&wdt_notifier); 290 err_out_region: 291 release_region(SBC7240_ENABLE_PORT, 1); 292 err_out: 293 return rc; 294 } 295 296 module_init(sbc7240_wdt_init); 297 module_exit(sbc7240_wdt_unload); 298 299 MODULE_AUTHOR("Gilles Gigan"); 300 MODULE_DESCRIPTION("Watchdog device driver for single board" 301 " computers EPIC Nano 7240 from iEi"); 302 MODULE_LICENSE("GPL"); 303