diff options
| -rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/watchdog/w83627hf_wdt.c | 215 |
2 files changed, 48 insertions, 168 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 74f29a2fc51c..5be6e919f785 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -885,6 +885,7 @@ config VIA_WDT | |||
| 885 | config W83627HF_WDT | 885 | config W83627HF_WDT |
| 886 | tristate "W83627HF/W83627DHG Watchdog Timer" | 886 | tristate "W83627HF/W83627DHG Watchdog Timer" |
| 887 | depends on X86 | 887 | depends on X86 |
| 888 | select WATCHDOG_CORE | ||
| 888 | ---help--- | 889 | ---help--- |
| 889 | This is the driver for the hardware watchdog on the W83627HF chipset | 890 | This is the driver for the hardware watchdog on the W83627HF chipset |
| 890 | as used in Advantech PC-9578 and Tyan S2721-533 motherboards | 891 | as used in Advantech PC-9578 and Tyan S2721-533 motherboards |
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index da6781c12523..ab5ec6d84b16 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * w83627hf/thf WDT driver | 2 | * w83627hf/thf WDT driver |
| 3 | * | 3 | * |
| 4 | * (c) Copyright 2013 Guenter Roeck | ||
| 5 | * converted to watchdog infrastructure | ||
| 6 | * | ||
| 4 | * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> | 7 | * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> |
| 5 | * added support for W83627THF. | 8 | * added support for W83627THF. |
| 6 | * | 9 | * |
| @@ -31,31 +34,22 @@ | |||
| 31 | #include <linux/module.h> | 34 | #include <linux/module.h> |
| 32 | #include <linux/moduleparam.h> | 35 | #include <linux/moduleparam.h> |
| 33 | #include <linux/types.h> | 36 | #include <linux/types.h> |
| 34 | #include <linux/miscdevice.h> | ||
| 35 | #include <linux/watchdog.h> | 37 | #include <linux/watchdog.h> |
| 36 | #include <linux/fs.h> | ||
| 37 | #include <linux/ioport.h> | 38 | #include <linux/ioport.h> |
| 38 | #include <linux/notifier.h> | 39 | #include <linux/notifier.h> |
| 39 | #include <linux/reboot.h> | 40 | #include <linux/reboot.h> |
| 40 | #include <linux/init.h> | 41 | #include <linux/init.h> |
| 41 | #include <linux/spinlock.h> | ||
| 42 | #include <linux/io.h> | 42 | #include <linux/io.h> |
| 43 | #include <linux/uaccess.h> | ||
| 44 | |||
| 45 | 43 | ||
| 46 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" | 44 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" |
| 47 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ | 45 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ |
| 48 | 46 | ||
| 49 | static unsigned long wdt_is_open; | ||
| 50 | static char expect_close; | ||
| 51 | static DEFINE_SPINLOCK(io_lock); | ||
| 52 | |||
| 53 | /* You must set this - there is no sane way to probe for this board. */ | 47 | /* You must set this - there is no sane way to probe for this board. */ |
| 54 | static int wdt_io = 0x2E; | 48 | static int wdt_io = 0x2E; |
| 55 | module_param(wdt_io, int, 0); | 49 | module_param(wdt_io, int, 0); |
| 56 | MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)"); | 50 | MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)"); |
| 57 | 51 | ||
| 58 | static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ | 52 | static int timeout; /* in seconds */ |
| 59 | module_param(timeout, int, 0); | 53 | module_param(timeout, int, 0); |
| 60 | MODULE_PARM_DESC(timeout, | 54 | MODULE_PARM_DESC(timeout, |
| 61 | "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" | 55 | "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" |
| @@ -110,7 +104,7 @@ static void w83627hf_unselect_wd_register(void) | |||
| 110 | /* tyan motherboards seem to set F5 to 0x4C ? | 104 | /* tyan motherboards seem to set F5 to 0x4C ? |
| 111 | * So explicitly init to appropriate value. */ | 105 | * So explicitly init to appropriate value. */ |
| 112 | 106 | ||
| 113 | static void w83627hf_init(void) | 107 | static void w83627hf_init(struct watchdog_device *wdog) |
| 114 | { | 108 | { |
| 115 | unsigned char t; | 109 | unsigned char t; |
| 116 | 110 | ||
| @@ -120,8 +114,8 @@ static void w83627hf_init(void) | |||
| 120 | t = inb_p(WDT_EFDR); /* read CRF6 */ | 114 | t = inb_p(WDT_EFDR); /* read CRF6 */ |
| 121 | if (t != 0) { | 115 | if (t != 0) { |
| 122 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", | 116 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", |
| 123 | timeout); | 117 | wdog->timeout); |
| 124 | outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ | 118 | outb_p(wdog->timeout, WDT_EFDR); /* Write back to CRF6 */ |
| 125 | } | 119 | } |
| 126 | 120 | ||
| 127 | outb_p(0xF5, WDT_EFER); /* Select CRF5 */ | 121 | outb_p(0xF5, WDT_EFER); /* Select CRF5 */ |
| @@ -141,10 +135,8 @@ static void w83627hf_init(void) | |||
| 141 | w83627hf_unselect_wd_register(); | 135 | w83627hf_unselect_wd_register(); |
| 142 | } | 136 | } |
| 143 | 137 | ||
| 144 | static void wdt_set_time(int timeout) | 138 | static int wdt_set_time(unsigned int timeout) |
| 145 | { | 139 | { |
| 146 | spin_lock(&io_lock); | ||
| 147 | |||
| 148 | w83627hf_select_wd_register(); | 140 | w83627hf_select_wd_register(); |
| 149 | 141 | ||
| 150 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ | 142 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ |
| @@ -152,34 +144,29 @@ static void wdt_set_time(int timeout) | |||
| 152 | 144 | ||
| 153 | w83627hf_unselect_wd_register(); | 145 | w83627hf_unselect_wd_register(); |
| 154 | 146 | ||
| 155 | spin_unlock(&io_lock); | 147 | return 0; |
| 156 | } | 148 | } |
| 157 | 149 | ||
| 158 | static int wdt_ping(void) | 150 | static int wdt_start(struct watchdog_device *wdog) |
| 159 | { | 151 | { |
| 160 | wdt_set_time(timeout); | 152 | return wdt_set_time(wdog->timeout); |
| 161 | return 0; | ||
| 162 | } | 153 | } |
| 163 | 154 | ||
| 164 | static int wdt_disable(void) | 155 | static int wdt_stop(struct watchdog_device *wdog) |
| 165 | { | 156 | { |
| 166 | wdt_set_time(0); | 157 | return wdt_set_time(0); |
| 167 | return 0; | ||
| 168 | } | 158 | } |
| 169 | 159 | ||
| 170 | static int wdt_set_heartbeat(int t) | 160 | static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) |
| 171 | { | 161 | { |
| 172 | if (t < 1 || t > 255) | 162 | wdog->timeout = timeout; |
| 173 | return -EINVAL; | 163 | |
| 174 | timeout = t; | ||
| 175 | return 0; | 164 | return 0; |
| 176 | } | 165 | } |
| 177 | 166 | ||
| 178 | static int wdt_get_time(void) | 167 | static unsigned int wdt_get_time(struct watchdog_device *wdog) |
| 179 | { | 168 | { |
| 180 | int timeleft; | 169 | unsigned int timeleft; |
| 181 | |||
| 182 | spin_lock(&io_lock); | ||
| 183 | 170 | ||
| 184 | w83627hf_select_wd_register(); | 171 | w83627hf_select_wd_register(); |
| 185 | 172 | ||
| @@ -188,124 +175,17 @@ static int wdt_get_time(void) | |||
| 188 | 175 | ||
| 189 | w83627hf_unselect_wd_register(); | 176 | w83627hf_unselect_wd_register(); |
| 190 | 177 | ||
| 191 | spin_unlock(&io_lock); | ||
| 192 | |||
| 193 | return timeleft; | 178 | return timeleft; |
| 194 | } | 179 | } |
| 195 | 180 | ||
| 196 | static ssize_t wdt_write(struct file *file, const char __user *buf, | ||
| 197 | size_t count, loff_t *ppos) | ||
| 198 | { | ||
| 199 | if (count) { | ||
| 200 | if (!nowayout) { | ||
| 201 | size_t i; | ||
| 202 | |||
| 203 | expect_close = 0; | ||
| 204 | |||
| 205 | for (i = 0; i != count; i++) { | ||
| 206 | char c; | ||
| 207 | if (get_user(c, buf + i)) | ||
| 208 | return -EFAULT; | ||
| 209 | if (c == 'V') | ||
| 210 | expect_close = 42; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | wdt_ping(); | ||
| 214 | } | ||
| 215 | return count; | ||
| 216 | } | ||
| 217 | |||
| 218 | static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 219 | { | ||
| 220 | void __user *argp = (void __user *)arg; | ||
| 221 | int __user *p = argp; | ||
| 222 | int timeval; | ||
| 223 | static const struct watchdog_info ident = { | ||
| 224 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | | ||
| 225 | WDIOF_MAGICCLOSE, | ||
| 226 | .firmware_version = 1, | ||
| 227 | .identity = "W83627HF WDT", | ||
| 228 | }; | ||
| 229 | |||
| 230 | switch (cmd) { | ||
| 231 | case WDIOC_GETSUPPORT: | ||
| 232 | if (copy_to_user(argp, &ident, sizeof(ident))) | ||
| 233 | return -EFAULT; | ||
| 234 | break; | ||
| 235 | case WDIOC_GETSTATUS: | ||
| 236 | case WDIOC_GETBOOTSTATUS: | ||
| 237 | return put_user(0, p); | ||
| 238 | case WDIOC_SETOPTIONS: | ||
| 239 | { | ||
| 240 | int options, retval = -EINVAL; | ||
| 241 | |||
| 242 | if (get_user(options, p)) | ||
| 243 | return -EFAULT; | ||
| 244 | if (options & WDIOS_DISABLECARD) { | ||
| 245 | wdt_disable(); | ||
| 246 | retval = 0; | ||
| 247 | } | ||
| 248 | if (options & WDIOS_ENABLECARD) { | ||
| 249 | wdt_ping(); | ||
| 250 | retval = 0; | ||
| 251 | } | ||
| 252 | return retval; | ||
| 253 | } | ||
| 254 | case WDIOC_KEEPALIVE: | ||
| 255 | wdt_ping(); | ||
| 256 | break; | ||
| 257 | case WDIOC_SETTIMEOUT: | ||
| 258 | if (get_user(timeval, p)) | ||
| 259 | return -EFAULT; | ||
| 260 | if (wdt_set_heartbeat(timeval)) | ||
| 261 | return -EINVAL; | ||
| 262 | wdt_ping(); | ||
| 263 | /* Fall */ | ||
| 264 | case WDIOC_GETTIMEOUT: | ||
| 265 | return put_user(timeout, p); | ||
| 266 | case WDIOC_GETTIMELEFT: | ||
| 267 | timeval = wdt_get_time(); | ||
| 268 | return put_user(timeval, p); | ||
| 269 | default: | ||
| 270 | return -ENOTTY; | ||
| 271 | } | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | static int wdt_open(struct inode *inode, struct file *file) | ||
| 276 | { | ||
| 277 | if (test_and_set_bit(0, &wdt_is_open)) | ||
| 278 | return -EBUSY; | ||
| 279 | /* | ||
| 280 | * Activate | ||
| 281 | */ | ||
| 282 | |||
| 283 | wdt_ping(); | ||
| 284 | return nonseekable_open(inode, file); | ||
| 285 | } | ||
| 286 | |||
| 287 | static int wdt_close(struct inode *inode, struct file *file) | ||
| 288 | { | ||
| 289 | if (expect_close == 42) | ||
| 290 | wdt_disable(); | ||
| 291 | else { | ||
| 292 | pr_crit("Unexpected close, not stopping watchdog!\n"); | ||
| 293 | wdt_ping(); | ||
| 294 | } | ||
| 295 | expect_close = 0; | ||
| 296 | clear_bit(0, &wdt_is_open); | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* | 181 | /* |
| 301 | * Notifier for system down | 182 | * Notifier for system down |
| 302 | */ | 183 | */ |
| 303 | |||
| 304 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | 184 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, |
| 305 | void *unused) | 185 | void *unused) |
| 306 | { | 186 | { |
| 307 | if (code == SYS_DOWN || code == SYS_HALT) | 187 | if (code == SYS_DOWN || code == SYS_HALT) |
| 308 | wdt_disable(); /* Turn the WDT off */ | 188 | wdt_set_time(0); /* Turn the WDT off */ |
| 309 | 189 | ||
| 310 | return NOTIFY_DONE; | 190 | return NOTIFY_DONE; |
| 311 | } | 191 | } |
| @@ -314,19 +194,25 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | |||
| 314 | * Kernel Interfaces | 194 | * Kernel Interfaces |
| 315 | */ | 195 | */ |
| 316 | 196 | ||
| 317 | static const struct file_operations wdt_fops = { | 197 | static struct watchdog_info wdt_info = { |
| 318 | .owner = THIS_MODULE, | 198 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
| 319 | .llseek = no_llseek, | 199 | .identity = "W83627HF Watchdog", |
| 320 | .write = wdt_write, | ||
| 321 | .unlocked_ioctl = wdt_ioctl, | ||
| 322 | .open = wdt_open, | ||
| 323 | .release = wdt_close, | ||
| 324 | }; | 200 | }; |
| 325 | 201 | ||
| 326 | static struct miscdevice wdt_miscdev = { | 202 | static struct watchdog_ops wdt_ops = { |
| 327 | .minor = WATCHDOG_MINOR, | 203 | .owner = THIS_MODULE, |
| 328 | .name = "watchdog", | 204 | .start = wdt_start, |
| 329 | .fops = &wdt_fops, | 205 | .stop = wdt_stop, |
| 206 | .set_timeout = wdt_set_timeout, | ||
| 207 | .get_timeleft = wdt_get_time, | ||
| 208 | }; | ||
| 209 | |||
| 210 | static struct watchdog_device wdt_dev = { | ||
| 211 | .info = &wdt_info, | ||
| 212 | .ops = &wdt_ops, | ||
| 213 | .timeout = WATCHDOG_TIMEOUT, | ||
| 214 | .min_timeout = 1, | ||
| 215 | .max_timeout = 255, | ||
| 330 | }; | 216 | }; |
| 331 | 217 | ||
| 332 | /* | 218 | /* |
| @@ -344,19 +230,15 @@ static int __init wdt_init(void) | |||
| 344 | 230 | ||
| 345 | pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); | 231 | pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); |
| 346 | 232 | ||
| 347 | if (wdt_set_heartbeat(timeout)) { | ||
| 348 | wdt_set_heartbeat(WATCHDOG_TIMEOUT); | ||
| 349 | pr_info("timeout value must be 1 <= timeout <= 255, using %d\n", | ||
| 350 | WATCHDOG_TIMEOUT); | ||
| 351 | } | ||
| 352 | |||
| 353 | if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { | 233 | if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { |
| 354 | pr_err("I/O address 0x%04x already in use\n", wdt_io); | 234 | pr_err("I/O address 0x%04x already in use\n", wdt_io); |
| 355 | ret = -EIO; | 235 | return -EIO; |
| 356 | goto out; | ||
| 357 | } | 236 | } |
| 358 | 237 | ||
| 359 | w83627hf_init(); | 238 | watchdog_init_timeout(&wdt_dev, timeout, NULL); |
| 239 | watchdog_set_nowayout(&wdt_dev, nowayout); | ||
| 240 | |||
| 241 | w83627hf_init(&wdt_dev); | ||
| 360 | 242 | ||
| 361 | ret = register_reboot_notifier(&wdt_notifier); | 243 | ret = register_reboot_notifier(&wdt_notifier); |
| 362 | if (ret != 0) { | 244 | if (ret != 0) { |
| @@ -364,28 +246,25 @@ static int __init wdt_init(void) | |||
| 364 | goto unreg_regions; | 246 | goto unreg_regions; |
| 365 | } | 247 | } |
| 366 | 248 | ||
| 367 | ret = misc_register(&wdt_miscdev); | 249 | ret = watchdog_register_device(&wdt_dev); |
| 368 | if (ret != 0) { | 250 | if (ret) |
| 369 | pr_err("cannot register miscdev on minor=%d (err=%d)\n", | ||
| 370 | WATCHDOG_MINOR, ret); | ||
| 371 | goto unreg_reboot; | 251 | goto unreg_reboot; |
| 372 | } | ||
| 373 | 252 | ||
| 374 | pr_info("initialized. timeout=%d sec (nowayout=%d)\n", | 253 | pr_info("initialized. timeout=%d sec (nowayout=%d)\n", |
| 375 | timeout, nowayout); | 254 | wdt_dev.timeout, nowayout); |
| 376 | 255 | ||
| 377 | out: | ||
| 378 | return ret; | 256 | return ret; |
| 257 | |||
| 379 | unreg_reboot: | 258 | unreg_reboot: |
| 380 | unregister_reboot_notifier(&wdt_notifier); | 259 | unregister_reboot_notifier(&wdt_notifier); |
| 381 | unreg_regions: | 260 | unreg_regions: |
| 382 | release_region(wdt_io, 1); | 261 | release_region(wdt_io, 1); |
| 383 | goto out; | 262 | return ret; |
| 384 | } | 263 | } |
| 385 | 264 | ||
| 386 | static void __exit wdt_exit(void) | 265 | static void __exit wdt_exit(void) |
| 387 | { | 266 | { |
| 388 | misc_deregister(&wdt_miscdev); | 267 | watchdog_unregister_device(&wdt_dev); |
| 389 | unregister_reboot_notifier(&wdt_notifier); | 268 | unregister_reboot_notifier(&wdt_notifier); |
| 390 | release_region(wdt_io, 1); | 269 | release_region(wdt_io, 1); |
| 391 | } | 270 | } |
