550 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * w83627hf/thf WDT driver4 *5 * (c) Copyright 2013 Guenter Roeck6 * converted to watchdog infrastructure7 *8 * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>9 * added support for W83627THF.10 *11 * (c) Copyright 2003,2007 Pádraig Brady <P@draigBrady.com>12 *13 * Based on advantechwdt.c which is based on wdt.c.14 * Original copyright messages:15 *16 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>17 *18 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,19 * All Rights Reserved.20 *21 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide22 * warranty for any of this software. This material is provided23 * "AS-IS" and at no charge.24 *25 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>26 */27 28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt29 30#include <linux/module.h>31#include <linux/moduleparam.h>32#include <linux/types.h>33#include <linux/watchdog.h>34#include <linux/ioport.h>35#include <linux/init.h>36#include <linux/io.h>37#include <linux/dmi.h>38 39#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"40#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */41 42static int wdt_io;43static int cr_wdt_timeout; /* WDT timeout register */44static int cr_wdt_control; /* WDT control register */45static int cr_wdt_csr; /* WDT control & status register */46static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */47static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */48 49enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,50 w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,51 w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,52 nct6795, nct6796, nct6102, nct6116 };53 54static int timeout; /* in seconds */55module_param(timeout, int, 0);56MODULE_PARM_DESC(timeout,57 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="58 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");59 60static bool nowayout = WATCHDOG_NOWAYOUT;61module_param(nowayout, bool, 0);62MODULE_PARM_DESC(nowayout,63 "Watchdog cannot be stopped once started (default="64 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");65 66static int early_disable;67module_param(early_disable, int, 0);68MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");69 70/*71 * Kernel methods.72 */73 74#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */75#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register76 (same as EFER) */77#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */78 79#define W83627HF_LD_WDT 0x0880 81#define W83627HF_ID 0x5282#define W83627S_ID 0x5983#define W83697HF_ID 0x6084#define W83697UG_ID 0x6885#define W83637HF_ID 0x7086#define W83627THF_ID 0x8287#define W83687THF_ID 0x8588#define W83627EHF_ID 0x8889#define W83627DHG_ID 0xa090#define W83627UHG_ID 0xa291#define W83667HG_ID 0xa592#define W83627DHG_P_ID 0xb093#define W83667HG_B_ID 0xb394#define NCT6775_ID 0xb495#define NCT6776_ID 0xc396#define NCT6102_ID 0xc497#define NCT6116_ID 0xd298#define NCT6779_ID 0xc599#define NCT6791_ID 0xc8100#define NCT6792_ID 0xc9101#define NCT6793_ID 0xd1102#define NCT6795_ID 0xd3103#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */104 105#define W83627HF_WDT_TIMEOUT 0xf6106#define W83697HF_WDT_TIMEOUT 0xf4107#define NCT6102D_WDT_TIMEOUT 0xf1108 109#define W83627HF_WDT_CONTROL 0xf5110#define W83697HF_WDT_CONTROL 0xf3111#define NCT6102D_WDT_CONTROL 0xf0112 113#define W836X7HF_WDT_CSR 0xf7114#define NCT6102D_WDT_CSR 0xf2115 116#define WDT_CSR_STATUS 0x10117#define WDT_CSR_KBD 0x40118#define WDT_CSR_MOUSE 0x80119 120static void superio_outb(int reg, int val)121{122 outb(reg, WDT_EFER);123 outb(val, WDT_EFDR);124}125 126static inline int superio_inb(int reg)127{128 outb(reg, WDT_EFER);129 return inb(WDT_EFDR);130}131 132static int superio_enter(void)133{134 if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))135 return -EBUSY;136 137 outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */138 outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */139 140 return 0;141}142 143static void superio_select(int ld)144{145 superio_outb(0x07, ld);146}147 148static void superio_exit(void)149{150 outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */151 release_region(wdt_io, 2);152}153 154static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)155{156 int ret;157 unsigned char t;158 159 ret = superio_enter();160 if (ret)161 return ret;162 163 superio_select(W83627HF_LD_WDT);164 165 /* set CR30 bit 0 to activate GPIO2 */166 t = superio_inb(0x30);167 if (!(t & 0x01))168 superio_outb(0x30, t | 0x01);169 170 switch (chip) {171 case w83627hf:172 case w83627s:173 t = superio_inb(0x2B) & ~0x10;174 superio_outb(0x2B, t); /* set GPIO24 to WDT0 */175 break;176 case w83697hf:177 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */178 t = superio_inb(0x29) & ~0x60;179 t |= 0x20;180 superio_outb(0x29, t);181 break;182 case w83697ug:183 /* Set pin 118 to WDTO# mode */184 t = superio_inb(0x2b) & ~0x04;185 superio_outb(0x2b, t);186 break;187 case w83627thf:188 t = (superio_inb(0x2B) & ~0x08) | 0x04;189 superio_outb(0x2B, t); /* set GPIO3 to WDT0 */190 break;191 case w83627dhg:192 case w83627dhg_p:193 t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */194 superio_outb(0x2D, t); /* set GPIO5 to WDT0 */195 t = superio_inb(cr_wdt_control);196 t |= 0x02; /* enable the WDTO# output low pulse197 * to the KBRST# pin */198 superio_outb(cr_wdt_control, t);199 break;200 case w83637hf:201 break;202 case w83687thf:203 t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */204 superio_outb(0x2C, t);205 break;206 case w83627ehf:207 case w83627uhg:208 case w83667hg:209 case w83667hg_b:210 case nct6775:211 case nct6776:212 case nct6779:213 case nct6791:214 case nct6792:215 case nct6793:216 case nct6795:217 case nct6796:218 case nct6102:219 case nct6116:220 /*221 * These chips have a fixed WDTO# output pin (W83627UHG),222 * or support more than one WDTO# output pin.223 * Don't touch its configuration, and hope the BIOS224 * does the right thing.225 */226 t = superio_inb(cr_wdt_control);227 t |= 0x02; /* enable the WDTO# output low pulse228 * to the KBRST# pin */229 superio_outb(cr_wdt_control, t);230 break;231 default:232 break;233 }234 235 t = superio_inb(cr_wdt_timeout);236 if (t != 0) {237 if (early_disable) {238 pr_warn("Stopping previously enabled watchdog until userland kicks in\n");239 superio_outb(cr_wdt_timeout, 0);240 } else {241 pr_info("Watchdog already running. Resetting timeout to %d sec\n",242 wdog->timeout);243 superio_outb(cr_wdt_timeout, wdog->timeout);244 }245 }246 247 /* set second mode & disable keyboard turning off watchdog */248 t = superio_inb(cr_wdt_control) & ~0x0C;249 superio_outb(cr_wdt_control, t);250 251 t = superio_inb(cr_wdt_csr);252 if (t & WDT_CSR_STATUS)253 wdog->bootstatus |= WDIOF_CARDRESET;254 255 /* reset status, disable keyboard & mouse turning off watchdog */256 t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);257 superio_outb(cr_wdt_csr, t);258 259 superio_exit();260 261 return 0;262}263 264static int wdt_set_time(unsigned int timeout)265{266 int ret;267 268 ret = superio_enter();269 if (ret)270 return ret;271 272 superio_select(W83627HF_LD_WDT);273 superio_outb(cr_wdt_timeout, timeout);274 superio_exit();275 276 return 0;277}278 279static int wdt_start(struct watchdog_device *wdog)280{281 return wdt_set_time(wdog->timeout);282}283 284static int wdt_stop(struct watchdog_device *wdog)285{286 return wdt_set_time(0);287}288 289static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)290{291 wdog->timeout = timeout;292 293 return 0;294}295 296static unsigned int wdt_get_time(struct watchdog_device *wdog)297{298 unsigned int timeleft;299 int ret;300 301 ret = superio_enter();302 if (ret)303 return 0;304 305 superio_select(W83627HF_LD_WDT);306 timeleft = superio_inb(cr_wdt_timeout);307 superio_exit();308 309 return timeleft;310}311 312/*313 * Kernel Interfaces314 */315 316static const struct watchdog_info wdt_info = {317 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,318 .identity = "W83627HF Watchdog",319};320 321static const struct watchdog_ops wdt_ops = {322 .owner = THIS_MODULE,323 .start = wdt_start,324 .stop = wdt_stop,325 .set_timeout = wdt_set_timeout,326 .get_timeleft = wdt_get_time,327};328 329static struct watchdog_device wdt_dev = {330 .info = &wdt_info,331 .ops = &wdt_ops,332 .timeout = WATCHDOG_TIMEOUT,333 .min_timeout = 1,334 .max_timeout = 255,335};336 337/*338 * The WDT needs to learn about soft shutdowns in order to339 * turn the timebomb registers off.340 */341 342static int wdt_find(int addr)343{344 u8 val;345 int ret;346 347 cr_wdt_timeout = W83627HF_WDT_TIMEOUT;348 cr_wdt_control = W83627HF_WDT_CONTROL;349 cr_wdt_csr = W836X7HF_WDT_CSR;350 351 ret = superio_enter();352 if (ret)353 return ret;354 superio_select(W83627HF_LD_WDT);355 val = superio_inb(0x20);356 switch (val) {357 case W83627HF_ID:358 ret = w83627hf;359 break;360 case W83627S_ID:361 ret = w83627s;362 break;363 case W83697HF_ID:364 ret = w83697hf;365 cr_wdt_timeout = W83697HF_WDT_TIMEOUT;366 cr_wdt_control = W83697HF_WDT_CONTROL;367 break;368 case W83697UG_ID:369 ret = w83697ug;370 cr_wdt_timeout = W83697HF_WDT_TIMEOUT;371 cr_wdt_control = W83697HF_WDT_CONTROL;372 break;373 case W83637HF_ID:374 ret = w83637hf;375 break;376 case W83627THF_ID:377 ret = w83627thf;378 break;379 case W83687THF_ID:380 ret = w83687thf;381 break;382 case W83627EHF_ID:383 ret = w83627ehf;384 break;385 case W83627DHG_ID:386 ret = w83627dhg;387 break;388 case W83627DHG_P_ID:389 ret = w83627dhg_p;390 break;391 case W83627UHG_ID:392 ret = w83627uhg;393 break;394 case W83667HG_ID:395 ret = w83667hg;396 break;397 case W83667HG_B_ID:398 ret = w83667hg_b;399 break;400 case NCT6775_ID:401 ret = nct6775;402 break;403 case NCT6776_ID:404 ret = nct6776;405 break;406 case NCT6779_ID:407 ret = nct6779;408 break;409 case NCT6791_ID:410 ret = nct6791;411 break;412 case NCT6792_ID:413 ret = nct6792;414 break;415 case NCT6793_ID:416 ret = nct6793;417 break;418 case NCT6795_ID:419 ret = nct6795;420 break;421 case NCT6796_ID:422 ret = nct6796;423 break;424 case NCT6102_ID:425 ret = nct6102;426 cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;427 cr_wdt_control = NCT6102D_WDT_CONTROL;428 cr_wdt_csr = NCT6102D_WDT_CSR;429 break;430 case NCT6116_ID:431 ret = nct6116;432 cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;433 cr_wdt_control = NCT6102D_WDT_CONTROL;434 cr_wdt_csr = NCT6102D_WDT_CSR;435 break;436 case 0xff:437 ret = -ENODEV;438 break;439 default:440 ret = -ENODEV;441 pr_err("Unsupported chip ID: 0x%02x\n", val);442 break;443 }444 superio_exit();445 return ret;446}447 448/*449 * On some systems, the NCT6791D comes with a companion chip and the450 * watchdog function is in this companion chip. We must use a different451 * unlocking sequence to access the companion chip.452 */453static int __init wdt_use_alt_key(const struct dmi_system_id *d)454{455 wdt_cfg_enter = 0x88;456 wdt_cfg_leave = 0xBB;457 458 return 0;459}460 461static const struct dmi_system_id wdt_dmi_table[] __initconst = {462 {463 .matches = {464 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "INVES"),465 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CTS"),466 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "INVES"),467 DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"),468 },469 .callback = wdt_use_alt_key,470 },471 {}472};473 474static int __init wdt_init(void)475{476 int ret;477 int chip;478 static const char * const chip_name[] = {479 "W83627HF",480 "W83627S",481 "W83697HF",482 "W83697UG",483 "W83637HF",484 "W83627THF",485 "W83687THF",486 "W83627EHF",487 "W83627DHG",488 "W83627UHG",489 "W83667HG",490 "W83667DHG-P",491 "W83667HG-B",492 "NCT6775",493 "NCT6776",494 "NCT6779",495 "NCT6791",496 "NCT6792",497 "NCT6793",498 "NCT6795",499 "NCT6796",500 "NCT6102",501 "NCT6116",502 };503 504 /* Apply system-specific quirks */505 dmi_check_system(wdt_dmi_table);506 507 wdt_io = 0x2e;508 chip = wdt_find(0x2e);509 if (chip < 0) {510 wdt_io = 0x4e;511 chip = wdt_find(0x4e);512 if (chip < 0)513 return chip;514 }515 516 pr_info("WDT driver for %s Super I/O chip initialising\n",517 chip_name[chip]);518 519 watchdog_init_timeout(&wdt_dev, timeout, NULL);520 watchdog_set_nowayout(&wdt_dev, nowayout);521 watchdog_stop_on_reboot(&wdt_dev);522 523 ret = w83627hf_init(&wdt_dev, chip);524 if (ret) {525 pr_err("failed to initialize watchdog (err=%d)\n", ret);526 return ret;527 }528 529 ret = watchdog_register_device(&wdt_dev);530 if (ret)531 return ret;532 533 pr_info("initialized. timeout=%d sec (nowayout=%d)\n",534 wdt_dev.timeout, nowayout);535 536 return ret;537}538 539static void __exit wdt_exit(void)540{541 watchdog_unregister_device(&wdt_dev);542}543 544module_init(wdt_init);545module_exit(wdt_exit);546 547MODULE_LICENSE("GPL");548MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>");549MODULE_DESCRIPTION("w83627hf/thf WDT driver");550