Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog

* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
[WATCHDOG] Add support for the WM8350 watchdog
[WATCHDOG] Add SMSC SCH311x Watchdog Timer.
[WATCHDOG] ib700wdt - add timeout parameter

+947 -30
+19
drivers/watchdog/Kconfig
··· 55 55 To compile this driver as a module, choose M here: the 56 56 module will be called softdog. 57 57 58 + config WM8350_WATCHDOG 59 + tristate "WM8350 watchdog" 60 + depends on MFD_WM8350 61 + help 62 + Support for the watchdog in the WM8350 AudioPlus PMIC. When 63 + the watchdog triggers the system will be reset. 64 + 58 65 # ALPHA Architecture 59 66 60 67 # ARM Architecture ··· 557 550 TBD. 558 551 To compile this driver as a module, choose M here: the 559 552 module will be called cpu5wdt. 553 + 554 + config SMSC_SCH311X_WDT 555 + tristate "SMSC SCH311X Watchdog Timer" 556 + depends on X86 557 + ---help--- 558 + This is the driver for the hardware watchdog timer on the 559 + SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset 560 + (LPC IO with 8042 KBC, Reset Generation, HWM and multiple 561 + serial ports). 562 + 563 + To compile this driver as a module, choose M here: the 564 + module will be called sch311x_wdt. 560 565 561 566 config SMSC37B787_WDT 562 567 tristate "Winbond SMsC37B787 Watchdog Timer"
+2
drivers/watchdog/Makefile
··· 83 83 obj-$(CONFIG_SBC8360_WDT) += sbc8360.o 84 84 obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o 85 85 obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o 86 + obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o 86 87 obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 87 88 obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 88 89 obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o ··· 134 133 # XTENSA Architecture 135 134 136 135 # Architecture Independant 136 + obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 137 137 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+19 -30
drivers/watchdog/ib700wdt.c
··· 91 91 * 92 92 */ 93 93 94 - static int wd_times[] = { 95 - 30, /* 0x0 */ 96 - 28, /* 0x1 */ 97 - 26, /* 0x2 */ 98 - 24, /* 0x3 */ 99 - 22, /* 0x4 */ 100 - 20, /* 0x5 */ 101 - 18, /* 0x6 */ 102 - 16, /* 0x7 */ 103 - 14, /* 0x8 */ 104 - 12, /* 0x9 */ 105 - 10, /* 0xA */ 106 - 8, /* 0xB */ 107 - 6, /* 0xC */ 108 - 4, /* 0xD */ 109 - 2, /* 0xE */ 110 - 0, /* 0xF */ 111 - }; 112 - 113 94 #define WDT_STOP 0x441 114 95 #define WDT_START 0x443 115 96 116 97 /* Default timeout */ 117 - #define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ 118 - 119 - static int wd_margin = WD_TIMO; 98 + #define WATCHDOG_TIMEOUT 30 /* 30 seconds +/- 20% */ 99 + static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ 100 + module_param(timeout, int, 0); 101 + MODULE_PARM_DESC(timeout, 102 + "Watchdog timeout in seconds. 0<= timeout <=30, default=" 103 + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 120 104 121 105 static int nowayout = WATCHDOG_NOWAYOUT; 122 106 module_param(nowayout, int, 0); ··· 115 131 116 132 static void ibwdt_ping(void) 117 133 { 134 + int wd_margin = 15 - ((timeout + 1) / 2); 135 + 118 136 spin_lock(&ibwdt_lock); 119 137 120 138 /* Write a watchdog value */ ··· 134 148 135 149 static int ibwdt_set_heartbeat(int t) 136 150 { 137 - int i; 138 - 139 - if ((t < 0) || (t > 30)) 151 + if (t < 0 || t > 30) 140 152 return -EINVAL; 141 153 142 - for (i = 0x0F; i > -1; i--) 143 - if (wd_times[i] >= t) 144 - break; 145 - wd_margin = i; 154 + timeout = t; 146 155 return 0; 147 156 } 148 157 ··· 221 240 /* Fall */ 222 241 223 242 case WDIOC_GETTIMEOUT: 224 - return put_user(wd_times[wd_margin], p); 243 + return put_user(timeout, p); 225 244 226 245 default: 227 246 return -ENOTTY; ··· 296 315 WDT_START); 297 316 res = -EIO; 298 317 goto out_nostartreg; 318 + } 319 + 320 + /* Check that the heartbeat value is within it's range ; 321 + * if not reset to the default */ 322 + if (ibwdt_set_heartbeat(timeout)) { 323 + ibwdt_set_heartbeat(WATCHDOG_TIMEOUT); 324 + printk(KERN_INFO PFX 325 + "timeout value must be 0<=x<=30, using %d\n", timeout); 299 326 } 300 327 301 328 res = misc_register(&ibwdt_miscdev);
+578
drivers/watchdog/sch311x_wdt.c
··· 1 + /* 2 + * sch311x_wdt.c - Driver for the SCH311x Super-I/O chips 3 + * integrated watchdog. 4 + * 5 + * (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>. 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * as published by the Free Software Foundation; either version 10 + * 2 of the License, or (at your option) any later version. 11 + * 12 + * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor 13 + * provide warranty for any of this software. This material is 14 + * provided "AS-IS" and at no charge. 15 + */ 16 + 17 + /* 18 + * Includes, defines, variables, module parameters, ... 19 + */ 20 + 21 + /* Includes */ 22 + #include <linux/module.h> /* For module specific items */ 23 + #include <linux/moduleparam.h> /* For new moduleparam's */ 24 + #include <linux/types.h> /* For standard types (like size_t) */ 25 + #include <linux/errno.h> /* For the -ENODEV/... values */ 26 + #include <linux/kernel.h> /* For printk/... */ 27 + #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV 28 + (WATCHDOG_MINOR) */ 29 + #include <linux/watchdog.h> /* For the watchdog specific items */ 30 + #include <linux/init.h> /* For __init/__exit/... */ 31 + #include <linux/fs.h> /* For file operations */ 32 + #include <linux/platform_device.h> /* For platform_driver framework */ 33 + #include <linux/ioport.h> /* For io-port access */ 34 + #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ 35 + #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ 36 + #include <linux/io.h> /* For inb/outb/... */ 37 + 38 + /* Module and version information */ 39 + #define DRV_NAME "sch311x_wdt" 40 + #define PFX DRV_NAME ": " 41 + 42 + /* Runtime registers */ 43 + #define RESGEN 0x1d 44 + #define GP60 0x47 45 + #define WDT_TIME_OUT 0x65 46 + #define WDT_VAL 0x66 47 + #define WDT_CFG 0x67 48 + #define WDT_CTRL 0x68 49 + 50 + /* internal variables */ 51 + static unsigned long sch311x_wdt_is_open; 52 + static char sch311x_wdt_expect_close; 53 + static struct platform_device *sch311x_wdt_pdev; 54 + 55 + static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x00 }; 56 + 57 + static struct { /* The devices private data */ 58 + /* the Runtime Register base address */ 59 + unsigned short runtime_reg; 60 + /* The card's boot status */ 61 + int boot_status; 62 + /* the lock for io operations */ 63 + spinlock_t io_lock; 64 + } sch311x_wdt_data; 65 + 66 + /* Module load parameters */ 67 + static unsigned short force_id; 68 + module_param(force_id, ushort, 0); 69 + MODULE_PARM_DESC(force_id, "Override the detected device ID"); 70 + 71 + static unsigned short therm_trip; 72 + module_param(therm_trip, ushort, 0); 73 + MODULE_PARM_DESC(therm_trip, "Should a ThermTrip trigger the reset generator"); 74 + 75 + #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ 76 + static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ 77 + module_param(timeout, int, 0); 78 + MODULE_PARM_DESC(timeout, 79 + "Watchdog timeout in seconds. 1<= timeout <=15300, default=" 80 + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 81 + 82 + static int nowayout = WATCHDOG_NOWAYOUT; 83 + module_param(nowayout, int, 0); 84 + MODULE_PARM_DESC(nowayout, 85 + "Watchdog cannot be stopped once started (default=" 86 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 87 + 88 + /* 89 + * Super-IO functions 90 + */ 91 + 92 + static inline void sch311x_sio_enter(int sio_config_port) 93 + { 94 + outb(0x55, sio_config_port); 95 + } 96 + 97 + static inline void sch311x_sio_exit(int sio_config_port) 98 + { 99 + outb(0xaa, sio_config_port); 100 + } 101 + 102 + static inline int sch311x_sio_inb(int sio_config_port, int reg) 103 + { 104 + outb(reg, sio_config_port); 105 + return inb(sio_config_port + 1); 106 + } 107 + 108 + static inline void sch311x_sio_outb(int sio_config_port, int reg, int val) 109 + { 110 + outb(reg, sio_config_port); 111 + outb(val, sio_config_port + 1); 112 + } 113 + 114 + /* 115 + * Watchdog Operations 116 + */ 117 + 118 + static void sch311x_wdt_set_timeout(int t) 119 + { 120 + unsigned char timeout_unit = 0x80; 121 + 122 + /* When new timeout is bigger then 255 seconds, we will use minutes */ 123 + if (t > 255) { 124 + timeout_unit = 0; 125 + t /= 60; 126 + } 127 + 128 + /* -- Watchdog Timeout -- 129 + * Bit 0-6 (Reserved) 130 + * Bit 7 WDT Time-out Value Units Select 131 + * (0 = Minutes, 1 = Seconds) 132 + */ 133 + outb(timeout_unit, sch311x_wdt_data.runtime_reg + WDT_TIME_OUT); 134 + 135 + /* -- Watchdog Timer Time-out Value -- 136 + * Bit 0-7 Binary coded units (0=Disabled, 1..255) 137 + */ 138 + outb(t, sch311x_wdt_data.runtime_reg + WDT_VAL); 139 + } 140 + 141 + static void sch311x_wdt_start(void) 142 + { 143 + spin_lock(&sch311x_wdt_data.io_lock); 144 + 145 + /* set watchdog's timeout */ 146 + sch311x_wdt_set_timeout(timeout); 147 + /* enable the watchdog */ 148 + /* -- General Purpose I/O Bit 6.0 -- 149 + * Bit 0, In/Out: 0 = Output, 1 = Input 150 + * Bit 1, Polarity: 0 = No Invert, 1 = Invert 151 + * Bit 2-3, Function select: 00 = GPI/O, 01 = LED1, 11 = WDT, 152 + * 10 = Either Edge Triggered Intr.4 153 + * Bit 4-6 (Reserved) 154 + * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain 155 + */ 156 + outb(0x0e, sch311x_wdt_data.runtime_reg + GP60); 157 + 158 + spin_unlock(&sch311x_wdt_data.io_lock); 159 + 160 + } 161 + 162 + static void sch311x_wdt_stop(void) 163 + { 164 + spin_lock(&sch311x_wdt_data.io_lock); 165 + 166 + /* stop the watchdog */ 167 + outb(0x01, sch311x_wdt_data.runtime_reg + GP60); 168 + /* disable timeout by setting it to 0 */ 169 + sch311x_wdt_set_timeout(0); 170 + 171 + spin_unlock(&sch311x_wdt_data.io_lock); 172 + } 173 + 174 + static void sch311x_wdt_keepalive(void) 175 + { 176 + spin_lock(&sch311x_wdt_data.io_lock); 177 + sch311x_wdt_set_timeout(timeout); 178 + spin_unlock(&sch311x_wdt_data.io_lock); 179 + } 180 + 181 + static int sch311x_wdt_set_heartbeat(int t) 182 + { 183 + if (t < 1 || t > (255*60)) 184 + return -EINVAL; 185 + 186 + /* When new timeout is bigger then 255 seconds, 187 + * we will round up to minutes (with a max of 255) */ 188 + if (t > 255) 189 + t = (((t - 1) / 60) + 1) * 60; 190 + 191 + timeout = t; 192 + return 0; 193 + } 194 + 195 + static void sch311x_wdt_get_status(int *status) 196 + { 197 + unsigned char new_status; 198 + 199 + *status = 0; 200 + 201 + spin_lock(&sch311x_wdt_data.io_lock); 202 + 203 + /* -- Watchdog timer control -- 204 + * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured 205 + * Bit 1 Reserved 206 + * Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning) 207 + * Bit 3 P20 Force Timeout enabled: 208 + * 0 = P20 activity does not generate the WD timeout event 209 + * 1 = P20 Allows rising edge of P20, from the keyboard 210 + * controller, to force the WD timeout event. 211 + * Bit 4-7 Reserved 212 + */ 213 + new_status = inb(sch311x_wdt_data.runtime_reg + WDT_CTRL); 214 + if (new_status & 0x01) 215 + *status |= WDIOF_CARDRESET; 216 + 217 + spin_unlock(&sch311x_wdt_data.io_lock); 218 + } 219 + 220 + /* 221 + * /dev/watchdog handling 222 + */ 223 + 224 + static ssize_t sch311x_wdt_write(struct file *file, const char __user *buf, 225 + size_t count, loff_t *ppos) 226 + { 227 + if (count) { 228 + if (!nowayout) { 229 + size_t i; 230 + 231 + sch311x_wdt_expect_close = 0; 232 + 233 + for (i = 0; i != count; i++) { 234 + char c; 235 + if (get_user(c, buf + i)) 236 + return -EFAULT; 237 + if (c == 'V') 238 + sch311x_wdt_expect_close = 42; 239 + } 240 + } 241 + sch311x_wdt_keepalive(); 242 + } 243 + return count; 244 + } 245 + 246 + static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd, 247 + unsigned long arg) 248 + { 249 + int status; 250 + int new_timeout; 251 + void __user *argp = (void __user *)arg; 252 + int __user *p = argp; 253 + static struct watchdog_info ident = { 254 + .options = WDIOF_KEEPALIVEPING | 255 + WDIOF_SETTIMEOUT | 256 + WDIOF_MAGICCLOSE, 257 + .firmware_version = 1, 258 + .identity = DRV_NAME, 259 + }; 260 + 261 + switch (cmd) { 262 + case WDIOC_GETSUPPORT: 263 + if (copy_to_user(argp, &ident, sizeof(ident))) 264 + return -EFAULT; 265 + break; 266 + 267 + case WDIOC_GETSTATUS: 268 + { 269 + sch311x_wdt_get_status(&status); 270 + return put_user(status, p); 271 + } 272 + case WDIOC_GETBOOTSTATUS: 273 + return put_user(sch311x_wdt_data.boot_status, p); 274 + 275 + case WDIOC_SETOPTIONS: 276 + { 277 + int options, retval = -EINVAL; 278 + 279 + if (get_user(options, p)) 280 + return -EFAULT; 281 + if (options & WDIOS_DISABLECARD) { 282 + sch311x_wdt_stop(); 283 + retval = 0; 284 + } 285 + if (options & WDIOS_ENABLECARD) { 286 + sch311x_wdt_start(); 287 + retval = 0; 288 + } 289 + return retval; 290 + } 291 + case WDIOC_KEEPALIVE: 292 + sch311x_wdt_keepalive(); 293 + break; 294 + 295 + case WDIOC_SETTIMEOUT: 296 + if (get_user(new_timeout, p)) 297 + return -EFAULT; 298 + if (sch311x_wdt_set_heartbeat(new_timeout)) 299 + return -EINVAL; 300 + sch311x_wdt_keepalive(); 301 + /* Fall */ 302 + case WDIOC_GETTIMEOUT: 303 + return put_user(timeout, p); 304 + default: 305 + return -ENOTTY; 306 + } 307 + return 0; 308 + } 309 + 310 + static int sch311x_wdt_open(struct inode *inode, struct file *file) 311 + { 312 + if (test_and_set_bit(0, &sch311x_wdt_is_open)) 313 + return -EBUSY; 314 + /* 315 + * Activate 316 + */ 317 + sch311x_wdt_start(); 318 + return nonseekable_open(inode, file); 319 + } 320 + 321 + static int sch311x_wdt_close(struct inode *inode, struct file *file) 322 + { 323 + if (sch311x_wdt_expect_close == 42) { 324 + sch311x_wdt_stop(); 325 + } else { 326 + printk(KERN_CRIT PFX 327 + "Unexpected close, not stopping watchdog!\n"); 328 + sch311x_wdt_keepalive(); 329 + } 330 + clear_bit(0, &sch311x_wdt_is_open); 331 + sch311x_wdt_expect_close = 0; 332 + return 0; 333 + } 334 + 335 + /* 336 + * Kernel Interfaces 337 + */ 338 + 339 + static const struct file_operations sch311x_wdt_fops = { 340 + .owner = THIS_MODULE, 341 + .llseek = no_llseek, 342 + .write = sch311x_wdt_write, 343 + .unlocked_ioctl = sch311x_wdt_ioctl, 344 + .open = sch311x_wdt_open, 345 + .release = sch311x_wdt_close, 346 + }; 347 + 348 + static struct miscdevice sch311x_wdt_miscdev = { 349 + .minor = WATCHDOG_MINOR, 350 + .name = "watchdog", 351 + .fops = &sch311x_wdt_fops, 352 + }; 353 + 354 + /* 355 + * Init & exit routines 356 + */ 357 + 358 + static int __devinit sch311x_wdt_probe(struct platform_device *pdev) 359 + { 360 + struct device *dev = &pdev->dev; 361 + unsigned char val; 362 + int err; 363 + 364 + spin_lock_init(&sch311x_wdt_data.io_lock); 365 + 366 + if (!request_region(sch311x_wdt_data.runtime_reg + RESGEN, 1, 367 + DRV_NAME)) { 368 + dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n", 369 + sch311x_wdt_data.runtime_reg + RESGEN, 370 + sch311x_wdt_data.runtime_reg + RESGEN); 371 + err = -EBUSY; 372 + goto exit; 373 + } 374 + 375 + if (!request_region(sch311x_wdt_data.runtime_reg + GP60, 1, DRV_NAME)) { 376 + dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n", 377 + sch311x_wdt_data.runtime_reg + GP60, 378 + sch311x_wdt_data.runtime_reg + GP60); 379 + err = -EBUSY; 380 + goto exit_release_region; 381 + } 382 + 383 + if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4, 384 + DRV_NAME)) { 385 + dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n", 386 + sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 387 + sch311x_wdt_data.runtime_reg + WDT_CTRL); 388 + err = -EBUSY; 389 + goto exit_release_region2; 390 + } 391 + 392 + /* Make sure that the watchdog is not running */ 393 + sch311x_wdt_stop(); 394 + 395 + /* Disable keyboard and mouse interaction and interrupt */ 396 + /* -- Watchdog timer configuration -- 397 + * Bit 0 Reserved 398 + * Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr. 399 + * Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr 400 + * Bit 3 Reserved 401 + * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled, 402 + * 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15) 403 + */ 404 + outb(0, sch311x_wdt_data.runtime_reg + WDT_CFG); 405 + 406 + /* Check that the heartbeat value is within it's range ; 407 + * if not reset to the default */ 408 + if (sch311x_wdt_set_heartbeat(timeout)) { 409 + sch311x_wdt_set_heartbeat(WATCHDOG_TIMEOUT); 410 + dev_info(dev, "timeout value must be 1<=x<=15300, using %d\n", 411 + timeout); 412 + } 413 + 414 + /* Get status at boot */ 415 + sch311x_wdt_get_status(&sch311x_wdt_data.boot_status); 416 + 417 + /* enable watchdog */ 418 + /* -- Reset Generator -- 419 + * Bit 0 Enable Watchdog Timer Generation: 0* = Enabled, 1 = Disabled 420 + * Bit 1 Thermtrip Source Select: O* = No Source, 1 = Source 421 + * Bit 2 WDT2_CTL: WDT input bit 422 + * Bit 3-7 Reserved 423 + */ 424 + outb(0, sch311x_wdt_data.runtime_reg + RESGEN); 425 + val = therm_trip ? 0x06 : 0x04; 426 + outb(val, sch311x_wdt_data.runtime_reg + RESGEN); 427 + 428 + err = misc_register(&sch311x_wdt_miscdev); 429 + if (err != 0) { 430 + dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", 431 + WATCHDOG_MINOR, err); 432 + goto exit_release_region3; 433 + } 434 + 435 + sch311x_wdt_miscdev.parent = dev; 436 + 437 + dev_info(dev, 438 + "SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n", 439 + timeout, nowayout); 440 + 441 + return 0; 442 + 443 + exit_release_region3: 444 + release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4); 445 + exit_release_region2: 446 + release_region(sch311x_wdt_data.runtime_reg + GP60, 1); 447 + exit_release_region: 448 + release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1); 449 + sch311x_wdt_data.runtime_reg = 0; 450 + exit: 451 + return err; 452 + } 453 + 454 + static int __devexit sch311x_wdt_remove(struct platform_device *pdev) 455 + { 456 + /* Stop the timer before we leave */ 457 + if (!nowayout) 458 + sch311x_wdt_stop(); 459 + 460 + /* Deregister */ 461 + misc_deregister(&sch311x_wdt_miscdev); 462 + release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4); 463 + release_region(sch311x_wdt_data.runtime_reg + GP60, 1); 464 + release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1); 465 + sch311x_wdt_data.runtime_reg = 0; 466 + return 0; 467 + } 468 + 469 + static void sch311x_wdt_shutdown(struct platform_device *dev) 470 + { 471 + /* Turn the WDT off if we have a soft shutdown */ 472 + sch311x_wdt_stop(); 473 + } 474 + 475 + #define sch311x_wdt_suspend NULL 476 + #define sch311x_wdt_resume NULL 477 + 478 + static struct platform_driver sch311x_wdt_driver = { 479 + .probe = sch311x_wdt_probe, 480 + .remove = __devexit_p(sch311x_wdt_remove), 481 + .shutdown = sch311x_wdt_shutdown, 482 + .suspend = sch311x_wdt_suspend, 483 + .resume = sch311x_wdt_resume, 484 + .driver = { 485 + .owner = THIS_MODULE, 486 + .name = DRV_NAME, 487 + }, 488 + }; 489 + 490 + static int __init sch311x_detect(int sio_config_port, unsigned short *addr) 491 + { 492 + int err = 0, reg; 493 + unsigned short base_addr; 494 + unsigned char dev_id; 495 + 496 + sch311x_sio_enter(sio_config_port); 497 + 498 + /* Check device ID. We currently know about: 499 + * SCH3112 (0x7c), SCH3114 (0x7d), and SCH3116 (0x7f). */ 500 + reg = force_id ? force_id : sch311x_sio_inb(sio_config_port, 0x20); 501 + if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) { 502 + err = -ENODEV; 503 + goto exit; 504 + } 505 + dev_id = reg == 0x7c ? 2 : reg == 0x7d ? 4 : 6; 506 + 507 + /* Select logical device A (runtime registers) */ 508 + sch311x_sio_outb(sio_config_port, 0x07, 0x0a); 509 + 510 + /* Check if Logical Device Register is currently active */ 511 + if (sch311x_sio_inb(sio_config_port, 0x30) && 0x01 == 0) 512 + printk(KERN_INFO PFX "Seems that LDN 0x0a is not active...\n"); 513 + 514 + /* Get the base address of the runtime registers */ 515 + base_addr = (sch311x_sio_inb(sio_config_port, 0x60) << 8) | 516 + sch311x_sio_inb(sio_config_port, 0x61); 517 + if (!base_addr) { 518 + printk(KERN_ERR PFX "Base address not set.\n"); 519 + err = -ENODEV; 520 + goto exit; 521 + } 522 + *addr = base_addr; 523 + 524 + printk(KERN_INFO PFX "Found an SMSC SCH311%d chip at 0x%04x\n", 525 + dev_id, base_addr); 526 + 527 + exit: 528 + sch311x_sio_exit(sio_config_port); 529 + return err; 530 + } 531 + 532 + static int __init sch311x_wdt_init(void) 533 + { 534 + int err, i, found = 0; 535 + unsigned short addr = 0; 536 + 537 + for (i = 0; !found && sch311x_ioports[i]; i++) 538 + if (sch311x_detect(sch311x_ioports[i], &addr) == 0) 539 + found++; 540 + 541 + if (!found) 542 + return -ENODEV; 543 + 544 + sch311x_wdt_data.runtime_reg = addr; 545 + 546 + err = platform_driver_register(&sch311x_wdt_driver); 547 + if (err) 548 + return err; 549 + 550 + sch311x_wdt_pdev = platform_device_register_simple(DRV_NAME, addr, 551 + NULL, 0); 552 + 553 + if (IS_ERR(sch311x_wdt_pdev)) { 554 + err = PTR_ERR(sch311x_wdt_pdev); 555 + goto unreg_platform_driver; 556 + } 557 + 558 + return 0; 559 + 560 + unreg_platform_driver: 561 + platform_driver_unregister(&sch311x_wdt_driver); 562 + return err; 563 + } 564 + 565 + static void __exit sch311x_wdt_exit(void) 566 + { 567 + platform_device_unregister(sch311x_wdt_pdev); 568 + platform_driver_unregister(&sch311x_wdt_driver); 569 + } 570 + 571 + module_init(sch311x_wdt_init); 572 + module_exit(sch311x_wdt_exit); 573 + 574 + MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); 575 + MODULE_DESCRIPTION("SMSC SCH311x WatchDog Timer Driver"); 576 + MODULE_LICENSE("GPL"); 577 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 578 +
+329
drivers/watchdog/wm8350_wdt.c
··· 1 + /* 2 + * Watchdog driver for the wm8350 3 + * 4 + * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation 9 + */ 10 + 11 + #include <linux/module.h> 12 + #include <linux/moduleparam.h> 13 + #include <linux/types.h> 14 + #include <linux/kernel.h> 15 + #include <linux/fs.h> 16 + #include <linux/miscdevice.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/watchdog.h> 19 + #include <linux/uaccess.h> 20 + #include <linux/mfd/wm8350/core.h> 21 + 22 + static int nowayout = WATCHDOG_NOWAYOUT; 23 + module_param(nowayout, int, 0); 24 + MODULE_PARM_DESC(nowayout, 25 + "Watchdog cannot be stopped once started (default=" 26 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 27 + 28 + static unsigned long wm8350_wdt_users; 29 + static struct miscdevice wm8350_wdt_miscdev; 30 + static int wm8350_wdt_expect_close; 31 + static DEFINE_MUTEX(wdt_mutex); 32 + 33 + static struct { 34 + int time; /* Seconds */ 35 + u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */ 36 + } wm8350_wdt_cfgs[] = { 37 + { 1, 0x02 }, 38 + { 2, 0x04 }, 39 + { 4, 0x05 }, 40 + }; 41 + 42 + static struct wm8350 *get_wm8350(void) 43 + { 44 + return dev_get_drvdata(wm8350_wdt_miscdev.parent); 45 + } 46 + 47 + static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value) 48 + { 49 + int ret; 50 + u16 reg; 51 + 52 + mutex_lock(&wdt_mutex); 53 + wm8350_reg_unlock(wm8350); 54 + 55 + reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 56 + reg &= ~WM8350_WDOG_TO_MASK; 57 + reg |= value; 58 + ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 59 + 60 + wm8350_reg_lock(wm8350); 61 + mutex_unlock(&wdt_mutex); 62 + 63 + return ret; 64 + } 65 + 66 + static int wm8350_wdt_start(struct wm8350 *wm8350) 67 + { 68 + int ret; 69 + u16 reg; 70 + 71 + mutex_lock(&wdt_mutex); 72 + wm8350_reg_unlock(wm8350); 73 + 74 + reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 75 + reg &= ~WM8350_WDOG_MODE_MASK; 76 + reg |= 0x20; 77 + ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 78 + 79 + wm8350_reg_lock(wm8350); 80 + mutex_unlock(&wdt_mutex); 81 + 82 + return ret; 83 + } 84 + 85 + static int wm8350_wdt_stop(struct wm8350 *wm8350) 86 + { 87 + int ret; 88 + u16 reg; 89 + 90 + mutex_lock(&wdt_mutex); 91 + wm8350_reg_unlock(wm8350); 92 + 93 + reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 94 + reg &= ~WM8350_WDOG_MODE_MASK; 95 + ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 96 + 97 + wm8350_reg_lock(wm8350); 98 + mutex_unlock(&wdt_mutex); 99 + 100 + return ret; 101 + } 102 + 103 + static int wm8350_wdt_kick(struct wm8350 *wm8350) 104 + { 105 + int ret; 106 + u16 reg; 107 + 108 + mutex_lock(&wdt_mutex); 109 + 110 + reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 111 + ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 112 + 113 + mutex_unlock(&wdt_mutex); 114 + 115 + return ret; 116 + } 117 + 118 + static int wm8350_wdt_open(struct inode *inode, struct file *file) 119 + { 120 + struct wm8350 *wm8350 = get_wm8350(); 121 + int ret; 122 + 123 + if (!wm8350) 124 + return -ENODEV; 125 + 126 + if (test_and_set_bit(0, &wm8350_wdt_users)) 127 + return -EBUSY; 128 + 129 + ret = wm8350_wdt_start(wm8350); 130 + if (ret != 0) 131 + return ret; 132 + 133 + return nonseekable_open(inode, file); 134 + } 135 + 136 + static int wm8350_wdt_release(struct inode *inode, struct file *file) 137 + { 138 + struct wm8350 *wm8350 = get_wm8350(); 139 + 140 + if (wm8350_wdt_expect_close) 141 + wm8350_wdt_stop(wm8350); 142 + else { 143 + dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n"); 144 + wm8350_wdt_kick(wm8350); 145 + } 146 + 147 + clear_bit(0, &wm8350_wdt_users); 148 + 149 + return 0; 150 + } 151 + 152 + static ssize_t wm8350_wdt_write(struct file *file, 153 + const char __user *data, size_t count, 154 + loff_t *ppos) 155 + { 156 + struct wm8350 *wm8350 = get_wm8350(); 157 + size_t i; 158 + 159 + if (count) { 160 + wm8350_wdt_kick(wm8350); 161 + 162 + if (!nowayout) { 163 + /* In case it was set long ago */ 164 + wm8350_wdt_expect_close = 0; 165 + 166 + /* scan to see whether or not we got the magic 167 + character */ 168 + for (i = 0; i != count; i++) { 169 + char c; 170 + if (get_user(c, data + i)) 171 + return -EFAULT; 172 + if (c == 'V') 173 + wm8350_wdt_expect_close = 42; 174 + } 175 + } 176 + } 177 + return count; 178 + } 179 + 180 + static struct watchdog_info ident = { 181 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 182 + .identity = "WM8350 Watchdog", 183 + }; 184 + 185 + static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd, 186 + unsigned long arg) 187 + { 188 + struct wm8350 *wm8350 = get_wm8350(); 189 + int ret = -ENOTTY, time, i; 190 + void __user *argp = (void __user *)arg; 191 + int __user *p = argp; 192 + u16 reg; 193 + 194 + switch (cmd) { 195 + case WDIOC_GETSUPPORT: 196 + ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 197 + break; 198 + 199 + case WDIOC_GETSTATUS: 200 + case WDIOC_GETBOOTSTATUS: 201 + ret = put_user(0, p); 202 + break; 203 + 204 + case WDIOC_SETOPTIONS: 205 + { 206 + int options; 207 + 208 + if (get_user(options, p)) 209 + return -EFAULT; 210 + 211 + ret = -EINVAL; 212 + 213 + /* Setting both simultaneously means at least one must fail */ 214 + if (options == WDIOS_DISABLECARD) 215 + ret = wm8350_wdt_start(wm8350); 216 + 217 + if (options == WDIOS_ENABLECARD) 218 + ret = wm8350_wdt_stop(wm8350); 219 + break; 220 + } 221 + 222 + case WDIOC_KEEPALIVE: 223 + ret = wm8350_wdt_kick(wm8350); 224 + break; 225 + 226 + case WDIOC_SETTIMEOUT: 227 + ret = get_user(time, p); 228 + if (ret) 229 + break; 230 + 231 + if (time == 0) { 232 + if (nowayout) 233 + ret = -EINVAL; 234 + else 235 + wm8350_wdt_stop(wm8350); 236 + break; 237 + } 238 + 239 + for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) 240 + if (wm8350_wdt_cfgs[i].time == time) 241 + break; 242 + if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) 243 + ret = -EINVAL; 244 + else 245 + ret = wm8350_wdt_set_timeout(wm8350, 246 + wm8350_wdt_cfgs[i].val); 247 + break; 248 + 249 + case WDIOC_GETTIMEOUT: 250 + reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 251 + reg &= WM8350_WDOG_TO_MASK; 252 + for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) 253 + if (wm8350_wdt_cfgs[i].val == reg) 254 + break; 255 + if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) { 256 + dev_warn(wm8350->dev, 257 + "Unknown watchdog configuration: %x\n", reg); 258 + ret = -EINVAL; 259 + } else 260 + ret = put_user(wm8350_wdt_cfgs[i].time, p); 261 + 262 + } 263 + 264 + return ret; 265 + } 266 + 267 + static const struct file_operations wm8350_wdt_fops = { 268 + .owner = THIS_MODULE, 269 + .llseek = no_llseek, 270 + .write = wm8350_wdt_write, 271 + .unlocked_ioctl = wm8350_wdt_ioctl, 272 + .open = wm8350_wdt_open, 273 + .release = wm8350_wdt_release, 274 + }; 275 + 276 + static struct miscdevice wm8350_wdt_miscdev = { 277 + .minor = WATCHDOG_MINOR, 278 + .name = "watchdog", 279 + .fops = &wm8350_wdt_fops, 280 + }; 281 + 282 + static int wm8350_wdt_probe(struct platform_device *pdev) 283 + { 284 + struct wm8350 *wm8350 = platform_get_drvdata(pdev); 285 + 286 + if (!wm8350) { 287 + dev_err(wm8350->dev, "No driver data supplied\n"); 288 + return -ENODEV; 289 + } 290 + 291 + /* Default to 4s timeout */ 292 + wm8350_wdt_set_timeout(wm8350, 0x05); 293 + 294 + wm8350_wdt_miscdev.parent = &pdev->dev; 295 + 296 + return misc_register(&wm8350_wdt_miscdev); 297 + } 298 + 299 + static int __exit wm8350_wdt_remove(struct platform_device *pdev) 300 + { 301 + misc_deregister(&wm8350_wdt_miscdev); 302 + 303 + return 0; 304 + } 305 + 306 + static struct platform_driver wm8350_wdt_driver = { 307 + .probe = wm8350_wdt_probe, 308 + .remove = wm8350_wdt_remove, 309 + .driver = { 310 + .name = "wm8350-wdt", 311 + }, 312 + }; 313 + 314 + static int __init wm8350_wdt_init(void) 315 + { 316 + return platform_driver_register(&wm8350_wdt_driver); 317 + } 318 + module_init(wm8350_wdt_init); 319 + 320 + static void __exit wm8350_wdt_exit(void) 321 + { 322 + platform_driver_unregister(&wm8350_wdt_driver); 323 + } 324 + module_exit(wm8350_wdt_exit); 325 + 326 + MODULE_AUTHOR("Mark Brown"); 327 + MODULE_DESCRIPTION("WM8350 Watchdog"); 328 + MODULE_LICENSE("GPL"); 329 + MODULE_ALIAS("platform:wm8350-wdt");