1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *	intel TCO vendor specific watchdog driver support
4  *
5  *	(c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>.
6  *
7  *	Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
8  *	provide warranty for any of this software. This material is
9  *	provided "AS-IS" and at no charge.
10  */
11 
12 /*
13  *	Includes, defines, variables, module parameters, ...
14  */
15 
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17 
18 /* Module and version information */
19 #define DRV_NAME	"iTCO_vendor_support"
20 #define DRV_VERSION	"1.04"
21 
22 /* Includes */
23 #include <linux/module.h>		/* For module specific items */
24 #include <linux/moduleparam.h>		/* For new moduleparam's */
25 #include <linux/types.h>		/* For standard types (like size_t) */
26 #include <linux/errno.h>		/* For the -ENODEV/... values */
27 #include <linux/kernel.h>		/* For printk/panic/... */
28 #include <linux/init.h>			/* For __init/__exit/... */
29 #include <linux/ioport.h>		/* For io-port access */
30 #include <linux/io.h>			/* For inb/outb/... */
31 
32 #include "iTCO_vendor.h"
33 
34 /* List of vendor support modes */
35 /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
36 #define SUPERMICRO_OLD_BOARD	1
37 /* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems - no longer supported */
38 #define SUPERMICRO_NEW_BOARD	2
39 /* Broken BIOS */
40 #define BROKEN_BIOS		911
41 
42 static int vendorsupport;
43 module_param(vendorsupport, int, 0);
44 MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
45 			"0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS");
46 
47 /*
48  *	Vendor Specific Support
49  */
50 
51 /*
52  *	Vendor Support: 1
53  *	Board: Super Micro Computer Inc. 370SSE+-OEM1/P3TSSE
54  *	iTCO chipset: ICH2
55  *
56  *	Code contributed by: R. Seretny <lkpatches@paypc.com>
57  *	Documentation obtained by R. Seretny from SuperMicro Technical Support
58  *
59  *	To enable Watchdog function:
60  *	    BIOS setup -> Power -> TCO Logic SMI Enable -> Within5Minutes
61  *	    This setting enables SMI to clear the watchdog expired flag.
62  *	    If BIOS or CPU fail which may cause SMI hang, then system will
63  *	    reboot. When application starts to use watchdog function,
64  *	    application has to take over the control from SMI.
65  *
66  *	    For P3TSSE, J36 jumper needs to be removed to enable the Watchdog
67  *	    function.
68  *
69  *	    Note: The system will reboot when Expire Flag is set TWICE.
70  *	    So, if the watchdog timer is 20 seconds, then the maximum hang
71  *	    time is about 40 seconds, and the minimum hang time is about
72  *	    20.6 seconds.
73  */
74 
75 static void supermicro_old_pre_start(struct resource *smires)
76 {
77 	unsigned long val32;
78 
79 	/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
80 	val32 = inl(smires->start);
81 	val32 &= 0xffffdfff;	/* Turn off SMI clearing watchdog */
82 	outl(val32, smires->start);	/* Needed to activate watchdog */
83 }
84 
85 static void supermicro_old_pre_stop(struct resource *smires)
86 {
87 	unsigned long val32;
88 
89 	/* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
90 	val32 = inl(smires->start);
91 	val32 |= 0x00002000;	/* Turn on SMI clearing watchdog */
92 	outl(val32, smires->start);	/* Needed to deactivate watchdog */
93 }
94 
95 /*
96  *	Vendor Support: 911
97  *	Board: Some Intel ICHx based motherboards
98  *	iTCO chipset: ICH7+
99  *
100  *	Some Intel motherboards have a broken BIOS implementation: i.e.
101  *	the SMI handler clear's the TIMEOUT bit in the TC01_STS register
102  *	and does not reload the time. Thus the TCO watchdog does not reboot
103  *	the system.
104  *
105  *	These are the conclusions of Andriy Gapon <avg@icyb.net.ua> after
106  *	debugging: the SMI handler is quite simple - it tests value in
107  *	TCO1_CNT against 0x800, i.e. checks TCO_TMR_HLT. If the bit is set
108  *	the handler goes into an infinite loop, apparently to allow the
109  *	second timeout and reboot. Otherwise it simply clears TIMEOUT bit
110  *	in TCO1_STS and that's it.
111  *	So the logic seems to be reversed, because it is hard to see how
112  *	TIMEOUT can get set to 1 and SMI generated when TCO_TMR_HLT is set
113  *	(other than a transitional effect).
114  *
115  *	The only fix found to get the motherboard(s) to reboot is to put
116  *	the glb_smi_en bit to 0. This is a dirty hack that bypasses the
117  *	broken code by disabling Global SMI.
118  *
119  *	WARNING: globally disabling SMI could possibly lead to dramatic
120  *	problems, especially on laptops! I.e. various ACPI things where
121  *	SMI is used for communication between OS and firmware.
122  *
123  *	Don't use this fix if you don't need to!!!
124  */
125 
126 static void broken_bios_start(struct resource *smires)
127 {
128 	unsigned long val32;
129 
130 	val32 = inl(smires->start);
131 	/* Bit 13: TCO_EN     -> 0 = Disables TCO logic generating an SMI#
132 	   Bit  0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */
133 	val32 &= 0xffffdffe;
134 	outl(val32, smires->start);
135 }
136 
137 static void broken_bios_stop(struct resource *smires)
138 {
139 	unsigned long val32;
140 
141 	val32 = inl(smires->start);
142 	/* Bit 13: TCO_EN     -> 1 = Enables TCO logic generating an SMI#
143 	   Bit  0: GBL_SMI_EN -> 1 = Turn global SMI on again. */
144 	val32 |= 0x00002001;
145 	outl(val32, smires->start);
146 }
147 
148 /*
149  *	Generic Support Functions
150  */
151 
152 void iTCO_vendor_pre_start(struct resource *smires,
153 			   unsigned int heartbeat)
154 {
155 	switch (vendorsupport) {
156 	case SUPERMICRO_OLD_BOARD:
157 		supermicro_old_pre_start(smires);
158 		break;
159 	case BROKEN_BIOS:
160 		broken_bios_start(smires);
161 		break;
162 	}
163 }
164 EXPORT_SYMBOL(iTCO_vendor_pre_start);
165 
166 void iTCO_vendor_pre_stop(struct resource *smires)
167 {
168 	switch (vendorsupport) {
169 	case SUPERMICRO_OLD_BOARD:
170 		supermicro_old_pre_stop(smires);
171 		break;
172 	case BROKEN_BIOS:
173 		broken_bios_stop(smires);
174 		break;
175 	}
176 }
177 EXPORT_SYMBOL(iTCO_vendor_pre_stop);
178 
179 int iTCO_vendor_check_noreboot_on(void)
180 {
181 	switch (vendorsupport) {
182 	case SUPERMICRO_OLD_BOARD:
183 		return 0;
184 	default:
185 		return 1;
186 	}
187 }
188 EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
189 
190 static int __init iTCO_vendor_init_module(void)
191 {
192 	if (vendorsupport == SUPERMICRO_NEW_BOARD) {
193 		pr_warn("Option vendorsupport=%d is no longer supported, "
194 			"please use the w83627hf_wdt driver instead\n",
195 			SUPERMICRO_NEW_BOARD);
196 		return -EINVAL;
197 	}
198 	pr_info("vendor-support=%d\n", vendorsupport);
199 	return 0;
200 }
201 
202 static void __exit iTCO_vendor_exit_module(void)
203 {
204 	pr_info("Module Unloaded\n");
205 }
206 
207 module_init(iTCO_vendor_init_module);
208 module_exit(iTCO_vendor_exit_module);
209 
210 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>, "
211 		"R. Seretny <lkpatches@paypc.com>");
212 MODULE_DESCRIPTION("Intel TCO Vendor Specific WatchDog Timer Driver Support");
213 MODULE_VERSION(DRV_VERSION);
214 MODULE_LICENSE("GPL");
215