cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

w83627hf_wdt.c (11915B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *	w83627hf/thf WDT driver
      4 *
      5 *	(c) Copyright 2013 Guenter Roeck
      6 *		converted to watchdog infrastructure
      7 *
      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 provide
     22 *	warranty for any of this software. This material is provided
     23 *	"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 ": " fmt
     29
     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 Register
     76							(same as EFER) */
     77#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
     78
     79#define W83627HF_LD_WDT		0x08
     80
     81#define W83627HF_ID		0x52
     82#define W83627S_ID		0x59
     83#define W83697HF_ID		0x60
     84#define W83697UG_ID		0x68
     85#define W83637HF_ID		0x70
     86#define W83627THF_ID		0x82
     87#define W83687THF_ID		0x85
     88#define W83627EHF_ID		0x88
     89#define W83627DHG_ID		0xa0
     90#define W83627UHG_ID		0xa2
     91#define W83667HG_ID		0xa5
     92#define W83627DHG_P_ID		0xb0
     93#define W83667HG_B_ID		0xb3
     94#define NCT6775_ID		0xb4
     95#define NCT6776_ID		0xc3
     96#define NCT6102_ID		0xc4
     97#define NCT6116_ID		0xd2
     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
    116static void superio_outb(int reg, int val)
    117{
    118	outb(reg, WDT_EFER);
    119	outb(val, WDT_EFDR);
    120}
    121
    122static inline int superio_inb(int reg)
    123{
    124	outb(reg, WDT_EFER);
    125	return inb(WDT_EFDR);
    126}
    127
    128static int superio_enter(void)
    129{
    130	if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
    131		return -EBUSY;
    132
    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
    136	return 0;
    137}
    138
    139static void superio_select(int ld)
    140{
    141	superio_outb(0x07, ld);
    142}
    143
    144static void superio_exit(void)
    145{
    146	outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */
    147	release_region(wdt_io, 2);
    148}
    149
    150static 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	case nct6116:
    216		/*
    217		 * These chips have a fixed WDTO# output pin (W83627UHG),
    218		 * or support more than one WDTO# output pin.
    219		 * Don't touch its configuration, and hope the BIOS
    220		 * does the right thing.
    221		 */
    222		t = superio_inb(cr_wdt_control);
    223		t |= 0x02;	/* enable the WDTO# output low pulse
    224				 * to the KBRST# pin */
    225		superio_outb(cr_wdt_control, t);
    226		break;
    227	default:
    228		break;
    229	}
    230
    231	t = superio_inb(cr_wdt_timeout);
    232	if (t != 0) {
    233		if (early_disable) {
    234			pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
    235			superio_outb(cr_wdt_timeout, 0);
    236		} else {
    237			pr_info("Watchdog already running. Resetting timeout to %d sec\n",
    238				wdog->timeout);
    239			superio_outb(cr_wdt_timeout, wdog->timeout);
    240		}
    241	}
    242
    243	/* set second mode & disable keyboard turning off watchdog */
    244	t = superio_inb(cr_wdt_control) & ~0x0C;
    245	superio_outb(cr_wdt_control, t);
    246
    247	/* reset trigger, disable keyboard & mouse turning off watchdog */
    248	t = superio_inb(cr_wdt_csr) & ~0xD0;
    249	superio_outb(cr_wdt_csr, t);
    250
    251	superio_exit();
    252
    253	return 0;
    254}
    255
    256static int wdt_set_time(unsigned int timeout)
    257{
    258	int ret;
    259
    260	ret = superio_enter();
    261	if (ret)
    262		return ret;
    263
    264	superio_select(W83627HF_LD_WDT);
    265	superio_outb(cr_wdt_timeout, timeout);
    266	superio_exit();
    267
    268	return 0;
    269}
    270
    271static int wdt_start(struct watchdog_device *wdog)
    272{
    273	return wdt_set_time(wdog->timeout);
    274}
    275
    276static int wdt_stop(struct watchdog_device *wdog)
    277{
    278	return wdt_set_time(0);
    279}
    280
    281static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
    282{
    283	wdog->timeout = timeout;
    284
    285	return 0;
    286}
    287
    288static unsigned int wdt_get_time(struct watchdog_device *wdog)
    289{
    290	unsigned int timeleft;
    291	int ret;
    292
    293	ret = superio_enter();
    294	if (ret)
    295		return 0;
    296
    297	superio_select(W83627HF_LD_WDT);
    298	timeleft = superio_inb(cr_wdt_timeout);
    299	superio_exit();
    300
    301	return timeleft;
    302}
    303
    304/*
    305 *	Kernel Interfaces
    306 */
    307
    308static const struct watchdog_info wdt_info = {
    309	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    310	.identity = "W83627HF Watchdog",
    311};
    312
    313static const struct watchdog_ops wdt_ops = {
    314	.owner = THIS_MODULE,
    315	.start = wdt_start,
    316	.stop = wdt_stop,
    317	.set_timeout = wdt_set_timeout,
    318	.get_timeleft = wdt_get_time,
    319};
    320
    321static struct watchdog_device wdt_dev = {
    322	.info = &wdt_info,
    323	.ops = &wdt_ops,
    324	.timeout = WATCHDOG_TIMEOUT,
    325	.min_timeout = 1,
    326	.max_timeout = 255,
    327};
    328
    329/*
    330 *	The WDT needs to learn about soft shutdowns in order to
    331 *	turn the timebomb registers off.
    332 */
    333
    334static int wdt_find(int addr)
    335{
    336	u8 val;
    337	int ret;
    338
    339	cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
    340	cr_wdt_control = W83627HF_WDT_CONTROL;
    341	cr_wdt_csr = W836X7HF_WDT_CSR;
    342
    343	ret = superio_enter();
    344	if (ret)
    345		return ret;
    346	superio_select(W83627HF_LD_WDT);
    347	val = superio_inb(0x20);
    348	switch (val) {
    349	case W83627HF_ID:
    350		ret = w83627hf;
    351		break;
    352	case W83627S_ID:
    353		ret = w83627s;
    354		break;
    355	case W83697HF_ID:
    356		ret = w83697hf;
    357		cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
    358		cr_wdt_control = W83697HF_WDT_CONTROL;
    359		break;
    360	case W83697UG_ID:
    361		ret = w83697ug;
    362		cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
    363		cr_wdt_control = W83697HF_WDT_CONTROL;
    364		break;
    365	case W83637HF_ID:
    366		ret = w83637hf;
    367		break;
    368	case W83627THF_ID:
    369		ret = w83627thf;
    370		break;
    371	case W83687THF_ID:
    372		ret = w83687thf;
    373		break;
    374	case W83627EHF_ID:
    375		ret = w83627ehf;
    376		break;
    377	case W83627DHG_ID:
    378		ret = w83627dhg;
    379		break;
    380	case W83627DHG_P_ID:
    381		ret = w83627dhg_p;
    382		break;
    383	case W83627UHG_ID:
    384		ret = w83627uhg;
    385		break;
    386	case W83667HG_ID:
    387		ret = w83667hg;
    388		break;
    389	case W83667HG_B_ID:
    390		ret = w83667hg_b;
    391		break;
    392	case NCT6775_ID:
    393		ret = nct6775;
    394		break;
    395	case NCT6776_ID:
    396		ret = nct6776;
    397		break;
    398	case NCT6779_ID:
    399		ret = nct6779;
    400		break;
    401	case NCT6791_ID:
    402		ret = nct6791;
    403		break;
    404	case NCT6792_ID:
    405		ret = nct6792;
    406		break;
    407	case NCT6793_ID:
    408		ret = nct6793;
    409		break;
    410	case NCT6795_ID:
    411		ret = nct6795;
    412		break;
    413	case NCT6796_ID:
    414		ret = nct6796;
    415		break;
    416	case NCT6102_ID:
    417		ret = nct6102;
    418		cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
    419		cr_wdt_control = NCT6102D_WDT_CONTROL;
    420		cr_wdt_csr = NCT6102D_WDT_CSR;
    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;
    428	case 0xff:
    429		ret = -ENODEV;
    430		break;
    431	default:
    432		ret = -ENODEV;
    433		pr_err("Unsupported chip ID: 0x%02x\n", val);
    434		break;
    435	}
    436	superio_exit();
    437	return ret;
    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 */
    445static 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
    453static 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};
    465
    466static int __init wdt_init(void)
    467{
    468	int ret;
    469	int chip;
    470	static const char * const chip_name[] = {
    471		"W83627HF",
    472		"W83627S",
    473		"W83697HF",
    474		"W83697UG",
    475		"W83637HF",
    476		"W83627THF",
    477		"W83687THF",
    478		"W83627EHF",
    479		"W83627DHG",
    480		"W83627UHG",
    481		"W83667HG",
    482		"W83667DHG-P",
    483		"W83667HG-B",
    484		"NCT6775",
    485		"NCT6776",
    486		"NCT6779",
    487		"NCT6791",
    488		"NCT6792",
    489		"NCT6793",
    490		"NCT6795",
    491		"NCT6796",
    492		"NCT6102",
    493		"NCT6116",
    494	};
    495
    496	/* Apply system-specific quirks */
    497	dmi_check_system(wdt_dmi_table);
    498
    499	wdt_io = 0x2e;
    500	chip = wdt_find(0x2e);
    501	if (chip < 0) {
    502		wdt_io = 0x4e;
    503		chip = wdt_find(0x4e);
    504		if (chip < 0)
    505			return chip;
    506	}
    507
    508	pr_info("WDT driver for %s Super I/O chip initialising\n",
    509		chip_name[chip]);
    510
    511	watchdog_init_timeout(&wdt_dev, timeout, NULL);
    512	watchdog_set_nowayout(&wdt_dev, nowayout);
    513	watchdog_stop_on_reboot(&wdt_dev);
    514
    515	ret = w83627hf_init(&wdt_dev, chip);
    516	if (ret) {
    517		pr_err("failed to initialize watchdog (err=%d)\n", ret);
    518		return ret;
    519	}
    520
    521	ret = watchdog_register_device(&wdt_dev);
    522	if (ret)
    523		return ret;
    524
    525	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
    526		wdt_dev.timeout, nowayout);
    527
    528	return ret;
    529}
    530
    531static void __exit wdt_exit(void)
    532{
    533	watchdog_unregister_device(&wdt_dev);
    534}
    535
    536module_init(wdt_init);
    537module_exit(wdt_exit);
    538
    539MODULE_LICENSE("GPL");
    540MODULE_AUTHOR("Pádraig  Brady <P@draigBrady.com>");
    541MODULE_DESCRIPTION("w83627hf/thf WDT driver");