1 /* 2 * w83627hf/thf WDT driver 3 * 4 * (c) Copyright 2013 Guenter Roeck 5 * converted to watchdog infrastructure 6 * 7 * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> 8 * added support for W83627THF. 9 * 10 * (c) Copyright 2003,2007 Pádraig Brady <P@draigBrady.com> 11 * 12 * Based on advantechwdt.c which is based on wdt.c. 13 * Original copyright messages: 14 * 15 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> 16 * 17 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 18 * All Rights Reserved. 19 * 20 * This program is free software; you can redistribute it and/or 21 * modify it under the terms of the GNU General Public License 22 * as published by the Free Software Foundation; either version 23 * 2 of the License, or (at your option) any later version. 24 * 25 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 26 * warranty for any of this software. This material is provided 27 * "AS-IS" and at no charge. 28 * 29 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> 30 */ 31 32 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 33 34 #include <linux/module.h> 35 #include <linux/moduleparam.h> 36 #include <linux/types.h> 37 #include <linux/watchdog.h> 38 #include <linux/ioport.h> 39 #include <linux/init.h> 40 #include <linux/io.h> 41 42 #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" 43 #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ 44 45 static int wdt_io; 46 static int cr_wdt_timeout; /* WDT timeout register */ 47 static int cr_wdt_control; /* WDT control register */ 48 static int cr_wdt_csr; /* WDT control & status register */ 49 50 enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, 51 w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, 52 w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, 53 nct6795, nct6796, nct6102 }; 54 55 static int timeout; /* in seconds */ 56 module_param(timeout, int, 0); 57 MODULE_PARM_DESC(timeout, 58 "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" 59 __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 60 61 static bool nowayout = WATCHDOG_NOWAYOUT; 62 module_param(nowayout, bool, 0); 63 MODULE_PARM_DESC(nowayout, 64 "Watchdog cannot be stopped once started (default=" 65 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 66 67 static int early_disable; 68 module_param(early_disable, int, 0); 69 MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); 70 71 /* 72 * Kernel methods. 73 */ 74 75 #define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ 76 #define WDT_EFIR (wdt_io+0) /* Extended Function Index Register 77 (same as EFER) */ 78 #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ 79 80 #define W83627HF_LD_WDT 0x08 81 82 #define W83627HF_ID 0x52 83 #define W83627S_ID 0x59 84 #define W83697HF_ID 0x60 85 #define W83697UG_ID 0x68 86 #define W83637HF_ID 0x70 87 #define W83627THF_ID 0x82 88 #define W83687THF_ID 0x85 89 #define W83627EHF_ID 0x88 90 #define W83627DHG_ID 0xa0 91 #define W83627UHG_ID 0xa2 92 #define W83667HG_ID 0xa5 93 #define W83627DHG_P_ID 0xb0 94 #define W83667HG_B_ID 0xb3 95 #define NCT6775_ID 0xb4 96 #define NCT6776_ID 0xc3 97 #define NCT6102_ID 0xc4 98 #define NCT6779_ID 0xc5 99 #define NCT6791_ID 0xc8 100 #define NCT6792_ID 0xc9 101 #define NCT6793_ID 0xd1 102 #define NCT6795_ID 0xd3 103 #define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */ 104 105 #define W83627HF_WDT_TIMEOUT 0xf6 106 #define W83697HF_WDT_TIMEOUT 0xf4 107 #define NCT6102D_WDT_TIMEOUT 0xf1 108 109 #define W83627HF_WDT_CONTROL 0xf5 110 #define W83697HF_WDT_CONTROL 0xf3 111 #define NCT6102D_WDT_CONTROL 0xf0 112 113 #define W836X7HF_WDT_CSR 0xf7 114 #define NCT6102D_WDT_CSR 0xf2 115 116 static void superio_outb(int reg, int val) 117 { 118 outb(reg, WDT_EFER); 119 outb(val, WDT_EFDR); 120 } 121 122 static inline int superio_inb(int reg) 123 { 124 outb(reg, WDT_EFER); 125 return inb(WDT_EFDR); 126 } 127 128 static int superio_enter(void) 129 { 130 if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) 131 return -EBUSY; 132 133 outb_p(0x87, WDT_EFER); /* Enter extended function mode */ 134 outb_p(0x87, WDT_EFER); /* Again according to manual */ 135 136 return 0; 137 } 138 139 static void superio_select(int ld) 140 { 141 superio_outb(0x07, ld); 142 } 143 144 static void superio_exit(void) 145 { 146 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ 147 release_region(wdt_io, 2); 148 } 149 150 static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) 151 { 152 int ret; 153 unsigned char t; 154 155 ret = superio_enter(); 156 if (ret) 157 return ret; 158 159 superio_select(W83627HF_LD_WDT); 160 161 /* set CR30 bit 0 to activate GPIO2 */ 162 t = superio_inb(0x30); 163 if (!(t & 0x01)) 164 superio_outb(0x30, t | 0x01); 165 166 switch (chip) { 167 case w83627hf: 168 case w83627s: 169 t = superio_inb(0x2B) & ~0x10; 170 superio_outb(0x2B, t); /* set GPIO24 to WDT0 */ 171 break; 172 case w83697hf: 173 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ 174 t = superio_inb(0x29) & ~0x60; 175 t |= 0x20; 176 superio_outb(0x29, t); 177 break; 178 case w83697ug: 179 /* Set pin 118 to WDTO# mode */ 180 t = superio_inb(0x2b) & ~0x04; 181 superio_outb(0x2b, t); 182 break; 183 case w83627thf: 184 t = (superio_inb(0x2B) & ~0x08) | 0x04; 185 superio_outb(0x2B, t); /* set GPIO3 to WDT0 */ 186 break; 187 case w83627dhg: 188 case w83627dhg_p: 189 t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */ 190 superio_outb(0x2D, t); /* set GPIO5 to WDT0 */ 191 t = superio_inb(cr_wdt_control); 192 t |= 0x02; /* enable the WDTO# output low pulse 193 * to the KBRST# pin */ 194 superio_outb(cr_wdt_control, t); 195 break; 196 case w83637hf: 197 break; 198 case w83687thf: 199 t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */ 200 superio_outb(0x2C, t); 201 break; 202 case w83627ehf: 203 case w83627uhg: 204 case w83667hg: 205 case w83667hg_b: 206 case nct6775: 207 case nct6776: 208 case nct6779: 209 case nct6791: 210 case nct6792: 211 case nct6793: 212 case nct6795: 213 case nct6796: 214 case nct6102: 215 /* 216 * These chips have a fixed WDTO# output pin (W83627UHG), 217 * or support more than one WDTO# output pin. 218 * Don't touch its configuration, and hope the BIOS 219 * does the right thing. 220 */ 221 t = superio_inb(cr_wdt_control); 222 t |= 0x02; /* enable the WDTO# output low pulse 223 * to the KBRST# pin */ 224 superio_outb(cr_wdt_control, t); 225 break; 226 default: 227 break; 228 } 229 230 t = superio_inb(cr_wdt_timeout); 231 if (t != 0) { 232 if (early_disable) { 233 pr_warn("Stopping previously enabled watchdog until userland kicks in\n"); 234 superio_outb(cr_wdt_timeout, 0); 235 } else { 236 pr_info("Watchdog already running. Resetting timeout to %d sec\n", 237 wdog->timeout); 238 superio_outb(cr_wdt_timeout, wdog->timeout); 239 } 240 } 241 242 /* set second mode & disable keyboard turning off watchdog */ 243 t = superio_inb(cr_wdt_control) & ~0x0C; 244 superio_outb(cr_wdt_control, t); 245 246 /* reset trigger, disable keyboard & mouse turning off watchdog */ 247 t = superio_inb(cr_wdt_csr) & ~0xD0; 248 superio_outb(cr_wdt_csr, t); 249 250 superio_exit(); 251 252 return 0; 253 } 254 255 static int wdt_set_time(unsigned int timeout) 256 { 257 int ret; 258 259 ret = superio_enter(); 260 if (ret) 261 return ret; 262 263 superio_select(W83627HF_LD_WDT); 264 superio_outb(cr_wdt_timeout, timeout); 265 superio_exit(); 266 267 return 0; 268 } 269 270 static int wdt_start(struct watchdog_device *wdog) 271 { 272 return wdt_set_time(wdog->timeout); 273 } 274 275 static int wdt_stop(struct watchdog_device *wdog) 276 { 277 return wdt_set_time(0); 278 } 279 280 static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) 281 { 282 wdog->timeout = timeout; 283 284 return 0; 285 } 286 287 static unsigned int wdt_get_time(struct watchdog_device *wdog) 288 { 289 unsigned int timeleft; 290 int ret; 291 292 ret = superio_enter(); 293 if (ret) 294 return 0; 295 296 superio_select(W83627HF_LD_WDT); 297 timeleft = superio_inb(cr_wdt_timeout); 298 superio_exit(); 299 300 return timeleft; 301 } 302 303 /* 304 * Kernel Interfaces 305 */ 306 307 static const struct watchdog_info wdt_info = { 308 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 309 .identity = "W83627HF Watchdog", 310 }; 311 312 static const struct watchdog_ops wdt_ops = { 313 .owner = THIS_MODULE, 314 .start = wdt_start, 315 .stop = wdt_stop, 316 .set_timeout = wdt_set_timeout, 317 .get_timeleft = wdt_get_time, 318 }; 319 320 static struct watchdog_device wdt_dev = { 321 .info = &wdt_info, 322 .ops = &wdt_ops, 323 .timeout = WATCHDOG_TIMEOUT, 324 .min_timeout = 1, 325 .max_timeout = 255, 326 }; 327 328 /* 329 * The WDT needs to learn about soft shutdowns in order to 330 * turn the timebomb registers off. 331 */ 332 333 static int wdt_find(int addr) 334 { 335 u8 val; 336 int ret; 337 338 cr_wdt_timeout = W83627HF_WDT_TIMEOUT; 339 cr_wdt_control = W83627HF_WDT_CONTROL; 340 cr_wdt_csr = W836X7HF_WDT_CSR; 341 342 ret = superio_enter(); 343 if (ret) 344 return ret; 345 superio_select(W83627HF_LD_WDT); 346 val = superio_inb(0x20); 347 switch (val) { 348 case W83627HF_ID: 349 ret = w83627hf; 350 break; 351 case W83627S_ID: 352 ret = w83627s; 353 break; 354 case W83697HF_ID: 355 ret = w83697hf; 356 cr_wdt_timeout = W83697HF_WDT_TIMEOUT; 357 cr_wdt_control = W83697HF_WDT_CONTROL; 358 break; 359 case W83697UG_ID: 360 ret = w83697ug; 361 cr_wdt_timeout = W83697HF_WDT_TIMEOUT; 362 cr_wdt_control = W83697HF_WDT_CONTROL; 363 break; 364 case W83637HF_ID: 365 ret = w83637hf; 366 break; 367 case W83627THF_ID: 368 ret = w83627thf; 369 break; 370 case W83687THF_ID: 371 ret = w83687thf; 372 break; 373 case W83627EHF_ID: 374 ret = w83627ehf; 375 break; 376 case W83627DHG_ID: 377 ret = w83627dhg; 378 break; 379 case W83627DHG_P_ID: 380 ret = w83627dhg_p; 381 break; 382 case W83627UHG_ID: 383 ret = w83627uhg; 384 break; 385 case W83667HG_ID: 386 ret = w83667hg; 387 break; 388 case W83667HG_B_ID: 389 ret = w83667hg_b; 390 break; 391 case NCT6775_ID: 392 ret = nct6775; 393 break; 394 case NCT6776_ID: 395 ret = nct6776; 396 break; 397 case NCT6779_ID: 398 ret = nct6779; 399 break; 400 case NCT6791_ID: 401 ret = nct6791; 402 break; 403 case NCT6792_ID: 404 ret = nct6792; 405 break; 406 case NCT6793_ID: 407 ret = nct6793; 408 break; 409 case NCT6795_ID: 410 ret = nct6795; 411 break; 412 case NCT6796_ID: 413 ret = nct6796; 414 break; 415 case NCT6102_ID: 416 ret = nct6102; 417 cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; 418 cr_wdt_control = NCT6102D_WDT_CONTROL; 419 cr_wdt_csr = NCT6102D_WDT_CSR; 420 break; 421 case 0xff: 422 ret = -ENODEV; 423 break; 424 default: 425 ret = -ENODEV; 426 pr_err("Unsupported chip ID: 0x%02x\n", val); 427 break; 428 } 429 superio_exit(); 430 return ret; 431 } 432 433 static int __init wdt_init(void) 434 { 435 int ret; 436 int chip; 437 static const char * const chip_name[] = { 438 "W83627HF", 439 "W83627S", 440 "W83697HF", 441 "W83697UG", 442 "W83637HF", 443 "W83627THF", 444 "W83687THF", 445 "W83627EHF", 446 "W83627DHG", 447 "W83627UHG", 448 "W83667HG", 449 "W83667DHG-P", 450 "W83667HG-B", 451 "NCT6775", 452 "NCT6776", 453 "NCT6779", 454 "NCT6791", 455 "NCT6792", 456 "NCT6793", 457 "NCT6795", 458 "NCT6796", 459 "NCT6102", 460 }; 461 462 wdt_io = 0x2e; 463 chip = wdt_find(0x2e); 464 if (chip < 0) { 465 wdt_io = 0x4e; 466 chip = wdt_find(0x4e); 467 if (chip < 0) 468 return chip; 469 } 470 471 pr_info("WDT driver for %s Super I/O chip initialising\n", 472 chip_name[chip]); 473 474 watchdog_init_timeout(&wdt_dev, timeout, NULL); 475 watchdog_set_nowayout(&wdt_dev, nowayout); 476 watchdog_stop_on_reboot(&wdt_dev); 477 478 ret = w83627hf_init(&wdt_dev, chip); 479 if (ret) { 480 pr_err("failed to initialize watchdog (err=%d)\n", ret); 481 return ret; 482 } 483 484 ret = watchdog_register_device(&wdt_dev); 485 if (ret) 486 return ret; 487 488 pr_info("initialized. timeout=%d sec (nowayout=%d)\n", 489 wdt_dev.timeout, nowayout); 490 491 return ret; 492 } 493 494 static void __exit wdt_exit(void) 495 { 496 watchdog_unregister_device(&wdt_dev); 497 } 498 499 module_init(wdt_init); 500 module_exit(wdt_exit); 501 502 MODULE_LICENSE("GPL"); 503 MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>"); 504 MODULE_DESCRIPTION("w83627hf/thf WDT driver"); 505