| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * w83627hf/thf WDT driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 17 | 18 | * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, |
|---|
| 18 | 19 | * All Rights Reserved. |
|---|
| 19 | 20 | * |
|---|
| 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 | 21 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide |
|---|
| 26 | 22 | * warranty for any of this software. This material is provided |
|---|
| 27 | 23 | * "AS-IS" and at no charge. |
|---|
| .. | .. |
|---|
| 38 | 34 | #include <linux/ioport.h> |
|---|
| 39 | 35 | #include <linux/init.h> |
|---|
| 40 | 36 | #include <linux/io.h> |
|---|
| 37 | +#include <linux/dmi.h> |
|---|
| 41 | 38 | |
|---|
| 42 | 39 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" |
|---|
| 43 | 40 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ |
|---|
| .. | .. |
|---|
| 46 | 43 | static int cr_wdt_timeout; /* WDT timeout register */ |
|---|
| 47 | 44 | static int cr_wdt_control; /* WDT control register */ |
|---|
| 48 | 45 | static int cr_wdt_csr; /* WDT control & status register */ |
|---|
| 46 | +static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */ |
|---|
| 47 | +static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ |
|---|
| 49 | 48 | |
|---|
| 50 | 49 | enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, |
|---|
| 51 | 50 | w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, |
|---|
| 52 | 51 | w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, |
|---|
| 53 | | - nct6795, nct6796, nct6102 }; |
|---|
| 52 | + nct6795, nct6796, nct6102, nct6116 }; |
|---|
| 54 | 53 | |
|---|
| 55 | 54 | static int timeout; /* in seconds */ |
|---|
| 56 | 55 | module_param(timeout, int, 0); |
|---|
| .. | .. |
|---|
| 95 | 94 | #define NCT6775_ID 0xb4 |
|---|
| 96 | 95 | #define NCT6776_ID 0xc3 |
|---|
| 97 | 96 | #define NCT6102_ID 0xc4 |
|---|
| 97 | +#define NCT6116_ID 0xd2 |
|---|
| 98 | 98 | #define NCT6779_ID 0xc5 |
|---|
| 99 | 99 | #define NCT6791_ID 0xc8 |
|---|
| 100 | 100 | #define NCT6792_ID 0xc9 |
|---|
| .. | .. |
|---|
| 130 | 130 | if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) |
|---|
| 131 | 131 | return -EBUSY; |
|---|
| 132 | 132 | |
|---|
| 133 | | - outb_p(0x87, WDT_EFER); /* Enter extended function mode */ |
|---|
| 134 | | - outb_p(0x87, WDT_EFER); /* Again according to manual */ |
|---|
| 133 | + outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */ |
|---|
| 134 | + outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */ |
|---|
| 135 | 135 | |
|---|
| 136 | 136 | return 0; |
|---|
| 137 | 137 | } |
|---|
| .. | .. |
|---|
| 143 | 143 | |
|---|
| 144 | 144 | static void superio_exit(void) |
|---|
| 145 | 145 | { |
|---|
| 146 | | - outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ |
|---|
| 146 | + outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */ |
|---|
| 147 | 147 | release_region(wdt_io, 2); |
|---|
| 148 | 148 | } |
|---|
| 149 | 149 | |
|---|
| .. | .. |
|---|
| 212 | 212 | case nct6795: |
|---|
| 213 | 213 | case nct6796: |
|---|
| 214 | 214 | case nct6102: |
|---|
| 215 | + case nct6116: |
|---|
| 215 | 216 | /* |
|---|
| 216 | 217 | * These chips have a fixed WDTO# output pin (W83627UHG), |
|---|
| 217 | 218 | * or support more than one WDTO# output pin. |
|---|
| .. | .. |
|---|
| 418 | 419 | cr_wdt_control = NCT6102D_WDT_CONTROL; |
|---|
| 419 | 420 | cr_wdt_csr = NCT6102D_WDT_CSR; |
|---|
| 420 | 421 | break; |
|---|
| 422 | + case NCT6116_ID: |
|---|
| 423 | + ret = nct6116; |
|---|
| 424 | + cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; |
|---|
| 425 | + cr_wdt_control = NCT6102D_WDT_CONTROL; |
|---|
| 426 | + cr_wdt_csr = NCT6102D_WDT_CSR; |
|---|
| 427 | + break; |
|---|
| 421 | 428 | case 0xff: |
|---|
| 422 | 429 | ret = -ENODEV; |
|---|
| 423 | 430 | break; |
|---|
| .. | .. |
|---|
| 429 | 436 | superio_exit(); |
|---|
| 430 | 437 | return ret; |
|---|
| 431 | 438 | } |
|---|
| 439 | + |
|---|
| 440 | +/* |
|---|
| 441 | + * On some systems, the NCT6791D comes with a companion chip and the |
|---|
| 442 | + * watchdog function is in this companion chip. We must use a different |
|---|
| 443 | + * unlocking sequence to access the companion chip. |
|---|
| 444 | + */ |
|---|
| 445 | +static int __init wdt_use_alt_key(const struct dmi_system_id *d) |
|---|
| 446 | +{ |
|---|
| 447 | + wdt_cfg_enter = 0x88; |
|---|
| 448 | + wdt_cfg_leave = 0xBB; |
|---|
| 449 | + |
|---|
| 450 | + return 0; |
|---|
| 451 | +} |
|---|
| 452 | + |
|---|
| 453 | +static const struct dmi_system_id wdt_dmi_table[] __initconst = { |
|---|
| 454 | + { |
|---|
| 455 | + .matches = { |
|---|
| 456 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "INVES"), |
|---|
| 457 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CTS"), |
|---|
| 458 | + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "INVES"), |
|---|
| 459 | + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"), |
|---|
| 460 | + }, |
|---|
| 461 | + .callback = wdt_use_alt_key, |
|---|
| 462 | + }, |
|---|
| 463 | + {} |
|---|
| 464 | +}; |
|---|
| 432 | 465 | |
|---|
| 433 | 466 | static int __init wdt_init(void) |
|---|
| 434 | 467 | { |
|---|
| .. | .. |
|---|
| 457 | 490 | "NCT6795", |
|---|
| 458 | 491 | "NCT6796", |
|---|
| 459 | 492 | "NCT6102", |
|---|
| 493 | + "NCT6116", |
|---|
| 460 | 494 | }; |
|---|
| 461 | 495 | |
|---|
| 496 | + /* Apply system-specific quirks */ |
|---|
| 497 | + dmi_check_system(wdt_dmi_table); |
|---|
| 498 | + |
|---|
| 462 | 499 | wdt_io = 0x2e; |
|---|
| 463 | 500 | chip = wdt_find(0x2e); |
|---|
| 464 | 501 | if (chip < 0) { |
|---|