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 .write = fop_write, 209 .open = fop_open, 210 .release = fop_close, 211 .unlocked_ioctl = fop_ioctl, 212 .compat_ioctl = compat_ptr_ioctl, 213 }; 214 215 static struct miscdevice wdt_miscdev = { 216 .minor = WATCHDOG_MINOR, 217 .name = "watchdog", 218 .fops = &wdt_fops, 219 }; 220 221 /* 222 * Notifier for system down 223 */ 224 225 static int wdt_notify_sys(struct notifier_block *this, unsigned long code, 226 void *unused) 227 { 228 if (code == SYS_DOWN || code == SYS_HALT) 229 wdt_disable(); 230 return NOTIFY_DONE; 231 } 232 233 static struct notifier_block wdt_notifier = { 234 .notifier_call = wdt_notify_sys, 235 }; 236 237 static void __exit sbc7240_wdt_unload(void) 238 { 239 pr_info("Removing watchdog\n"); 240 misc_deregister(&wdt_miscdev); 241 242 unregister_reboot_notifier(&wdt_notifier); 243 release_region(SBC7240_ENABLE_PORT, 1); 244 } 245 246 static int __init sbc7240_wdt_init(void) 247 { 248 int rc = -EBUSY; 249 250 if (!request_region(SBC7240_ENABLE_PORT, 1, "SBC7240 WDT")) { 251 pr_err("I/O address 0x%04x already in use\n", 252 SBC7240_ENABLE_PORT); 253 rc = -EIO; 254 goto err_out; 255 } 256 257 /* The IO port 0x043 used to disable the watchdog 258 * is already claimed by the system timer, so we 259 * can't request_region() it ...*/ 260 261 if (timeout < 1 || timeout > SBC7240_MAX_TIMEOUT) { 262 timeout = SBC7240_TIMEOUT; 263 pr_info("timeout value must be 1<=x<=%d, using %d\n", 264 SBC7240_MAX_TIMEOUT, timeout); 265 } 266 wdt_set_timeout(timeout); 267 wdt_disable(); 268 269 rc = register_reboot_notifier(&wdt_notifier); 270 if (rc) { 271 pr_err("cannot register reboot notifier (err=%d)\n", rc); 272 goto err_out_region; 273 } 274 275 rc = misc_register(&wdt_miscdev); 276 if (rc) { 277 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 278 wdt_miscdev.minor, rc); 279 goto err_out_reboot_notifier; 280 } 281 282 pr_info("Watchdog driver for SBC7240 initialised (nowayout=%d)\n", 283 nowayout); 284 285 return 0; 286 287 err_out_reboot_notifier: 288 unregister_reboot_notifier(&wdt_notifier); 289 err_out_region: 290 release_region(SBC7240_ENABLE_PORT, 1); 291 err_out: 292 return rc; 293 } 294 295 module_init(sbc7240_wdt_init); 296 module_exit(sbc7240_wdt_unload); 297 298 MODULE_AUTHOR("Gilles Gigan"); 299 MODULE_DESCRIPTION("Watchdog device driver for single board" 300 " computers EPIC Nano 7240 from iEi"); 301 MODULE_LICENSE("GPL"); 302