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