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