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