xref: /openbmc/linux/drivers/watchdog/w83627hf_wdt.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1d0173278SGuenter Roeck // SPDX-License-Identifier: GPL-2.0+
2b7e04f8cSWim Van Sebroeck /*
3b7e04f8cSWim Van Sebroeck  *	w83627hf/thf WDT driver
4b7e04f8cSWim Van Sebroeck  *
530a83695SGuenter Roeck  *	(c) Copyright 2013 Guenter Roeck
630a83695SGuenter Roeck  *		converted to watchdog infrastructure
730a83695SGuenter Roeck  *
8b7e04f8cSWim Van Sebroeck  *	(c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
9b7e04f8cSWim Van Sebroeck  *		added support for W83627THF.
10b7e04f8cSWim Van Sebroeck  *
11d36b6910SAl Viro  *	(c) Copyright 2003,2007 Pádraig Brady <P@draigBrady.com>
12b7e04f8cSWim Van Sebroeck  *
13b7e04f8cSWim Van Sebroeck  *	Based on advantechwdt.c which is based on wdt.c.
14b7e04f8cSWim Van Sebroeck  *	Original copyright messages:
15b7e04f8cSWim Van Sebroeck  *
16b7e04f8cSWim Van Sebroeck  *	(c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
17b7e04f8cSWim Van Sebroeck  *
1829fa0586SAlan Cox  *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
1929fa0586SAlan Cox  *						All Rights Reserved.
20b7e04f8cSWim Van Sebroeck  *
21b7e04f8cSWim Van Sebroeck  *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
22b7e04f8cSWim Van Sebroeck  *	warranty for any of this software. This material is provided
23b7e04f8cSWim Van Sebroeck  *	"AS-IS" and at no charge.
24b7e04f8cSWim Van Sebroeck  *
2529fa0586SAlan Cox  *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
26b7e04f8cSWim Van Sebroeck  */
27b7e04f8cSWim Van Sebroeck 
2827c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2927c766aaSJoe Perches 
30b7e04f8cSWim Van Sebroeck #include <linux/module.h>
31b7e04f8cSWim Van Sebroeck #include <linux/moduleparam.h>
32b7e04f8cSWim Van Sebroeck #include <linux/types.h>
33b7e04f8cSWim Van Sebroeck #include <linux/watchdog.h>
34b7e04f8cSWim Van Sebroeck #include <linux/ioport.h>
35b7e04f8cSWim Van Sebroeck #include <linux/init.h>
3646a3949dSAlan Cox #include <linux/io.h>
3731eb42bdSJean Delvare #include <linux/dmi.h>
38b7e04f8cSWim Van Sebroeck 
399c67bea4SBenny Loenstrup Ammitzboell #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
40b7e04f8cSWim Van Sebroeck #define WATCHDOG_TIMEOUT 60		/* 60 sec default timeout */
41b7e04f8cSWim Van Sebroeck 
42962c04f5SGuenter Roeck static int wdt_io;
437b6d0b6aSGuenter Roeck static int cr_wdt_timeout;	/* WDT timeout register */
447b6d0b6aSGuenter Roeck static int cr_wdt_control;	/* WDT control register */
4533f74b89SRob Kramer static int cr_wdt_csr;		/* WDT control & status register */
4631eb42bdSJean Delvare static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
4731eb42bdSJean Delvare static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
48962c04f5SGuenter Roeck 
497b6d0b6aSGuenter Roeck enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
507b6d0b6aSGuenter Roeck 	     w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
513a9aedb2SGuenter Roeck 	     w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
52e11cfc69SSrikanth Krishnakar 	     nct6795, nct6796, nct6102, nct6116 };
53b7e04f8cSWim Van Sebroeck 
5430a83695SGuenter Roeck static int timeout;			/* in seconds */
55b7e04f8cSWim Van Sebroeck module_param(timeout, int, 0);
5646a3949dSAlan Cox MODULE_PARM_DESC(timeout,
5746a3949dSAlan Cox 		"Watchdog timeout in seconds. 1 <= timeout <= 255, default="
5846a3949dSAlan Cox 				__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
59b7e04f8cSWim Van Sebroeck 
6086a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
6186a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
6246a3949dSAlan Cox MODULE_PARM_DESC(nowayout,
6346a3949dSAlan Cox 		"Watchdog cannot be stopped once started (default="
6446a3949dSAlan Cox 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
65b7e04f8cSWim Van Sebroeck 
66be281588SGuenter Roeck static int early_disable;
67be281588SGuenter Roeck module_param(early_disable, int, 0);
68be281588SGuenter Roeck MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
69be281588SGuenter Roeck 
70b7e04f8cSWim Van Sebroeck /*
71b7e04f8cSWim Van Sebroeck  *	Kernel methods.
72b7e04f8cSWim Van Sebroeck  */
73b7e04f8cSWim Van Sebroeck 
74b7e04f8cSWim Van Sebroeck #define WDT_EFER (wdt_io+0)   /* Extended Function Enable Registers */
7546a3949dSAlan Cox #define WDT_EFIR (wdt_io+0)   /* Extended Function Index Register
7646a3949dSAlan Cox 							(same as EFER) */
77b7e04f8cSWim Van Sebroeck #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
78b7e04f8cSWim Van Sebroeck 
79ef0c1a6bSGuenter Roeck #define W83627HF_LD_WDT		0x08
80ef0c1a6bSGuenter Roeck 
81962c04f5SGuenter Roeck #define W83627HF_ID		0x52
82962c04f5SGuenter Roeck #define W83627S_ID		0x59
837b6d0b6aSGuenter Roeck #define W83697HF_ID		0x60
847b6d0b6aSGuenter Roeck #define W83697UG_ID		0x68
85962c04f5SGuenter Roeck #define W83637HF_ID		0x70
86962c04f5SGuenter Roeck #define W83627THF_ID		0x82
87962c04f5SGuenter Roeck #define W83687THF_ID		0x85
88962c04f5SGuenter Roeck #define W83627EHF_ID		0x88
89962c04f5SGuenter Roeck #define W83627DHG_ID		0xa0
90962c04f5SGuenter Roeck #define W83627UHG_ID		0xa2
91962c04f5SGuenter Roeck #define W83667HG_ID		0xa5
92962c04f5SGuenter Roeck #define W83627DHG_P_ID		0xb0
93962c04f5SGuenter Roeck #define W83667HG_B_ID		0xb3
94962c04f5SGuenter Roeck #define NCT6775_ID		0xb4
95962c04f5SGuenter Roeck #define NCT6776_ID		0xc3
9633f74b89SRob Kramer #define NCT6102_ID		0xc4
97e11cfc69SSrikanth Krishnakar #define NCT6116_ID		0xd2
98962c04f5SGuenter Roeck #define NCT6779_ID		0xc5
99a77841d5SGuenter Roeck #define NCT6791_ID		0xc8
100a77841d5SGuenter Roeck #define NCT6792_ID		0xc9
1013a9aedb2SGuenter Roeck #define NCT6793_ID		0xd1
1023a9aedb2SGuenter Roeck #define NCT6795_ID		0xd3
10357cbf0e3SGuenter Roeck #define NCT6796_ID		0xd4	/* also NCT9697D, NCT9698D */
104962c04f5SGuenter Roeck 
1057b6d0b6aSGuenter Roeck #define W83627HF_WDT_TIMEOUT	0xf6
1067b6d0b6aSGuenter Roeck #define W83697HF_WDT_TIMEOUT	0xf4
10733f74b89SRob Kramer #define NCT6102D_WDT_TIMEOUT	0xf1
1087b6d0b6aSGuenter Roeck 
1097b6d0b6aSGuenter Roeck #define W83627HF_WDT_CONTROL	0xf5
1107b6d0b6aSGuenter Roeck #define W83697HF_WDT_CONTROL	0xf3
11133f74b89SRob Kramer #define NCT6102D_WDT_CONTROL	0xf0
11233f74b89SRob Kramer 
11333f74b89SRob Kramer #define W836X7HF_WDT_CSR	0xf7
11433f74b89SRob Kramer #define NCT6102D_WDT_CSR	0xf2
1157b6d0b6aSGuenter Roeck 
116*5a9fbf8bSHenning Schild #define WDT_CSR_STATUS		0x10
117*5a9fbf8bSHenning Schild #define WDT_CSR_KBD		0x40
118*5a9fbf8bSHenning Schild #define WDT_CSR_MOUSE		0x80
119*5a9fbf8bSHenning Schild 
superio_outb(int reg,int val)120ef0c1a6bSGuenter Roeck static void superio_outb(int reg, int val)
121b7e04f8cSWim Van Sebroeck {
122ef0c1a6bSGuenter Roeck 	outb(reg, WDT_EFER);
123ef0c1a6bSGuenter Roeck 	outb(val, WDT_EFDR);
124b7e04f8cSWim Van Sebroeck }
125b7e04f8cSWim Van Sebroeck 
superio_inb(int reg)126ef0c1a6bSGuenter Roeck static inline int superio_inb(int reg)
127ef0c1a6bSGuenter Roeck {
128ef0c1a6bSGuenter Roeck 	outb(reg, WDT_EFER);
129ef0c1a6bSGuenter Roeck 	return inb(WDT_EFDR);
130ef0c1a6bSGuenter Roeck }
131ef0c1a6bSGuenter Roeck 
superio_enter(void)132ef0c1a6bSGuenter Roeck static int superio_enter(void)
133ef0c1a6bSGuenter Roeck {
134ef0c1a6bSGuenter Roeck 	if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME))
135ef0c1a6bSGuenter Roeck 		return -EBUSY;
136ef0c1a6bSGuenter Roeck 
13731eb42bdSJean Delvare 	outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */
13831eb42bdSJean Delvare 	outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */
139ef0c1a6bSGuenter Roeck 
140ef0c1a6bSGuenter Roeck 	return 0;
141ef0c1a6bSGuenter Roeck }
142ef0c1a6bSGuenter Roeck 
superio_select(int ld)143ef0c1a6bSGuenter Roeck static void superio_select(int ld)
144ef0c1a6bSGuenter Roeck {
145ef0c1a6bSGuenter Roeck 	superio_outb(0x07, ld);
146ef0c1a6bSGuenter Roeck }
147ef0c1a6bSGuenter Roeck 
superio_exit(void)148ef0c1a6bSGuenter Roeck static void superio_exit(void)
149b7e04f8cSWim Van Sebroeck {
15031eb42bdSJean Delvare 	outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */
151ef0c1a6bSGuenter Roeck 	release_region(wdt_io, 2);
152b7e04f8cSWim Van Sebroeck }
153b7e04f8cSWim Van Sebroeck 
w83627hf_init(struct watchdog_device * wdog,enum chips chip)154962c04f5SGuenter Roeck static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
155b7e04f8cSWim Van Sebroeck {
156ef0c1a6bSGuenter Roeck 	int ret;
157b7e04f8cSWim Van Sebroeck 	unsigned char t;
158b7e04f8cSWim Van Sebroeck 
159ef0c1a6bSGuenter Roeck 	ret = superio_enter();
160ef0c1a6bSGuenter Roeck 	if (ret)
161ef0c1a6bSGuenter Roeck 		return ret;
162b7e04f8cSWim Van Sebroeck 
163ef0c1a6bSGuenter Roeck 	superio_select(W83627HF_LD_WDT);
1648f526389SGuenter Roeck 
165ef0c1a6bSGuenter Roeck 	/* set CR30 bit 0 to activate GPIO2 */
166ef0c1a6bSGuenter Roeck 	t = superio_inb(0x30);
167ac461103SGuenter Roeck 	if (!(t & 0x01))
168ef0c1a6bSGuenter Roeck 		superio_outb(0x30, t | 0x01);
1698f526389SGuenter Roeck 
170962c04f5SGuenter Roeck 	switch (chip) {
171962c04f5SGuenter Roeck 	case w83627hf:
172962c04f5SGuenter Roeck 	case w83627s:
173962c04f5SGuenter Roeck 		t = superio_inb(0x2B) & ~0x10;
174962c04f5SGuenter Roeck 		superio_outb(0x2B, t); /* set GPIO24 to WDT0 */
175962c04f5SGuenter Roeck 		break;
1767b6d0b6aSGuenter Roeck 	case w83697hf:
1777b6d0b6aSGuenter Roeck 		/* Set pin 119 to WDTO# mode (= CR29, WDT0) */
1787b6d0b6aSGuenter Roeck 		t = superio_inb(0x29) & ~0x60;
1797b6d0b6aSGuenter Roeck 		t |= 0x20;
1807b6d0b6aSGuenter Roeck 		superio_outb(0x29, t);
1817b6d0b6aSGuenter Roeck 		break;
1827b6d0b6aSGuenter Roeck 	case w83697ug:
1837b6d0b6aSGuenter Roeck 		/* Set pin 118 to WDTO# mode */
1847b6d0b6aSGuenter Roeck 		t = superio_inb(0x2b) & ~0x04;
1857b6d0b6aSGuenter Roeck 		superio_outb(0x2b, t);
1867b6d0b6aSGuenter Roeck 		break;
187962c04f5SGuenter Roeck 	case w83627thf:
188962c04f5SGuenter Roeck 		t = (superio_inb(0x2B) & ~0x08) | 0x04;
189962c04f5SGuenter Roeck 		superio_outb(0x2B, t); /* set GPIO3 to WDT0 */
190962c04f5SGuenter Roeck 		break;
191962c04f5SGuenter Roeck 	case w83627dhg:
192962c04f5SGuenter Roeck 	case w83627dhg_p:
193962c04f5SGuenter Roeck 		t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */
194962c04f5SGuenter Roeck 		superio_outb(0x2D, t); /* set GPIO5 to WDT0 */
1957b6d0b6aSGuenter Roeck 		t = superio_inb(cr_wdt_control);
196962c04f5SGuenter Roeck 		t |= 0x02;	/* enable the WDTO# output low pulse
197962c04f5SGuenter Roeck 				 * to the KBRST# pin */
1987b6d0b6aSGuenter Roeck 		superio_outb(cr_wdt_control, t);
199962c04f5SGuenter Roeck 		break;
200962c04f5SGuenter Roeck 	case w83637hf:
201962c04f5SGuenter Roeck 		break;
202962c04f5SGuenter Roeck 	case w83687thf:
203962c04f5SGuenter Roeck 		t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */
204962c04f5SGuenter Roeck 		superio_outb(0x2C, t);
205962c04f5SGuenter Roeck 		break;
206962c04f5SGuenter Roeck 	case w83627ehf:
207962c04f5SGuenter Roeck 	case w83627uhg:
208962c04f5SGuenter Roeck 	case w83667hg:
209962c04f5SGuenter Roeck 	case w83667hg_b:
210962c04f5SGuenter Roeck 	case nct6775:
211962c04f5SGuenter Roeck 	case nct6776:
212962c04f5SGuenter Roeck 	case nct6779:
213a77841d5SGuenter Roeck 	case nct6791:
214a77841d5SGuenter Roeck 	case nct6792:
2153a9aedb2SGuenter Roeck 	case nct6793:
2163a9aedb2SGuenter Roeck 	case nct6795:
21757cbf0e3SGuenter Roeck 	case nct6796:
21833f74b89SRob Kramer 	case nct6102:
219e11cfc69SSrikanth Krishnakar 	case nct6116:
220962c04f5SGuenter Roeck 		/*
221962c04f5SGuenter Roeck 		 * These chips have a fixed WDTO# output pin (W83627UHG),
222962c04f5SGuenter Roeck 		 * or support more than one WDTO# output pin.
223962c04f5SGuenter Roeck 		 * Don't touch its configuration, and hope the BIOS
224962c04f5SGuenter Roeck 		 * does the right thing.
225962c04f5SGuenter Roeck 		 */
2267b6d0b6aSGuenter Roeck 		t = superio_inb(cr_wdt_control);
227962c04f5SGuenter Roeck 		t |= 0x02;	/* enable the WDTO# output low pulse
228962c04f5SGuenter Roeck 				 * to the KBRST# pin */
2297b6d0b6aSGuenter Roeck 		superio_outb(cr_wdt_control, t);
230962c04f5SGuenter Roeck 		break;
231962c04f5SGuenter Roeck 	default:
232962c04f5SGuenter Roeck 		break;
233962c04f5SGuenter Roeck 	}
234962c04f5SGuenter Roeck 
2357b6d0b6aSGuenter Roeck 	t = superio_inb(cr_wdt_timeout);
236b7e04f8cSWim Van Sebroeck 	if (t != 0) {
237be281588SGuenter Roeck 		if (early_disable) {
238be281588SGuenter Roeck 			pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
239be281588SGuenter Roeck 			superio_outb(cr_wdt_timeout, 0);
240be281588SGuenter Roeck 		} else {
24127c766aaSJoe Perches 			pr_info("Watchdog already running. Resetting timeout to %d sec\n",
24230a83695SGuenter Roeck 				wdog->timeout);
2437b6d0b6aSGuenter Roeck 			superio_outb(cr_wdt_timeout, wdog->timeout);
244b7e04f8cSWim Van Sebroeck 		}
245be281588SGuenter Roeck 	}
246b7e04f8cSWim Van Sebroeck 
247ef0c1a6bSGuenter Roeck 	/* set second mode & disable keyboard turning off watchdog */
2487b6d0b6aSGuenter Roeck 	t = superio_inb(cr_wdt_control) & ~0x0C;
2497b6d0b6aSGuenter Roeck 	superio_outb(cr_wdt_control, t);
250b7e04f8cSWim Van Sebroeck 
251*5a9fbf8bSHenning Schild 	t = superio_inb(cr_wdt_csr);
252*5a9fbf8bSHenning Schild 	if (t & WDT_CSR_STATUS)
253*5a9fbf8bSHenning Schild 		wdog->bootstatus |= WDIOF_CARDRESET;
254*5a9fbf8bSHenning Schild 
255*5a9fbf8bSHenning Schild 	/* reset status, disable keyboard & mouse turning off watchdog */
256*5a9fbf8bSHenning Schild 	t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);
25733f74b89SRob Kramer 	superio_outb(cr_wdt_csr, t);
258b7e04f8cSWim Van Sebroeck 
259ef0c1a6bSGuenter Roeck 	superio_exit();
260ef0c1a6bSGuenter Roeck 
261ef0c1a6bSGuenter Roeck 	return 0;
262b7e04f8cSWim Van Sebroeck }
263b7e04f8cSWim Van Sebroeck 
wdt_set_time(unsigned int timeout)26430a83695SGuenter Roeck static int wdt_set_time(unsigned int timeout)
265b7e04f8cSWim Van Sebroeck {
266ef0c1a6bSGuenter Roeck 	int ret;
267b7e04f8cSWim Van Sebroeck 
268ef0c1a6bSGuenter Roeck 	ret = superio_enter();
269ef0c1a6bSGuenter Roeck 	if (ret)
270ef0c1a6bSGuenter Roeck 		return ret;
271b7e04f8cSWim Van Sebroeck 
272ef0c1a6bSGuenter Roeck 	superio_select(W83627HF_LD_WDT);
2737b6d0b6aSGuenter Roeck 	superio_outb(cr_wdt_timeout, timeout);
274ef0c1a6bSGuenter Roeck 	superio_exit();
275b7e04f8cSWim Van Sebroeck 
276b7e04f8cSWim Van Sebroeck 	return 0;
277b7e04f8cSWim Van Sebroeck }
278b7e04f8cSWim Van Sebroeck 
wdt_start(struct watchdog_device * wdog)27930a83695SGuenter Roeck static int wdt_start(struct watchdog_device *wdog)
280b7e04f8cSWim Van Sebroeck {
28130a83695SGuenter Roeck 	return wdt_set_time(wdog->timeout);
28230a83695SGuenter Roeck }
28330a83695SGuenter Roeck 
wdt_stop(struct watchdog_device * wdog)28430a83695SGuenter Roeck static int wdt_stop(struct watchdog_device *wdog)
28530a83695SGuenter Roeck {
28630a83695SGuenter Roeck 	return wdt_set_time(0);
28730a83695SGuenter Roeck }
28830a83695SGuenter Roeck 
wdt_set_timeout(struct watchdog_device * wdog,unsigned int timeout)28930a83695SGuenter Roeck static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
29030a83695SGuenter Roeck {
29130a83695SGuenter Roeck 	wdog->timeout = timeout;
29230a83695SGuenter Roeck 
293b7e04f8cSWim Van Sebroeck 	return 0;
294b7e04f8cSWim Van Sebroeck }
295b7e04f8cSWim Van Sebroeck 
wdt_get_time(struct watchdog_device * wdog)29630a83695SGuenter Roeck static unsigned int wdt_get_time(struct watchdog_device *wdog)
297b7e04f8cSWim Van Sebroeck {
29830a83695SGuenter Roeck 	unsigned int timeleft;
299ef0c1a6bSGuenter Roeck 	int ret;
300c63b6d02SGreg Lee 
301ef0c1a6bSGuenter Roeck 	ret = superio_enter();
302ef0c1a6bSGuenter Roeck 	if (ret)
303ef0c1a6bSGuenter Roeck 		return 0;
304c63b6d02SGreg Lee 
305ef0c1a6bSGuenter Roeck 	superio_select(W83627HF_LD_WDT);
3067b6d0b6aSGuenter Roeck 	timeleft = superio_inb(cr_wdt_timeout);
307ef0c1a6bSGuenter Roeck 	superio_exit();
308c63b6d02SGreg Lee 
309c63b6d02SGreg Lee 	return timeleft;
310c63b6d02SGreg Lee }
311c63b6d02SGreg Lee 
312b7e04f8cSWim Van Sebroeck /*
313b7e04f8cSWim Van Sebroeck  *	Kernel Interfaces
314b7e04f8cSWim Van Sebroeck  */
315b7e04f8cSWim Van Sebroeck 
3166c368932SBhumika Goyal static const struct watchdog_info wdt_info = {
31730a83695SGuenter Roeck 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
31830a83695SGuenter Roeck 	.identity = "W83627HF Watchdog",
319b7e04f8cSWim Van Sebroeck };
320b7e04f8cSWim Van Sebroeck 
32185f15cfcSJulia Lawall static const struct watchdog_ops wdt_ops = {
32230a83695SGuenter Roeck 	.owner = THIS_MODULE,
32330a83695SGuenter Roeck 	.start = wdt_start,
32430a83695SGuenter Roeck 	.stop = wdt_stop,
32530a83695SGuenter Roeck 	.set_timeout = wdt_set_timeout,
32630a83695SGuenter Roeck 	.get_timeleft = wdt_get_time,
32730a83695SGuenter Roeck };
32830a83695SGuenter Roeck 
32930a83695SGuenter Roeck static struct watchdog_device wdt_dev = {
33030a83695SGuenter Roeck 	.info = &wdt_info,
33130a83695SGuenter Roeck 	.ops = &wdt_ops,
33230a83695SGuenter Roeck 	.timeout = WATCHDOG_TIMEOUT,
33330a83695SGuenter Roeck 	.min_timeout = 1,
33430a83695SGuenter Roeck 	.max_timeout = 255,
335b7e04f8cSWim Van Sebroeck };
336b7e04f8cSWim Van Sebroeck 
337b7e04f8cSWim Van Sebroeck /*
338b7e04f8cSWim Van Sebroeck  *	The WDT needs to learn about soft shutdowns in order to
339b7e04f8cSWim Van Sebroeck  *	turn the timebomb registers off.
340b7e04f8cSWim Van Sebroeck  */
341b7e04f8cSWim Van Sebroeck 
wdt_find(int addr)342962c04f5SGuenter Roeck static int wdt_find(int addr)
343962c04f5SGuenter Roeck {
344962c04f5SGuenter Roeck 	u8 val;
345962c04f5SGuenter Roeck 	int ret;
346962c04f5SGuenter Roeck 
3477b6d0b6aSGuenter Roeck 	cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
3487b6d0b6aSGuenter Roeck 	cr_wdt_control = W83627HF_WDT_CONTROL;
34933f74b89SRob Kramer 	cr_wdt_csr = W836X7HF_WDT_CSR;
3507b6d0b6aSGuenter Roeck 
351962c04f5SGuenter Roeck 	ret = superio_enter();
352962c04f5SGuenter Roeck 	if (ret)
353962c04f5SGuenter Roeck 		return ret;
354962c04f5SGuenter Roeck 	superio_select(W83627HF_LD_WDT);
355962c04f5SGuenter Roeck 	val = superio_inb(0x20);
356962c04f5SGuenter Roeck 	switch (val) {
357962c04f5SGuenter Roeck 	case W83627HF_ID:
358962c04f5SGuenter Roeck 		ret = w83627hf;
359962c04f5SGuenter Roeck 		break;
360962c04f5SGuenter Roeck 	case W83627S_ID:
361962c04f5SGuenter Roeck 		ret = w83627s;
362962c04f5SGuenter Roeck 		break;
3637b6d0b6aSGuenter Roeck 	case W83697HF_ID:
3647b6d0b6aSGuenter Roeck 		ret = w83697hf;
3657b6d0b6aSGuenter Roeck 		cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
3667b6d0b6aSGuenter Roeck 		cr_wdt_control = W83697HF_WDT_CONTROL;
3677b6d0b6aSGuenter Roeck 		break;
3687b6d0b6aSGuenter Roeck 	case W83697UG_ID:
3697b6d0b6aSGuenter Roeck 		ret = w83697ug;
3707b6d0b6aSGuenter Roeck 		cr_wdt_timeout = W83697HF_WDT_TIMEOUT;
3717b6d0b6aSGuenter Roeck 		cr_wdt_control = W83697HF_WDT_CONTROL;
3727b6d0b6aSGuenter Roeck 		break;
373962c04f5SGuenter Roeck 	case W83637HF_ID:
374962c04f5SGuenter Roeck 		ret = w83637hf;
375962c04f5SGuenter Roeck 		break;
376962c04f5SGuenter Roeck 	case W83627THF_ID:
377962c04f5SGuenter Roeck 		ret = w83627thf;
378962c04f5SGuenter Roeck 		break;
379962c04f5SGuenter Roeck 	case W83687THF_ID:
380962c04f5SGuenter Roeck 		ret = w83687thf;
381962c04f5SGuenter Roeck 		break;
382962c04f5SGuenter Roeck 	case W83627EHF_ID:
383962c04f5SGuenter Roeck 		ret = w83627ehf;
384962c04f5SGuenter Roeck 		break;
385962c04f5SGuenter Roeck 	case W83627DHG_ID:
386962c04f5SGuenter Roeck 		ret = w83627dhg;
387962c04f5SGuenter Roeck 		break;
388962c04f5SGuenter Roeck 	case W83627DHG_P_ID:
389962c04f5SGuenter Roeck 		ret = w83627dhg_p;
390962c04f5SGuenter Roeck 		break;
391962c04f5SGuenter Roeck 	case W83627UHG_ID:
392962c04f5SGuenter Roeck 		ret = w83627uhg;
393962c04f5SGuenter Roeck 		break;
394962c04f5SGuenter Roeck 	case W83667HG_ID:
395962c04f5SGuenter Roeck 		ret = w83667hg;
396962c04f5SGuenter Roeck 		break;
397962c04f5SGuenter Roeck 	case W83667HG_B_ID:
398962c04f5SGuenter Roeck 		ret = w83667hg_b;
399962c04f5SGuenter Roeck 		break;
400962c04f5SGuenter Roeck 	case NCT6775_ID:
401962c04f5SGuenter Roeck 		ret = nct6775;
402962c04f5SGuenter Roeck 		break;
403962c04f5SGuenter Roeck 	case NCT6776_ID:
404962c04f5SGuenter Roeck 		ret = nct6776;
405962c04f5SGuenter Roeck 		break;
406962c04f5SGuenter Roeck 	case NCT6779_ID:
407962c04f5SGuenter Roeck 		ret = nct6779;
408962c04f5SGuenter Roeck 		break;
409a77841d5SGuenter Roeck 	case NCT6791_ID:
410a77841d5SGuenter Roeck 		ret = nct6791;
411a77841d5SGuenter Roeck 		break;
412a77841d5SGuenter Roeck 	case NCT6792_ID:
413a77841d5SGuenter Roeck 		ret = nct6792;
414a77841d5SGuenter Roeck 		break;
4153a9aedb2SGuenter Roeck 	case NCT6793_ID:
4163a9aedb2SGuenter Roeck 		ret = nct6793;
4173a9aedb2SGuenter Roeck 		break;
4183a9aedb2SGuenter Roeck 	case NCT6795_ID:
4193a9aedb2SGuenter Roeck 		ret = nct6795;
4203a9aedb2SGuenter Roeck 		break;
42157cbf0e3SGuenter Roeck 	case NCT6796_ID:
42257cbf0e3SGuenter Roeck 		ret = nct6796;
42357cbf0e3SGuenter Roeck 		break;
42433f74b89SRob Kramer 	case NCT6102_ID:
42533f74b89SRob Kramer 		ret = nct6102;
42633f74b89SRob Kramer 		cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
42733f74b89SRob Kramer 		cr_wdt_control = NCT6102D_WDT_CONTROL;
42833f74b89SRob Kramer 		cr_wdt_csr = NCT6102D_WDT_CSR;
42933f74b89SRob Kramer 		break;
430e11cfc69SSrikanth Krishnakar 	case NCT6116_ID:
431afcd5b9bSSrikanth Krishnakar 		ret = nct6116;
432e11cfc69SSrikanth Krishnakar 		cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
433e11cfc69SSrikanth Krishnakar 		cr_wdt_control = NCT6102D_WDT_CONTROL;
434e11cfc69SSrikanth Krishnakar 		cr_wdt_csr = NCT6102D_WDT_CSR;
435e11cfc69SSrikanth Krishnakar 		break;
436962c04f5SGuenter Roeck 	case 0xff:
437962c04f5SGuenter Roeck 		ret = -ENODEV;
438962c04f5SGuenter Roeck 		break;
439962c04f5SGuenter Roeck 	default:
440962c04f5SGuenter Roeck 		ret = -ENODEV;
441962c04f5SGuenter Roeck 		pr_err("Unsupported chip ID: 0x%02x\n", val);
442962c04f5SGuenter Roeck 		break;
443962c04f5SGuenter Roeck 	}
444962c04f5SGuenter Roeck 	superio_exit();
445962c04f5SGuenter Roeck 	return ret;
446962c04f5SGuenter Roeck }
447962c04f5SGuenter Roeck 
44831eb42bdSJean Delvare /*
44931eb42bdSJean Delvare  * On some systems, the NCT6791D comes with a companion chip and the
45031eb42bdSJean Delvare  * watchdog function is in this companion chip. We must use a different
45131eb42bdSJean Delvare  * unlocking sequence to access the companion chip.
45231eb42bdSJean Delvare  */
wdt_use_alt_key(const struct dmi_system_id * d)45331eb42bdSJean Delvare static int __init wdt_use_alt_key(const struct dmi_system_id *d)
45431eb42bdSJean Delvare {
45531eb42bdSJean Delvare 	wdt_cfg_enter = 0x88;
45631eb42bdSJean Delvare 	wdt_cfg_leave = 0xBB;
45731eb42bdSJean Delvare 
45831eb42bdSJean Delvare 	return 0;
45931eb42bdSJean Delvare }
46031eb42bdSJean Delvare 
46131eb42bdSJean Delvare static const struct dmi_system_id wdt_dmi_table[] __initconst = {
46231eb42bdSJean Delvare 	{
46331eb42bdSJean Delvare 		.matches = {
46431eb42bdSJean Delvare 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "INVES"),
46531eb42bdSJean Delvare 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CTS"),
46631eb42bdSJean Delvare 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "INVES"),
46731eb42bdSJean Delvare 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"),
46831eb42bdSJean Delvare 		},
46931eb42bdSJean Delvare 		.callback = wdt_use_alt_key,
47031eb42bdSJean Delvare 	},
47131eb42bdSJean Delvare 	{}
47231eb42bdSJean Delvare };
47331eb42bdSJean Delvare 
wdt_init(void)47446a3949dSAlan Cox static int __init wdt_init(void)
475b7e04f8cSWim Van Sebroeck {
476b7e04f8cSWim Van Sebroeck 	int ret;
477962c04f5SGuenter Roeck 	int chip;
47808b10b57SColin Ian King 	static const char * const chip_name[] = {
479962c04f5SGuenter Roeck 		"W83627HF",
480962c04f5SGuenter Roeck 		"W83627S",
4817b6d0b6aSGuenter Roeck 		"W83697HF",
4827b6d0b6aSGuenter Roeck 		"W83697UG",
483962c04f5SGuenter Roeck 		"W83637HF",
484962c04f5SGuenter Roeck 		"W83627THF",
485962c04f5SGuenter Roeck 		"W83687THF",
486962c04f5SGuenter Roeck 		"W83627EHF",
487962c04f5SGuenter Roeck 		"W83627DHG",
488962c04f5SGuenter Roeck 		"W83627UHG",
489962c04f5SGuenter Roeck 		"W83667HG",
490962c04f5SGuenter Roeck 		"W83667DHG-P",
491962c04f5SGuenter Roeck 		"W83667HG-B",
492962c04f5SGuenter Roeck 		"NCT6775",
493962c04f5SGuenter Roeck 		"NCT6776",
494962c04f5SGuenter Roeck 		"NCT6779",
495a77841d5SGuenter Roeck 		"NCT6791",
496a77841d5SGuenter Roeck 		"NCT6792",
4973a9aedb2SGuenter Roeck 		"NCT6793",
4983a9aedb2SGuenter Roeck 		"NCT6795",
49957cbf0e3SGuenter Roeck 		"NCT6796",
50033f74b89SRob Kramer 		"NCT6102",
501e11cfc69SSrikanth Krishnakar 		"NCT6116",
502962c04f5SGuenter Roeck 	};
503b7e04f8cSWim Van Sebroeck 
50431eb42bdSJean Delvare 	/* Apply system-specific quirks */
50531eb42bdSJean Delvare 	dmi_check_system(wdt_dmi_table);
50631eb42bdSJean Delvare 
507962c04f5SGuenter Roeck 	wdt_io = 0x2e;
508962c04f5SGuenter Roeck 	chip = wdt_find(0x2e);
509962c04f5SGuenter Roeck 	if (chip < 0) {
510962c04f5SGuenter Roeck 		wdt_io = 0x4e;
511962c04f5SGuenter Roeck 		chip = wdt_find(0x4e);
512962c04f5SGuenter Roeck 		if (chip < 0)
513962c04f5SGuenter Roeck 			return chip;
514962c04f5SGuenter Roeck 	}
515962c04f5SGuenter Roeck 
516962c04f5SGuenter Roeck 	pr_info("WDT driver for %s Super I/O chip initialising\n",
517962c04f5SGuenter Roeck 		chip_name[chip]);
518b7e04f8cSWim Van Sebroeck 
51930a83695SGuenter Roeck 	watchdog_init_timeout(&wdt_dev, timeout, NULL);
52030a83695SGuenter Roeck 	watchdog_set_nowayout(&wdt_dev, nowayout);
521d68106bbSDamien Riegel 	watchdog_stop_on_reboot(&wdt_dev);
52230a83695SGuenter Roeck 
523962c04f5SGuenter Roeck 	ret = w83627hf_init(&wdt_dev, chip);
524ef0c1a6bSGuenter Roeck 	if (ret) {
525ef0c1a6bSGuenter Roeck 		pr_err("failed to initialize watchdog (err=%d)\n", ret);
526ef0c1a6bSGuenter Roeck 		return ret;
527ef0c1a6bSGuenter Roeck 	}
528b7e04f8cSWim Van Sebroeck 
52930a83695SGuenter Roeck 	ret = watchdog_register_device(&wdt_dev);
53030a83695SGuenter Roeck 	if (ret)
531d68106bbSDamien Riegel 		return ret;
532b7e04f8cSWim Van Sebroeck 
53327c766aaSJoe Perches 	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
53430a83695SGuenter Roeck 		wdt_dev.timeout, nowayout);
535b7e04f8cSWim Van Sebroeck 
536b7e04f8cSWim Van Sebroeck 	return ret;
537b7e04f8cSWim Van Sebroeck }
538b7e04f8cSWim Van Sebroeck 
wdt_exit(void)53946a3949dSAlan Cox static void __exit wdt_exit(void)
540b7e04f8cSWim Van Sebroeck {
54130a83695SGuenter Roeck 	watchdog_unregister_device(&wdt_dev);
542b7e04f8cSWim Van Sebroeck }
543b7e04f8cSWim Van Sebroeck 
544b7e04f8cSWim Van Sebroeck module_init(wdt_init);
545b7e04f8cSWim Van Sebroeck module_exit(wdt_exit);
546b7e04f8cSWim Van Sebroeck 
547b7e04f8cSWim Van Sebroeck MODULE_LICENSE("GPL");
548d36b6910SAl Viro MODULE_AUTHOR("Pádraig  Brady <P@draigBrady.com>");
549b7e04f8cSWim Van Sebroeck MODULE_DESCRIPTION("w83627hf/thf WDT driver");
550