Blame view

drivers/watchdog/iTCO_wdt.c 24.6 KB
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
1
  /*
cb711a193   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
2
   *	intel TCO Watchdog Driver
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
3
   *
12d60e28b   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
4
   *	(c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>.
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
5
6
7
8
9
10
11
12
13
14
15
16
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   *	Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
   *	provide warranty for any of this software. This material is
   *	provided "AS-IS" and at no charge.
   *
   *	The TCO watchdog is implemented in the following I/O controller hubs:
   *	(See the intel documentation on http://developer.intel.com.)
cb711a193   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   *	document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
   *	document number 290687-002, 298242-027: 82801BA (ICH2)
   *	document number 290733-003, 290739-013: 82801CA (ICH3-S)
   *	document number 290716-001, 290718-007: 82801CAM (ICH3-M)
   *	document number 290744-001, 290745-025: 82801DB (ICH4)
   *	document number 252337-001, 252663-008: 82801DBM (ICH4-M)
   *	document number 273599-001, 273645-002: 82801E (C-ICH)
   *	document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
   *	document number 300641-004, 300884-013: 6300ESB
   *	document number 301473-002, 301474-026: 82801F (ICH6)
   *	document number 313082-001, 313075-006: 631xESB, 632xESB
   *	document number 307013-003, 307014-024: 82801G (ICH7)
   *	document number 313056-003, 313057-017: 82801H (ICH8)
   *	document number 316972-004, 316973-012: 82801I (ICH9)
   *	document number 319973-002, 319974-002: 82801J (ICH10)
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
32
   *	document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
4946f8353   Imre Kaloz   [WATCHDOG] iTCO_w...
33
   *	document number 320066-003, 320257-008: EP80597 (IICH)
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
34
   *	document number TBD                   : Cougar Point (CPT)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
35
36
37
38
39
40
41
   */
  
  /*
   *	Includes, defines, variables, module parameters, ...
   */
  
  /* Module and version information */
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
42
  #define DRV_NAME	"iTCO_wdt"
12d60e28b   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
43
  #define DRV_VERSION	"1.05"
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
44
45
46
  #define PFX		DRV_NAME ": "
  
  /* Includes */
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
47
48
49
50
51
  #include <linux/module.h>		/* For module specific items */
  #include <linux/moduleparam.h>		/* For new moduleparam's */
  #include <linux/types.h>		/* For standard types (like size_t) */
  #include <linux/errno.h>		/* For the -ENODEV/... values */
  #include <linux/kernel.h>		/* For printk/panic/... */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
52
53
  #include <linux/miscdevice.h>		/* For MODULE_ALIAS_MISCDEV
  							(WATCHDOG_MINOR) */
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
54
  #include <linux/watchdog.h>		/* For the watchdog specific items */
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
55
56
57
58
59
60
  #include <linux/init.h>			/* For __init/__exit/... */
  #include <linux/fs.h>			/* For file operations */
  #include <linux/platform_device.h>	/* For platform_driver framework */
  #include <linux/pci.h>			/* For pci functions */
  #include <linux/ioport.h>		/* For io-port access */
  #include <linux/spinlock.h>		/* For spin_lock/spin_unlock/... */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
61
62
  #include <linux/uaccess.h>		/* For copy_to_user/put_user/... */
  #include <linux/io.h>			/* For inb/outb/... */
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
63

0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
64
  #include "iTCO_vendor.h"
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  
  /* TCO related info */
  enum iTCO_chipsets {
  	TCO_ICH = 0,	/* ICH */
  	TCO_ICH0,	/* ICH0 */
  	TCO_ICH2,	/* ICH2 */
  	TCO_ICH2M,	/* ICH2-M */
  	TCO_ICH3,	/* ICH3-S */
  	TCO_ICH3M,	/* ICH3-M */
  	TCO_ICH4,	/* ICH4 */
  	TCO_ICH4M,	/* ICH4-M */
  	TCO_CICH,	/* C-ICH */
  	TCO_ICH5,	/* ICH5 & ICH5R */
  	TCO_6300ESB,	/* 6300ESB */
  	TCO_ICH6,	/* ICH6 & ICH6R */
  	TCO_ICH6M,	/* ICH6-M */
  	TCO_ICH6W,	/* ICH6W & ICH6RW */
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
82
  	TCO_631XESB,	/* 631xESB/632xESB */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
83
  	TCO_ICH7,	/* ICH7 & ICH7R */
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
84
85
  	TCO_ICH7DH,	/* ICH7DH */
  	TCO_ICH7M,	/* ICH7-M & ICH7-U */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
86
  	TCO_ICH7MDH,	/* ICH7-M DH */
a8edd74e4   Wim Van Sebroeck   [WATCHDOG] add ic...
87
88
89
  	TCO_ICH8,	/* ICH8 & ICH8R */
  	TCO_ICH8DH,	/* ICH8DH */
  	TCO_ICH8DO,	/* ICH8DO */
acf603513   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
90
  	TCO_ICH8M,	/* ICH8M */
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
91
  	TCO_ICH8ME,	/* ICH8M-E */
286201dca   Wim Van Sebroeck   [WATCHDOG] ICH9 s...
92
93
94
  	TCO_ICH9,	/* ICH9 */
  	TCO_ICH9R,	/* ICH9R */
  	TCO_ICH9DH,	/* ICH9DH */
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
95
  	TCO_ICH9DO,	/* ICH9DO */
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
96
97
98
99
100
101
  	TCO_ICH9M,	/* ICH9M */
  	TCO_ICH9ME,	/* ICH9M-E */
  	TCO_ICH10,	/* ICH10 */
  	TCO_ICH10R,	/* ICH10R */
  	TCO_ICH10D,	/* ICH10D */
  	TCO_ICH10DO,	/* ICH10DO */
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
102
103
  	TCO_PCH,	/* PCH Desktop Full Featured */
  	TCO_PCHM,	/* PCH Mobile Full Featured */
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
104
105
106
107
108
109
110
111
  	TCO_P55,	/* P55 */
  	TCO_PM55,	/* PM55 */
  	TCO_H55,	/* H55 */
  	TCO_QM57,	/* QM57 */
  	TCO_H57,	/* H57 */
  	TCO_HM55,	/* HM55 */
  	TCO_Q57,	/* Q57 */
  	TCO_HM57,	/* HM57 */
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
112
  	TCO_PCHMSFF,	/* PCH Mobile SFF Full Featured */
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
113
114
115
116
  	TCO_QS57,	/* QS57 */
  	TCO_3400,	/* 3400 */
  	TCO_3420,	/* 3420 */
  	TCO_3450,	/* 3450 */
4946f8353   Imre Kaloz   [WATCHDOG] iTCO_w...
117
  	TCO_EP80579,	/* EP80579 */
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
118
119
  	TCO_CPTD,	/* CPT Desktop */
  	TCO_CPTM,	/* CPT Mobile */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  };
  
  static struct {
  	char *name;
  	unsigned int iTCO_version;
  } iTCO_chipset_info[] __devinitdata = {
  	{"ICH", 1},
  	{"ICH0", 1},
  	{"ICH2", 1},
  	{"ICH2-M", 1},
  	{"ICH3-S", 1},
  	{"ICH3-M", 1},
  	{"ICH4", 1},
  	{"ICH4-M", 1},
  	{"C-ICH", 1},
  	{"ICH5 or ICH5R", 1},
  	{"6300ESB", 1},
  	{"ICH6 or ICH6R", 2},
  	{"ICH6-M", 2},
  	{"ICH6W or ICH6RW", 2},
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
140
  	{"631xESB/632xESB", 2},
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
141
  	{"ICH7 or ICH7R", 2},
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
142
143
  	{"ICH7DH", 2},
  	{"ICH7-M or ICH7-U", 2},
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
144
  	{"ICH7-M DH", 2},
bcbf25bd0   Arnaud Patard   [WATCHDOG] add ic...
145
  	{"ICH8 or ICH8R", 2},
a8edd74e4   Wim Van Sebroeck   [WATCHDOG] add ic...
146
147
  	{"ICH8DH", 2},
  	{"ICH8DO", 2},
acf603513   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
148
  	{"ICH8M", 2},
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
149
  	{"ICH8M-E", 2},
286201dca   Wim Van Sebroeck   [WATCHDOG] ICH9 s...
150
151
152
  	{"ICH9", 2},
  	{"ICH9R", 2},
  	{"ICH9DH", 2},
a49056da0   Gabriel C   [WATCHDOG] Add IC...
153
  	{"ICH9DO", 2},
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
154
155
156
157
158
159
  	{"ICH9M", 2},
  	{"ICH9M-E", 2},
  	{"ICH10", 2},
  	{"ICH10R", 2},
  	{"ICH10D", 2},
  	{"ICH10DO", 2},
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
160
161
  	{"PCH Desktop Full Featured", 2},
  	{"PCH Mobile Full Featured", 2},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
162
163
164
165
166
167
168
169
  	{"P55", 2},
  	{"PM55", 2},
  	{"H55", 2},
  	{"QM57", 2},
  	{"H57", 2},
  	{"HM55", 2},
  	{"Q57", 2},
  	{"HM57", 2},
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
170
  	{"PCH Mobile SFF Full Featured", 2},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
171
172
173
174
  	{"QS57", 2},
  	{"3400", 2},
  	{"3420", 2},
  	{"3450", 2},
4946f8353   Imre Kaloz   [WATCHDOG] iTCO_w...
175
  	{"EP80579", 2},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
176
177
  	{"CPT Desktop", 2},
  	{"CPT Mobile", 2},
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
178
  	{NULL, 0}
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
179
  };
c87b639a2   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
180
181
182
183
184
185
186
187
  #define ITCO_PCI_DEVICE(dev, data) 	\
  	.vendor = PCI_VENDOR_ID_INTEL,	\
  	.device = dev,			\
  	.subvendor = PCI_ANY_ID,	\
  	.subdevice = PCI_ANY_ID,	\
  	.class = 0,			\
  	.class_mask = 0,		\
  	.driver_data = data
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
188
189
190
191
192
193
194
  /*
   * This data only exists for exporting the supported PCI ids
   * via MODULE_DEVICE_TABLE.  We do not actually register a
   * pci_driver, because the I/O Controller Hub has also other
   * functions that probably will be registered by other drivers.
   */
  static struct pci_device_id iTCO_wdt_pci_tbl[] = {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
195
196
197
198
199
200
201
202
203
204
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0,	TCO_ICH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0,	TCO_ICH0)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0,	TCO_ICH2)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10,	TCO_ICH2M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0,	TCO_ICH3)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12,	TCO_ICH3M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0,	TCO_ICH4)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12,	TCO_ICH4M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0,		TCO_CICH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0,	TCO_ICH5)},
c87b639a2   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
205
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1,		TCO_6300ESB)},
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
206
207
208
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0,		TCO_ICH6)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1,		TCO_ICH6M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2,		TCO_ICH6W)},
c87b639a2   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0,		TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2671,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2672,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2673,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2674,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2675,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2676,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2677,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2678,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x2679,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267a,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267b,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267c,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267d,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267e,				TCO_631XESB)},
  	{ ITCO_PCI_DEVICE(0x267f,				TCO_631XESB)},
28d41f53f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0,		TCO_ICH7)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30,		TCO_ICH7DH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1,		TCO_ICH7M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31,		TCO_ICH7MDH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0,		TCO_ICH8)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2,		TCO_ICH8DH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3,		TCO_ICH8DO)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4,		TCO_ICH8M)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1,		TCO_ICH8ME)},
  	{ ITCO_PCI_DEVICE(0x2918,				TCO_ICH9)},
  	{ ITCO_PCI_DEVICE(0x2916,				TCO_ICH9R)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2,		TCO_ICH9DH)},
  	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4,		TCO_ICH9DO)},
  	{ ITCO_PCI_DEVICE(0x2919,				TCO_ICH9M)},
  	{ ITCO_PCI_DEVICE(0x2917,				TCO_ICH9ME)},
  	{ ITCO_PCI_DEVICE(0x3a18,				TCO_ICH10)},
  	{ ITCO_PCI_DEVICE(0x3a16,				TCO_ICH10R)},
  	{ ITCO_PCI_DEVICE(0x3a1a,				TCO_ICH10D)},
  	{ ITCO_PCI_DEVICE(0x3a14,				TCO_ICH10DO)},
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
244
245
  	{ ITCO_PCI_DEVICE(0x3b00,				TCO_PCH)},
  	{ ITCO_PCI_DEVICE(0x3b01,				TCO_PCHM)},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
246
247
248
249
250
251
252
253
  	{ ITCO_PCI_DEVICE(0x3b02,				TCO_P55)},
  	{ ITCO_PCI_DEVICE(0x3b03,				TCO_PM55)},
  	{ ITCO_PCI_DEVICE(0x3b06,				TCO_H55)},
  	{ ITCO_PCI_DEVICE(0x3b07,				TCO_QM57)},
  	{ ITCO_PCI_DEVICE(0x3b08,				TCO_H57)},
  	{ ITCO_PCI_DEVICE(0x3b09,				TCO_HM55)},
  	{ ITCO_PCI_DEVICE(0x3b0a,				TCO_Q57)},
  	{ ITCO_PCI_DEVICE(0x3b0b,				TCO_HM57)},
79e8941dd   Seth Heasley   [WATCHDOG] iTCO_w...
254
  	{ ITCO_PCI_DEVICE(0x3b0d,				TCO_PCHMSFF)},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
255
256
257
258
  	{ ITCO_PCI_DEVICE(0x3b0f,				TCO_QS57)},
  	{ ITCO_PCI_DEVICE(0x3b12,				TCO_3400)},
  	{ ITCO_PCI_DEVICE(0x3b14,				TCO_3420)},
  	{ ITCO_PCI_DEVICE(0x3b16,				TCO_3450)},
4946f8353   Imre Kaloz   [WATCHDOG] iTCO_w...
259
  	{ ITCO_PCI_DEVICE(0x5031,				TCO_EP80579)},
3c9d8eccd   Seth Heasley   [WATCHDOG] iTCO_w...
260
261
  	{ ITCO_PCI_DEVICE(0x1c42,				TCO_CPTD)},
  	{ ITCO_PCI_DEVICE(0x1c43,				TCO_CPTM)},
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
262
263
  	{ 0, },			/* End of list */
  };
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
264
  MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
265
266
  
  /* Address definitions for the TCO */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
267
  /* TCO base address */
0a7e65822   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
268
  #define TCOBASE		(iTCO_wdt_private.ACPIBASE + 0x60)
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
269
  /* SMI Control and Enable Register */
0a7e65822   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
270
  #define SMI_EN		(iTCO_wdt_private.ACPIBASE + 0x30)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
271

0a7e65822   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
272
273
274
275
276
277
278
279
280
  #define TCO_RLD		(TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
  #define TCOv1_TMR	(TCOBASE + 0x01) /* TCOv1 Timer Initial Value	*/
  #define TCO_DAT_IN	(TCOBASE + 0x02) /* TCO Data In Register	*/
  #define TCO_DAT_OUT	(TCOBASE + 0x03) /* TCO Data Out Register	*/
  #define TCO1_STS	(TCOBASE + 0x04) /* TCO1 Status Register	*/
  #define TCO2_STS	(TCOBASE + 0x06) /* TCO2 Status Register	*/
  #define TCO1_CNT	(TCOBASE + 0x08) /* TCO1 Control Register	*/
  #define TCO2_CNT	(TCOBASE + 0x0a) /* TCO2 Control Register	*/
  #define TCOv2_TMR	(TCOBASE + 0x12) /* TCOv2 Timer Initial Value	*/
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
281
282
283
284
  
  /* internal variables */
  static unsigned long is_active;
  static char expect_release;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
285
286
287
288
289
290
291
292
293
294
295
  static struct {		/* this is private data for the iTCO_wdt device */
  	/* TCO version/generation */
  	unsigned int iTCO_version;
  	/* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
  	unsigned long ACPIBASE;
  	/* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
  	unsigned long __iomem *gcs;
  	/* the lock for io operations */
  	spinlock_t io_lock;
  	/* the PCI-device */
  	struct pci_dev *pdev;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
296
  } iTCO_wdt_private;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
297
298
  /* the watchdog platform device */
  static struct platform_device *iTCO_wdt_platform_device;
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
299

9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
300
301
302
303
  /* module parameters */
  #define WATCHDOG_HEARTBEAT 30	/* 30 sec default heartbeat */
  static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
  module_param(heartbeat, int, 0);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
304
305
306
  MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
  	"(2<heartbeat<39 (TCO v1) or 613 (TCO v2), default="
  				__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
307
308
309
  
  static int nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, int, 0);
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
310
311
312
  MODULE_PARM_DESC(nowayout,
  	"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
313

9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
  /*
   * Some TCO specific functions
   */
  
  static inline unsigned int seconds_to_ticks(int seconds)
  {
  	/* the internal timer is stored as ticks which decrement
  	 * every 0.6 seconds */
  	return (seconds * 10) / 6;
  }
  
  static void iTCO_wdt_set_NO_REBOOT_bit(void)
  {
  	u32 val32;
  
  	/* Set the NO_REBOOT bit: this disables reboots */
  	if (iTCO_wdt_private.iTCO_version == 2) {
  		val32 = readl(iTCO_wdt_private.gcs);
  		val32 |= 0x00000020;
  		writel(val32, iTCO_wdt_private.gcs);
  	} else if (iTCO_wdt_private.iTCO_version == 1) {
  		pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
  		val32 |= 0x00000002;
  		pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
  	}
  }
  
  static int iTCO_wdt_unset_NO_REBOOT_bit(void)
  {
  	int ret = 0;
  	u32 val32;
  
  	/* Unset the NO_REBOOT bit: this enables reboots */
  	if (iTCO_wdt_private.iTCO_version == 2) {
  		val32 = readl(iTCO_wdt_private.gcs);
  		val32 &= 0xffffffdf;
  		writel(val32, iTCO_wdt_private.gcs);
  
  		val32 = readl(iTCO_wdt_private.gcs);
  		if (val32 & 0x00000020)
  			ret = -EIO;
  	} else if (iTCO_wdt_private.iTCO_version == 1) {
  		pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
  		val32 &= 0xfffffffd;
  		pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
  
  		pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
  		if (val32 & 0x00000002)
  			ret = -EIO;
  	}
  
  	return ret; /* returns: 0 = OK, -EIO = Error */
  }
  
  static int iTCO_wdt_start(void)
  {
  	unsigned int val;
  
  	spin_lock(&iTCO_wdt_private.io_lock);
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
373
  	iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
374
375
  	/* disable chipset's NO_REBOOT bit */
  	if (iTCO_wdt_unset_NO_REBOOT_bit()) {
2ba7d7b39   Roel Kluin   [WATCHDOG] Unlock...
376
  		spin_unlock(&iTCO_wdt_private.io_lock);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
377
378
379
  		printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
  					"reboot disabled by hardware
  ");
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
380
381
  		return -EIO;
  	}
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
382
383
384
385
386
387
  	/* Force the timer to its reload value by writing to the TCO_RLD
  	   register */
  	if (iTCO_wdt_private.iTCO_version == 2)
  		outw(0x01, TCO_RLD);
  	else if (iTCO_wdt_private.iTCO_version == 1)
  		outb(0x01, TCO_RLD);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  	/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
  	val = inw(TCO1_CNT);
  	val &= 0xf7ff;
  	outw(val, TCO1_CNT);
  	val = inw(TCO1_CNT);
  	spin_unlock(&iTCO_wdt_private.io_lock);
  
  	if (val & 0x0800)
  		return -1;
  	return 0;
  }
  
  static int iTCO_wdt_stop(void)
  {
  	unsigned int val;
  
  	spin_lock(&iTCO_wdt_private.io_lock);
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
405
  	iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  	/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
  	val = inw(TCO1_CNT);
  	val |= 0x0800;
  	outw(val, TCO1_CNT);
  	val = inw(TCO1_CNT);
  
  	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
  	iTCO_wdt_set_NO_REBOOT_bit();
  
  	spin_unlock(&iTCO_wdt_private.io_lock);
  
  	if ((val & 0x0800) == 0)
  		return -1;
  	return 0;
  }
  
  static int iTCO_wdt_keepalive(void)
  {
  	spin_lock(&iTCO_wdt_private.io_lock);
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
425
  	iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
426
  	/* Reload the timer by writing to the TCO Timer Counter register */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
427
  	if (iTCO_wdt_private.iTCO_version == 2)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
428
  		outw(0x01, TCO_RLD);
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
429
  	else if (iTCO_wdt_private.iTCO_version == 1)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
430
  		outb(0x01, TCO_RLD);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
  
  	spin_unlock(&iTCO_wdt_private.io_lock);
  	return 0;
  }
  
  static int iTCO_wdt_set_heartbeat(int t)
  {
  	unsigned int val16;
  	unsigned char val8;
  	unsigned int tmrval;
  
  	tmrval = seconds_to_ticks(t);
  	/* from the specs: */
  	/* "Values of 0h-3h are ignored and should not be attempted" */
  	if (tmrval < 0x04)
  		return -EINVAL;
  	if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
  	    ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
  		return -EINVAL;
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
450
  	iTCO_vendor_pre_set_heartbeat(tmrval);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  	/* Write new heartbeat to watchdog */
  	if (iTCO_wdt_private.iTCO_version == 2) {
  		spin_lock(&iTCO_wdt_private.io_lock);
  		val16 = inw(TCOv2_TMR);
  		val16 &= 0xfc00;
  		val16 |= tmrval;
  		outw(val16, TCOv2_TMR);
  		val16 = inw(TCOv2_TMR);
  		spin_unlock(&iTCO_wdt_private.io_lock);
  
  		if ((val16 & 0x3ff) != tmrval)
  			return -EINVAL;
  	} else if (iTCO_wdt_private.iTCO_version == 1) {
  		spin_lock(&iTCO_wdt_private.io_lock);
  		val8 = inb(TCOv1_TMR);
  		val8 &= 0xc0;
  		val8 |= (tmrval & 0xff);
  		outb(val8, TCOv1_TMR);
  		val8 = inb(TCOv1_TMR);
  		spin_unlock(&iTCO_wdt_private.io_lock);
  
  		if ((val8 & 0x3f) != tmrval)
  			return -EINVAL;
  	}
  
  	heartbeat = t;
  	return 0;
  }
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
479
  static int iTCO_wdt_get_timeleft(int *time_left)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  {
  	unsigned int val16;
  	unsigned char val8;
  
  	/* read the TCO Timer */
  	if (iTCO_wdt_private.iTCO_version == 2) {
  		spin_lock(&iTCO_wdt_private.io_lock);
  		val16 = inw(TCO_RLD);
  		val16 &= 0x3ff;
  		spin_unlock(&iTCO_wdt_private.io_lock);
  
  		*time_left = (val16 * 6) / 10;
  	} else if (iTCO_wdt_private.iTCO_version == 1) {
  		spin_lock(&iTCO_wdt_private.io_lock);
  		val8 = inb(TCO_RLD);
  		val8 &= 0x3f;
  		spin_unlock(&iTCO_wdt_private.io_lock);
  
  		*time_left = (val8 * 6) / 10;
80060362a   Jeff Garzik   [WATCHDOG] watchd...
499
500
  	} else
  		return -EINVAL;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
501
502
503
504
505
506
  	return 0;
  }
  
  /*
   *	/dev/watchdog handling
   */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
507
  static int iTCO_wdt_open(struct inode *inode, struct file *file)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
508
509
510
511
512
513
514
515
  {
  	/* /dev/watchdog can only be opened once */
  	if (test_and_set_bit(0, &is_active))
  		return -EBUSY;
  
  	/*
  	 *      Reload and activate timer
  	 */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
516
517
518
  	iTCO_wdt_start();
  	return nonseekable_open(inode, file);
  }
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
519
  static int iTCO_wdt_release(struct inode *inode, struct file *file)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
520
521
522
523
524
525
526
  {
  	/*
  	 *      Shut off the timer.
  	 */
  	if (expect_release == 42) {
  		iTCO_wdt_stop();
  	} else {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
527
528
529
  		printk(KERN_CRIT PFX
  			"Unexpected close, not stopping watchdog!
  ");
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
530
531
532
533
534
535
  		iTCO_wdt_keepalive();
  	}
  	clear_bit(0, &is_active);
  	expect_release = 0;
  	return 0;
  }
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
536
537
  static ssize_t iTCO_wdt_write(struct file *file, const char __user *data,
  			      size_t len, loff_t *ppos)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
538
539
540
541
542
  {
  	/* See if we got the magic character 'V' and reload the timer */
  	if (len) {
  		if (!nowayout) {
  			size_t i;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
543
544
  			/* note: just in case someone wrote the magic
  			   character five months ago... */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
545
  			expect_release = 0;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
546
547
  			/* scan to see whether or not we got the
  			   magic character */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
548
549
  			for (i = 0; i != len; i++) {
  				char c;
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
550
  				if (get_user(c, data + i))
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
551
552
553
554
555
556
557
558
559
560
561
  					return -EFAULT;
  				if (c == 'V')
  					expect_release = 42;
  			}
  		}
  
  		/* someone wrote to us, we should reload the timer */
  		iTCO_wdt_keepalive();
  	}
  	return len;
  }
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
562
563
  static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
  							unsigned long arg)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
564
565
566
  {
  	int new_options, retval = -EINVAL;
  	int new_heartbeat;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
567
568
569
570
571
572
573
574
575
576
577
  	void __user *argp = (void __user *)arg;
  	int __user *p = argp;
  	static struct watchdog_info ident = {
  		.options =		WDIOF_SETTIMEOUT |
  					WDIOF_KEEPALIVEPING |
  					WDIOF_MAGICCLOSE,
  		.firmware_version =	0,
  		.identity =		DRV_NAME,
  	};
  
  	switch (cmd) {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
578
579
580
581
582
  	case WDIOC_GETSUPPORT:
  		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
  		return put_user(0, p);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
583

0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
584
585
586
587
  	case WDIOC_SETOPTIONS:
  	{
  		if (get_user(new_options, p))
  			return -EFAULT;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
588

0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
589
590
591
  		if (new_options & WDIOS_DISABLECARD) {
  			iTCO_wdt_stop();
  			retval = 0;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
592
  		}
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
593
  		if (new_options & WDIOS_ENABLECARD) {
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
594
  			iTCO_wdt_keepalive();
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
595
596
  			iTCO_wdt_start();
  			retval = 0;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
597
  		}
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
598
599
  		return retval;
  	}
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
600
601
602
  	case WDIOC_KEEPALIVE:
  		iTCO_wdt_keepalive();
  		return 0;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
  	case WDIOC_SETTIMEOUT:
  	{
  		if (get_user(new_heartbeat, p))
  			return -EFAULT;
  		if (iTCO_wdt_set_heartbeat(new_heartbeat))
  			return -EINVAL;
  		iTCO_wdt_keepalive();
  		/* Fall */
  	}
  	case WDIOC_GETTIMEOUT:
  		return put_user(heartbeat, p);
  	case WDIOC_GETTIMELEFT:
  	{
  		int time_left;
  		if (iTCO_wdt_get_timeleft(&time_left))
  			return -EINVAL;
  		return put_user(time_left, p);
  	}
  	default:
  		return -ENOTTY;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
623
624
625
626
  	}
  }
  
  /*
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
627
628
   *	Kernel Interfaces
   */
2b8693c06   Arjan van de Ven   [PATCH] mark stru...
629
  static const struct file_operations iTCO_wdt_fops = {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
630
631
632
633
634
635
  	.owner =		THIS_MODULE,
  	.llseek =		no_llseek,
  	.write =		iTCO_wdt_write,
  	.unlocked_ioctl =	iTCO_wdt_ioctl,
  	.open =			iTCO_wdt_open,
  	.release =		iTCO_wdt_release,
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
636
637
638
639
640
641
642
  };
  
  static struct miscdevice iTCO_wdt_miscdev = {
  	.minor =	WATCHDOG_MINOR,
  	.name =		"watchdog",
  	.fops =		&iTCO_wdt_fops,
  };
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
643
644
645
  /*
   *	Init & exit routines
   */
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
646
647
  static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
  		const struct pci_device_id *ent, struct platform_device *dev)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
648
649
650
651
  {
  	int ret;
  	u32 base_address;
  	unsigned long RCBA;
12d60e28b   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
652
  	unsigned long val32;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
653
654
655
656
657
658
659
  
  	/*
  	 *      Find the ACPI/PM base I/O address which is the base
  	 *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
  	 *      ACPIBASE is bits [15:7] from 0x40-0x43
  	 */
  	pci_read_config_dword(pdev, 0x40, &base_address);
0d4804b31   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
660
  	base_address &= 0x0000ff80;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
661
662
663
664
  	if (base_address == 0x00000000) {
  		/* Something's wrong here, ACPIBASE has to be set */
  		printk(KERN_ERR PFX "failed to get TCOBASE address
  ");
4802c6533   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
665
  		pci_dev_put(pdev);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
666
667
  		return -ENODEV;
  	}
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
668
669
  	iTCO_wdt_private.iTCO_version =
  			iTCO_chipset_info[ent->driver_data].iTCO_version;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
670
671
  	iTCO_wdt_private.ACPIBASE = base_address;
  	iTCO_wdt_private.pdev = pdev;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
672
673
674
675
  	/* Get the Memory-Mapped GCS register, we need it for the
  	   NO_REBOOT flag (TCO v2). To get access to it you have to
  	   read RCBA from PCI Config space 0xf0 and use it as base.
  	   GCS = RCBA + ICH6_GCS(0x3410). */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
676
677
  	if (iTCO_wdt_private.iTCO_version == 2) {
  		pci_read_config_dword(pdev, 0xf0, &base_address);
de8cd9a30   Denis V. Lunev   [WATCHDOG] iTCO_w...
678
679
680
681
682
683
  		if ((base_address & 1) == 0) {
  			printk(KERN_ERR PFX "RCBA is disabled by harddware
  ");
  			ret = -ENODEV;
  			goto out;
  		}
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
684
  		RCBA = base_address & 0xffffc000;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
685
  		iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
686
687
688
  	}
  
  	/* Check chipset's NO_REBOOT bit */
e033351d5   Wim Van Sebroeck   [WATCHDOG] Add iT...
689
  	if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
690
691
692
  		printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
  					"reboot disabled by hardware
  ");
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
693
  		ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
de8cd9a30   Denis V. Lunev   [WATCHDOG] iTCO_w...
694
  		goto out_unmap;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
695
696
697
698
  	}
  
  	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
  	iTCO_wdt_set_NO_REBOOT_bit();
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
699
  	/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
700
  	if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
701
702
703
  		printk(KERN_ERR PFX
  			"I/O address 0x%04lx already in use
  ", SMI_EN);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
704
  		ret = -EIO;
de8cd9a30   Denis V. Lunev   [WATCHDOG] iTCO_w...
705
  		goto out_unmap;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
706
  	}
12d60e28b   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
707
708
709
710
  	/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
  	val32 = inl(SMI_EN);
  	val32 &= 0xffffdfff;	/* Turn off SMI clearing watchdog */
  	outl(val32, SMI_EN);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
711

0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
712
713
714
715
716
  	/* The TCO I/O registers reside in a 32-byte range pointed to
  	   by the TCOBASE value */
  	if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
  		printk(KERN_ERR PFX "I/O address 0x%04lx already in use
  ",
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
717
718
  			TCOBASE);
  		ret = -EIO;
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
719
  		goto unreg_smi_en;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
720
  	}
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
721
722
723
724
725
726
  	printk(KERN_INFO PFX
  		"Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)
  ",
  			iTCO_chipset_info[ent->driver_data].name,
  			iTCO_chipset_info[ent->driver_data].iTCO_version,
  			TCOBASE);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
727
728
  
  	/* Clear out the (probably old) status */
c6904ddb9   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
729
730
731
  	outb(8, TCO1_STS);	/* Clear the Time Out Status bit */
  	outb(2, TCO2_STS);	/* Clear SECOND_TO_STS bit */
  	outb(4, TCO2_STS);	/* Clear BOOT_STS bit */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
732
733
734
  
  	/* Make sure the watchdog is not running */
  	iTCO_wdt_stop();
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
735
736
  	/* Check that the heartbeat value is within it's range;
  	   if not reset to the default */
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
737
738
  	if (iTCO_wdt_set_heartbeat(heartbeat)) {
  		iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
739
740
741
742
  		printk(KERN_INFO PFX
  			"heartbeat value must be 2 < heartbeat < 39 (TCO v1) "
  				"or 613 (TCO v2), using %d
  ", heartbeat);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
743
  	}
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
744
745
  	ret = misc_register(&iTCO_wdt_miscdev);
  	if (ret != 0) {
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
746
747
748
749
  		printk(KERN_ERR PFX
  			"cannot register miscdev on minor=%d (err=%d)
  ",
  							WATCHDOG_MINOR, ret);
1bef84bea   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
750
  		goto unreg_region;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
751
  	}
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
752
753
754
  	printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)
  ",
  							heartbeat, nowayout);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
755
756
  
  	return 0;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
757
  unreg_region:
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
758
  	release_region(TCOBASE, 0x20);
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
759
760
  unreg_smi_en:
  	release_region(SMI_EN, 4);
de8cd9a30   Denis V. Lunev   [WATCHDOG] iTCO_w...
761
  out_unmap:
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
762
763
  	if (iTCO_wdt_private.iTCO_version == 2)
  		iounmap(iTCO_wdt_private.gcs);
de8cd9a30   Denis V. Lunev   [WATCHDOG] iTCO_w...
764
  out:
4802c6533   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
765
  	pci_dev_put(iTCO_wdt_private.pdev);
1bef84bea   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
766
  	iTCO_wdt_private.ACPIBASE = 0;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
767
768
  	return ret;
  }
08113e39d   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
769
  static void __devexit iTCO_wdt_cleanup(void)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
770
771
772
773
774
775
776
  {
  	/* Stop the timer before we leave */
  	if (!nowayout)
  		iTCO_wdt_stop();
  
  	/* Deregister */
  	misc_deregister(&iTCO_wdt_miscdev);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
777
  	release_region(TCOBASE, 0x20);
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
778
  	release_region(SMI_EN, 4);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
779
780
  	if (iTCO_wdt_private.iTCO_version == 2)
  		iounmap(iTCO_wdt_private.gcs);
4802c6533   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
781
  	pci_dev_put(iTCO_wdt_private.pdev);
1bef84bea   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
782
  	iTCO_wdt_private.ACPIBASE = 0;
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
783
  }
08113e39d   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
784
  static int __devinit iTCO_wdt_probe(struct platform_device *dev)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
785
786
787
788
789
790
791
792
793
794
  {
  	int found = 0;
  	struct pci_dev *pdev = NULL;
  	const struct pci_device_id *ent;
  
  	spin_lock_init(&iTCO_wdt_private.io_lock);
  
  	for_each_pci_dev(pdev) {
  		ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
  		if (ent) {
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
795
  			if (!(iTCO_wdt_init(pdev, ent, dev))) {
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
796
797
798
799
800
801
802
803
804
805
806
807
808
809
  				found++;
  				break;
  			}
  		}
  	}
  
  	if (!found) {
  		printk(KERN_INFO PFX "No card detected
  ");
  		return -ENODEV;
  	}
  
  	return 0;
  }
08113e39d   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
810
  static int __devexit iTCO_wdt_remove(struct platform_device *dev)
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
811
812
813
  {
  	if (iTCO_wdt_private.ACPIBASE)
  		iTCO_wdt_cleanup();
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
814
815
816
817
818
819
820
821
822
823
824
825
826
  	return 0;
  }
  
  static void iTCO_wdt_shutdown(struct platform_device *dev)
  {
  	iTCO_wdt_stop();
  }
  
  #define iTCO_wdt_suspend NULL
  #define iTCO_wdt_resume  NULL
  
  static struct platform_driver iTCO_wdt_driver = {
  	.probe          = iTCO_wdt_probe,
08113e39d   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
827
  	.remove         = __devexit_p(iTCO_wdt_remove),
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
828
829
830
831
832
833
834
835
836
837
838
839
  	.shutdown       = iTCO_wdt_shutdown,
  	.suspend        = iTCO_wdt_suspend,
  	.resume         = iTCO_wdt_resume,
  	.driver         = {
  		.owner  = THIS_MODULE,
  		.name   = DRV_NAME,
  	},
  };
  
  static int __init iTCO_wdt_init_module(void)
  {
  	int err;
7cd5b08be   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
840
841
842
  	printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s
  ",
  		DRV_VERSION);
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
843
844
845
846
  
  	err = platform_driver_register(&iTCO_wdt_driver);
  	if (err)
  		return err;
0e6fa3fb3   Alan Cox   [WATCHDOG 18/57] ...
847
848
  	iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
  								-1, NULL, 0);
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
  	if (IS_ERR(iTCO_wdt_platform_device)) {
  		err = PTR_ERR(iTCO_wdt_platform_device);
  		goto unreg_platform_driver;
  	}
  
  	return 0;
  
  unreg_platform_driver:
  	platform_driver_unregister(&iTCO_wdt_driver);
  	return err;
  }
  
  static void __exit iTCO_wdt_cleanup_module(void)
  {
  	platform_device_unregister(iTCO_wdt_platform_device);
  	platform_driver_unregister(&iTCO_wdt_driver);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
865
866
867
868
869
870
871
872
873
  	printk(KERN_INFO PFX "Watchdog Module Unloaded.
  ");
  }
  
  module_init(iTCO_wdt_init_module);
  module_exit(iTCO_wdt_cleanup_module);
  
  MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
  MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
3836cc0ff   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
874
  MODULE_VERSION(DRV_VERSION);
9e0ea345f   Wim Van Sebroeck   [WATCHDOG] iTCO_w...
875
876
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);