diff options
| author | Guenter Roeck <linux@roeck-us.net> | 2013-08-17 16:58:43 -0400 |
|---|---|---|
| committer | Wim Van Sebroeck <wim@iguana.be> | 2014-01-28 15:35:30 -0500 |
| commit | 962c04f54e4a3c322d19b47256f9aec0b9c8124e (patch) | |
| tree | 0dbd271080882c80fc2c6642743c99b4e5b55d65 | |
| parent | f72fa00f8ab216ee484d61de17ddc84712456c3a (diff) | |
watchdog: w83627hf: Auto-detect IO address and supported chips
Instead of requiring the user to provide an IO address per module
parameter, auto-detect it as well as supported chips.
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
| -rw-r--r-- | drivers/watchdog/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/watchdog/w83627hf_wdt.c | 182 |
2 files changed, 173 insertions, 24 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3fc5158ffecf..4fa8428612b2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -892,13 +892,20 @@ config VIA_WDT | |||
| 892 | Most people will say N. | 892 | Most people will say N. |
| 893 | 893 | ||
| 894 | config W83627HF_WDT | 894 | config W83627HF_WDT |
| 895 | tristate "W83627HF/W83627DHG Watchdog Timer" | 895 | tristate "Watchdog timer for W83627HF/W83627DHG and compatibles" |
| 896 | depends on X86 | 896 | depends on X86 |
| 897 | select WATCHDOG_CORE | 897 | select WATCHDOG_CORE |
| 898 | ---help--- | 898 | ---help--- |
| 899 | This is the driver for the hardware watchdog on the W83627HF chipset | 899 | This is the driver for the hardware watchdog on the following |
| 900 | as used in Advantech PC-9578 and Tyan S2721-533 motherboards | 900 | Super I/O chips. |
| 901 | (and likely others). The driver also supports the W83627DHG chip. | 901 | W83627DHG/DHG-P/EHF/EHG/F/G/HF/S/SF/THF/UHG/UG |
| 902 | W83637HF | ||
| 903 | W83667HG/HG-B | ||
| 904 | W83687THF | ||
| 905 | NCT6775 | ||
| 906 | NCT6776 | ||
| 907 | NCT6779 | ||
| 908 | |||
| 902 | This watchdog simply watches your kernel to make sure it doesn't | 909 | This watchdog simply watches your kernel to make sure it doesn't |
| 903 | freeze, and if it does, it reboots your computer after a certain | 910 | freeze, and if it does, it reboots your computer after a certain |
| 904 | amount of time. | 911 | amount of time. |
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index e24b21082874..954becdf9cf5 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
| @@ -44,10 +44,11 @@ | |||
| 44 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" | 44 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" |
| 45 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ | 45 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ |
| 46 | 46 | ||
| 47 | /* You must set this - there is no sane way to probe for this board. */ | 47 | static int wdt_io; |
| 48 | static int wdt_io = 0x2E; | 48 | |
| 49 | module_param(wdt_io, int, 0); | 49 | enum chips { w83627hf, w83627s, w83637hf, w83627thf, w83687thf, |
| 50 | MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)"); | 50 | w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83667hg_b, |
| 51 | nct6775, nct6776, nct6779 }; | ||
| 51 | 52 | ||
| 52 | static int timeout; /* in seconds */ | 53 | static int timeout; /* in seconds */ |
| 53 | module_param(timeout, int, 0); | 54 | module_param(timeout, int, 0); |
| @@ -72,6 +73,21 @@ MODULE_PARM_DESC(nowayout, | |||
| 72 | 73 | ||
| 73 | #define W83627HF_LD_WDT 0x08 | 74 | #define W83627HF_LD_WDT 0x08 |
| 74 | 75 | ||
| 76 | #define W83627HF_ID 0x52 | ||
| 77 | #define W83627S_ID 0x59 | ||
| 78 | #define W83637HF_ID 0x70 | ||
| 79 | #define W83627THF_ID 0x82 | ||
| 80 | #define W83687THF_ID 0x85 | ||
| 81 | #define W83627EHF_ID 0x88 | ||
| 82 | #define W83627DHG_ID 0xa0 | ||
| 83 | #define W83627UHG_ID 0xa2 | ||
| 84 | #define W83667HG_ID 0xa5 | ||
| 85 | #define W83627DHG_P_ID 0xb0 | ||
| 86 | #define W83667HG_B_ID 0xb3 | ||
| 87 | #define NCT6775_ID 0xb4 | ||
| 88 | #define NCT6776_ID 0xc3 | ||
| 89 | #define NCT6779_ID 0xc5 | ||
| 90 | |||
| 75 | static void superio_outb(int reg, int val) | 91 | static void superio_outb(int reg, int val) |
| 76 | { | 92 | { |
| 77 | outb(reg, WDT_EFER); | 93 | outb(reg, WDT_EFER); |
| @@ -106,10 +122,7 @@ static void superio_exit(void) | |||
| 106 | release_region(wdt_io, 2); | 122 | release_region(wdt_io, 2); |
| 107 | } | 123 | } |
| 108 | 124 | ||
| 109 | /* tyan motherboards seem to set F5 to 0x4C ? | 125 | static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) |
| 110 | * So explicitly init to appropriate value. */ | ||
| 111 | |||
| 112 | static int w83627hf_init(struct watchdog_device *wdog) | ||
| 113 | { | 126 | { |
| 114 | int ret; | 127 | int ret; |
| 115 | unsigned char t; | 128 | unsigned char t; |
| @@ -119,20 +132,59 @@ static int w83627hf_init(struct watchdog_device *wdog) | |||
| 119 | return ret; | 132 | return ret; |
| 120 | 133 | ||
| 121 | superio_select(W83627HF_LD_WDT); | 134 | superio_select(W83627HF_LD_WDT); |
| 122 | t = superio_inb(0x20); /* check chip version */ | ||
| 123 | if (t == 0x82) { /* W83627THF */ | ||
| 124 | t = (superio_inb(0x2b) & 0xf7); | ||
| 125 | superio_outb(0x2b, t | 0x04); /* set GPIO3 to WDT0 */ | ||
| 126 | } else if (t == 0x88 || t == 0xa0) { /* W83627EHF / W83627DHG */ | ||
| 127 | t = superio_inb(0x2d); | ||
| 128 | superio_outb(0x2d, t & ~0x01); /* set GPIO5 to WDT0 */ | ||
| 129 | } | ||
| 130 | 135 | ||
| 131 | /* set CR30 bit 0 to activate GPIO2 */ | 136 | /* set CR30 bit 0 to activate GPIO2 */ |
| 132 | t = superio_inb(0x30); | 137 | t = superio_inb(0x30); |
| 133 | if (!(t & 0x01)) | 138 | if (!(t & 0x01)) |
| 134 | superio_outb(0x30, t | 0x01); | 139 | superio_outb(0x30, t | 0x01); |
| 135 | 140 | ||
| 141 | switch (chip) { | ||
| 142 | case w83627hf: | ||
| 143 | case w83627s: | ||
| 144 | t = superio_inb(0x2B) & ~0x10; | ||
| 145 | superio_outb(0x2B, t); /* set GPIO24 to WDT0 */ | ||
| 146 | break; | ||
| 147 | case w83627thf: | ||
| 148 | t = (superio_inb(0x2B) & ~0x08) | 0x04; | ||
| 149 | superio_outb(0x2B, t); /* set GPIO3 to WDT0 */ | ||
| 150 | break; | ||
| 151 | case w83627dhg: | ||
| 152 | case w83627dhg_p: | ||
| 153 | t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */ | ||
| 154 | superio_outb(0x2D, t); /* set GPIO5 to WDT0 */ | ||
| 155 | t = superio_inb(0xF5); | ||
| 156 | t |= 0x02; /* enable the WDTO# output low pulse | ||
| 157 | * to the KBRST# pin */ | ||
| 158 | superio_outb(0xF5, t); | ||
| 159 | break; | ||
| 160 | case w83637hf: | ||
| 161 | break; | ||
| 162 | case w83687thf: | ||
| 163 | t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */ | ||
| 164 | superio_outb(0x2C, t); | ||
| 165 | break; | ||
| 166 | case w83627ehf: | ||
| 167 | case w83627uhg: | ||
| 168 | case w83667hg: | ||
| 169 | case w83667hg_b: | ||
| 170 | case nct6775: | ||
| 171 | case nct6776: | ||
| 172 | case nct6779: | ||
| 173 | /* | ||
| 174 | * These chips have a fixed WDTO# output pin (W83627UHG), | ||
| 175 | * or support more than one WDTO# output pin. | ||
| 176 | * Don't touch its configuration, and hope the BIOS | ||
| 177 | * does the right thing. | ||
| 178 | */ | ||
| 179 | t = superio_inb(0xF5); | ||
| 180 | t |= 0x02; /* enable the WDTO# output low pulse | ||
| 181 | * to the KBRST# pin */ | ||
| 182 | superio_outb(0xF5, t); | ||
| 183 | break; | ||
| 184 | default: | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | |||
| 136 | t = superio_inb(0xF6); | 188 | t = superio_inb(0xF6); |
| 137 | if (t != 0) { | 189 | if (t != 0) { |
| 138 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", | 190 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", |
| @@ -142,8 +194,6 @@ static int w83627hf_init(struct watchdog_device *wdog) | |||
| 142 | 194 | ||
| 143 | /* set second mode & disable keyboard turning off watchdog */ | 195 | /* set second mode & disable keyboard turning off watchdog */ |
| 144 | t = superio_inb(0xF5) & ~0x0C; | 196 | t = superio_inb(0xF5) & ~0x0C; |
| 145 | /* enable the WDTO# output low pulse to the KBRST# pin */ | ||
| 146 | t |= 0x02; | ||
| 147 | superio_outb(0xF5, t); | 197 | superio_outb(0xF5, t); |
| 148 | 198 | ||
| 149 | /* disable keyboard & mouse turning off watchdog */ | 199 | /* disable keyboard & mouse turning off watchdog */ |
| @@ -249,16 +299,108 @@ static struct notifier_block wdt_notifier = { | |||
| 249 | .notifier_call = wdt_notify_sys, | 299 | .notifier_call = wdt_notify_sys, |
| 250 | }; | 300 | }; |
| 251 | 301 | ||
| 302 | static int wdt_find(int addr) | ||
| 303 | { | ||
| 304 | u8 val; | ||
| 305 | int ret; | ||
| 306 | |||
| 307 | ret = superio_enter(); | ||
| 308 | if (ret) | ||
| 309 | return ret; | ||
| 310 | superio_select(W83627HF_LD_WDT); | ||
| 311 | val = superio_inb(0x20); | ||
| 312 | switch (val) { | ||
| 313 | case W83627HF_ID: | ||
| 314 | ret = w83627hf; | ||
| 315 | break; | ||
| 316 | case W83627S_ID: | ||
| 317 | ret = w83627s; | ||
| 318 | break; | ||
| 319 | case W83637HF_ID: | ||
| 320 | ret = w83637hf; | ||
| 321 | break; | ||
| 322 | case W83627THF_ID: | ||
| 323 | ret = w83627thf; | ||
| 324 | break; | ||
| 325 | case W83687THF_ID: | ||
| 326 | ret = w83687thf; | ||
| 327 | break; | ||
| 328 | case W83627EHF_ID: | ||
| 329 | ret = w83627ehf; | ||
| 330 | break; | ||
| 331 | case W83627DHG_ID: | ||
| 332 | ret = w83627dhg; | ||
| 333 | break; | ||
| 334 | case W83627DHG_P_ID: | ||
| 335 | ret = w83627dhg_p; | ||
| 336 | break; | ||
| 337 | case W83627UHG_ID: | ||
| 338 | ret = w83627uhg; | ||
| 339 | break; | ||
| 340 | case W83667HG_ID: | ||
| 341 | ret = w83667hg; | ||
| 342 | break; | ||
| 343 | case W83667HG_B_ID: | ||
| 344 | ret = w83667hg_b; | ||
| 345 | break; | ||
| 346 | case NCT6775_ID: | ||
| 347 | ret = nct6775; | ||
| 348 | break; | ||
| 349 | case NCT6776_ID: | ||
| 350 | ret = nct6776; | ||
| 351 | break; | ||
| 352 | case NCT6779_ID: | ||
| 353 | ret = nct6779; | ||
| 354 | break; | ||
| 355 | case 0xff: | ||
| 356 | ret = -ENODEV; | ||
| 357 | break; | ||
| 358 | default: | ||
| 359 | ret = -ENODEV; | ||
| 360 | pr_err("Unsupported chip ID: 0x%02x\n", val); | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | superio_exit(); | ||
| 364 | return ret; | ||
| 365 | } | ||
| 366 | |||
| 252 | static int __init wdt_init(void) | 367 | static int __init wdt_init(void) |
| 253 | { | 368 | { |
| 254 | int ret; | 369 | int ret; |
| 370 | int chip; | ||
| 371 | const char * const chip_name[] = { | ||
| 372 | "W83627HF", | ||
| 373 | "W83627S", | ||
| 374 | "W83637HF", | ||
| 375 | "W83627THF", | ||
| 376 | "W83687THF", | ||
| 377 | "W83627EHF", | ||
| 378 | "W83627DHG", | ||
| 379 | "W83627UHG", | ||
| 380 | "W83667HG", | ||
| 381 | "W83667DHG-P", | ||
| 382 | "W83667HG-B", | ||
| 383 | "NCT6775", | ||
| 384 | "NCT6776", | ||
| 385 | "NCT6779", | ||
| 386 | }; | ||
| 387 | |||
| 388 | wdt_io = 0x2e; | ||
| 389 | chip = wdt_find(0x2e); | ||
| 390 | if (chip < 0) { | ||
| 391 | wdt_io = 0x4e; | ||
| 392 | chip = wdt_find(0x4e); | ||
| 393 | if (chip < 0) | ||
| 394 | return chip; | ||
| 395 | } | ||
| 255 | 396 | ||
| 256 | pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); | 397 | pr_info("WDT driver for %s Super I/O chip initialising\n", |
| 398 | chip_name[chip]); | ||
| 257 | 399 | ||
| 258 | watchdog_init_timeout(&wdt_dev, timeout, NULL); | 400 | watchdog_init_timeout(&wdt_dev, timeout, NULL); |
| 259 | watchdog_set_nowayout(&wdt_dev, nowayout); | 401 | watchdog_set_nowayout(&wdt_dev, nowayout); |
| 260 | 402 | ||
| 261 | ret = w83627hf_init(&wdt_dev); | 403 | ret = w83627hf_init(&wdt_dev, chip); |
| 262 | if (ret) { | 404 | if (ret) { |
| 263 | pr_err("failed to initialize watchdog (err=%d)\n", ret); | 405 | pr_err("failed to initialize watchdog (err=%d)\n", ret); |
| 264 | return ret; | 406 | return ret; |
