brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · bc33b63 Raw
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