xref: /dflybsd-src/sys/dev/misc/ichwd/ichwd.c (revision 35d6cbceb29bc6c8987a16f636fbbf41c9307195)
1ace1ab86SFrancois Tigeot /*-
2ace1ab86SFrancois Tigeot  * Copyright (c) 2004 Texas A&M University
3ace1ab86SFrancois Tigeot  * All rights reserved.
4ace1ab86SFrancois Tigeot  *
5ace1ab86SFrancois Tigeot  * Developer: Wm. Daryl Hawkins
6ace1ab86SFrancois Tigeot  *
7ace1ab86SFrancois Tigeot  * Redistribution and use in source and binary forms, with or without
8ace1ab86SFrancois Tigeot  * modification, are permitted provided that the following conditions
9ace1ab86SFrancois Tigeot  * are met:
10ace1ab86SFrancois Tigeot  * 1. Redistributions of source code must retain the above copyright
11ace1ab86SFrancois Tigeot  *    notice, this list of conditions and the following disclaimer.
12ace1ab86SFrancois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
13ace1ab86SFrancois Tigeot  *    notice, this list of conditions and the following disclaimer in the
14ace1ab86SFrancois Tigeot  *    documentation and/or other materials provided with the distribution.
15ace1ab86SFrancois Tigeot  *
16ace1ab86SFrancois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17ace1ab86SFrancois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ace1ab86SFrancois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ace1ab86SFrancois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20ace1ab86SFrancois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ace1ab86SFrancois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ace1ab86SFrancois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ace1ab86SFrancois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ace1ab86SFrancois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ace1ab86SFrancois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ace1ab86SFrancois Tigeot  * SUCH DAMAGE.
27ace1ab86SFrancois Tigeot  */
28ace1ab86SFrancois Tigeot 
29ace1ab86SFrancois Tigeot /*
30ace1ab86SFrancois Tigeot  * Intel ICH Watchdog Timer (WDT) driver
31ace1ab86SFrancois Tigeot  *
32ace1ab86SFrancois Tigeot  * Originally developed by Wm. Daryl Hawkins of Texas A&M
33ace1ab86SFrancois Tigeot  * Heavily modified by <des@FreeBSD.org>
34ace1ab86SFrancois Tigeot  *
35ace1ab86SFrancois Tigeot  * This is a tricky one.  The ICH WDT can't be treated as a regular PCI
36ace1ab86SFrancois Tigeot  * device as it's actually an integrated function of the ICH LPC interface
37ace1ab86SFrancois Tigeot  * bridge.  Detection is also awkward, because we can only infer the
38ace1ab86SFrancois Tigeot  * presence of the watchdog timer from the fact that the machine has an
39ace1ab86SFrancois Tigeot  * ICH chipset, or, on ACPI 2.x systems, by the presence of the 'WDDT'
40ace1ab86SFrancois Tigeot  * ACPI table (although this driver does not support the ACPI detection
41ace1ab86SFrancois Tigeot  * method).
42ace1ab86SFrancois Tigeot  *
43ace1ab86SFrancois Tigeot  * There is one slight problem on non-ACPI or ACPI 1.x systems: we have no
44ace1ab86SFrancois Tigeot  * way of knowing if the WDT is permanently disabled (either by the BIOS
45ace1ab86SFrancois Tigeot  * or in hardware).
46ace1ab86SFrancois Tigeot  *
47ace1ab86SFrancois Tigeot  * The WDT is programmed through I/O registers in the ACPI I/O space.
48ace1ab86SFrancois Tigeot  * Intel swears it's always at offset 0x60, so we use that.
49ace1ab86SFrancois Tigeot  *
50ace1ab86SFrancois Tigeot  * For details about the ICH WDT, see Intel Application Note AP-725
51ace1ab86SFrancois Tigeot  * (document no. 292273-001).  The WDT is also described in the individual
52ace1ab86SFrancois Tigeot  * chipset datasheets, e.g. Intel82801EB ICH5 / 82801ER ICH5R Datasheet
53ace1ab86SFrancois Tigeot  * (document no. 252516-001) sections 9.10 and 9.11.
54ace1ab86SFrancois Tigeot  *
55ace1ab86SFrancois Tigeot  * ICH6/7/8 support by Takeharu KATO <takeharu1219@ybb.ne.jp>
56ace1ab86SFrancois Tigeot  *
57ace1ab86SFrancois Tigeot  * $FreeBSD: src/sys/dev/ichwd/ichwd.c,v 1.34 2012/01/05 16:27:32 jhb Exp $
58ace1ab86SFrancois Tigeot  */
59ace1ab86SFrancois Tigeot 
60ace1ab86SFrancois Tigeot #include <sys/param.h>
61ace1ab86SFrancois Tigeot #include <sys/kernel.h>
62ace1ab86SFrancois Tigeot #include <sys/module.h>
63ace1ab86SFrancois Tigeot #include <sys/systm.h>
64ace1ab86SFrancois Tigeot #include <sys/bus.h>
65ace1ab86SFrancois Tigeot #include <sys/rman.h>
66ace1ab86SFrancois Tigeot #include <sys/resource.h>
67ace1ab86SFrancois Tigeot #include <sys/wdog.h>
68ace1ab86SFrancois Tigeot 
69ace1ab86SFrancois Tigeot #include <bus/isa/isavar.h>
70ace1ab86SFrancois Tigeot #include <bus/pci/pcivar.h>
71ace1ab86SFrancois Tigeot 
72ace1ab86SFrancois Tigeot #include "ichwd.h"
73ace1ab86SFrancois Tigeot 
74ace1ab86SFrancois Tigeot static struct ichwd_device ichwd_devices[] = {
75ace1ab86SFrancois Tigeot 	{ DEVICEID_82801AA,  "Intel 82801AA watchdog timer",	1 },
76ace1ab86SFrancois Tigeot 	{ DEVICEID_82801AB,  "Intel 82801AB watchdog timer",	1 },
77ace1ab86SFrancois Tigeot 	{ DEVICEID_82801BA,  "Intel 82801BA watchdog timer",	2 },
78ace1ab86SFrancois Tigeot 	{ DEVICEID_82801BAM, "Intel 82801BAM watchdog timer",	2 },
79ace1ab86SFrancois Tigeot 	{ DEVICEID_82801CA,  "Intel 82801CA watchdog timer",	3 },
80ace1ab86SFrancois Tigeot 	{ DEVICEID_82801CAM, "Intel 82801CAM watchdog timer",	3 },
81ace1ab86SFrancois Tigeot 	{ DEVICEID_82801DB,  "Intel 82801DB watchdog timer",	4 },
82ace1ab86SFrancois Tigeot 	{ DEVICEID_82801DBM, "Intel 82801DBM watchdog timer",	4 },
83ace1ab86SFrancois Tigeot 	{ DEVICEID_82801E,   "Intel 82801E watchdog timer",	5 },
84ace1ab86SFrancois Tigeot 	{ DEVICEID_82801EB,  "Intel 82801EB watchdog timer",	5 },
85ace1ab86SFrancois Tigeot 	{ DEVICEID_82801EBR, "Intel 82801EB/ER watchdog timer",	5 },
86ace1ab86SFrancois Tigeot 	{ DEVICEID_6300ESB,  "Intel 6300ESB watchdog timer",	5 },
87ace1ab86SFrancois Tigeot 	{ DEVICEID_82801FBR, "Intel 82801FB/FR watchdog timer",	6 },
88ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH6M,    "Intel ICH6M watchdog timer",	6 },
89ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH6W,    "Intel ICH6W watchdog timer",	6 },
90ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH7,     "Intel ICH7 watchdog timer",	7 },
91ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH7DH,   "Intel ICH7DH watchdog timer",	7 },
92ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH7M,    "Intel ICH7M watchdog timer",	7 },
93ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH7MDH,  "Intel ICH7MDH watchdog timer",	7 },
94ace1ab86SFrancois Tigeot 	{ DEVICEID_NM10,     "Intel NM10 watchdog timer",	7 },
95ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH8,     "Intel ICH8 watchdog timer",	8 },
96ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH8DH,   "Intel ICH8DH watchdog timer",	8 },
97ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH8DO,   "Intel ICH8DO watchdog timer",	8 },
98ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH8M,    "Intel ICH8M watchdog timer",	8 },
99ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH8ME,   "Intel ICH8M-E watchdog timer",	8 },
100ace1ab86SFrancois Tigeot 	{ DEVICEID_63XXESB,  "Intel 63XXESB watchdog timer",	8 },
101ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9,     "Intel ICH9 watchdog timer",	9 },
102ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9DH,   "Intel ICH9DH watchdog timer",	9 },
103ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9DO,   "Intel ICH9DO watchdog timer",	9 },
104ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9M,    "Intel ICH9M watchdog timer",	9 },
105ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9ME,   "Intel ICH9M-E watchdog timer",	9 },
106ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH9R,    "Intel ICH9R watchdog timer",	9 },
107ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH10,    "Intel ICH10 watchdog timer",	10 },
108ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH10D,   "Intel ICH10D watchdog timer",	10 },
109ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH10DO,  "Intel ICH10DO watchdog timer",	10 },
110ace1ab86SFrancois Tigeot 	{ DEVICEID_ICH10R,   "Intel ICH10R watchdog timer",	10 },
111ace1ab86SFrancois Tigeot 	{ DEVICEID_PCH,      "Intel PCH watchdog timer",	10 },
112ace1ab86SFrancois Tigeot 	{ DEVICEID_PCHM,     "Intel PCH watchdog timer",	10 },
113ace1ab86SFrancois Tigeot 	{ DEVICEID_P55,      "Intel P55 watchdog timer",	10 },
114ace1ab86SFrancois Tigeot 	{ DEVICEID_PM55,     "Intel PM55 watchdog timer",	10 },
115ace1ab86SFrancois Tigeot 	{ DEVICEID_H55,      "Intel H55 watchdog timer",	10 },
116ace1ab86SFrancois Tigeot 	{ DEVICEID_QM57,     "Intel QM57 watchdog timer",       10 },
117ace1ab86SFrancois Tigeot 	{ DEVICEID_H57,      "Intel H57 watchdog timer",        10 },
118ace1ab86SFrancois Tigeot 	{ DEVICEID_HM55,     "Intel HM55 watchdog timer",       10 },
119ace1ab86SFrancois Tigeot 	{ DEVICEID_Q57,      "Intel Q57 watchdog timer",        10 },
120ace1ab86SFrancois Tigeot 	{ DEVICEID_HM57,     "Intel HM57 watchdog timer",       10 },
121ace1ab86SFrancois Tigeot 	{ DEVICEID_PCHMSFF,  "Intel PCHMSFF watchdog timer",    10 },
122ace1ab86SFrancois Tigeot 	{ DEVICEID_QS57,     "Intel QS57 watchdog timer",       10 },
123ace1ab86SFrancois Tigeot 	{ DEVICEID_3400,     "Intel 3400 watchdog timer",       10 },
124ace1ab86SFrancois Tigeot 	{ DEVICEID_3420,     "Intel 3420 watchdog timer",       10 },
125ace1ab86SFrancois Tigeot 	{ DEVICEID_3450,     "Intel 3450 watchdog timer",       10 },
126ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT0,     "Intel Cougar Point watchdog timer",	10 },
127ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT1,     "Intel Cougar Point watchdog timer",	10 },
128ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT2,     "Intel Cougar Point watchdog timer",	10 },
129ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT3,     "Intel Cougar Point watchdog timer",	10 },
130ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT4,     "Intel Cougar Point watchdog timer",	10 },
131ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT5,     "Intel Cougar Point watchdog timer",	10 },
132ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT6,     "Intel Cougar Point watchdog timer",	10 },
133ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT7,     "Intel Cougar Point watchdog timer",	10 },
134ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT8,     "Intel Cougar Point watchdog timer",	10 },
135ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT9,     "Intel Cougar Point watchdog timer",	10 },
136ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT10,    "Intel Cougar Point watchdog timer",	10 },
137ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT11,    "Intel Cougar Point watchdog timer",	10 },
138ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT12,    "Intel Cougar Point watchdog timer",	10 },
139ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT13,    "Intel Cougar Point watchdog timer",	10 },
140ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT14,    "Intel Cougar Point watchdog timer",	10 },
141ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT15,    "Intel Cougar Point watchdog timer",	10 },
142ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT16,    "Intel Cougar Point watchdog timer",	10 },
143ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT17,    "Intel Cougar Point watchdog timer",	10 },
144ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT18,    "Intel Cougar Point watchdog timer",	10 },
145ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT19,    "Intel Cougar Point watchdog timer",	10 },
146ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT20,    "Intel Cougar Point watchdog timer",	10 },
147ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT21,    "Intel Cougar Point watchdog timer",	10 },
148ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT22,    "Intel Cougar Point watchdog timer",	10 },
149ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT23,    "Intel Cougar Point watchdog timer",	10 },
150ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT23,    "Intel Cougar Point watchdog timer",	10 },
151ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT25,    "Intel Cougar Point watchdog timer",	10 },
152ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT26,    "Intel Cougar Point watchdog timer",	10 },
153ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT27,    "Intel Cougar Point watchdog timer",	10 },
154ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT28,    "Intel Cougar Point watchdog timer",	10 },
155ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT29,    "Intel Cougar Point watchdog timer",	10 },
156ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT30,    "Intel Cougar Point watchdog timer",	10 },
157ace1ab86SFrancois Tigeot 	{ DEVICEID_CPT31,    "Intel Cougar Point watchdog timer",	10 },
158ace1ab86SFrancois Tigeot 	{ DEVICEID_PATSBURG_LPC1, "Intel Patsburg watchdog timer",	10 },
159ace1ab86SFrancois Tigeot 	{ DEVICEID_PATSBURG_LPC2, "Intel Patsburg watchdog timer",	10 },
160ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT0,     "Intel Panther Point watchdog timer",	10 },
161ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT1,     "Intel Panther Point watchdog timer",	10 },
162ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT2,     "Intel Panther Point watchdog timer",	10 },
163ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT3,     "Intel Panther Point watchdog timer",	10 },
164ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT4,     "Intel Panther Point watchdog timer",	10 },
165ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT5,     "Intel Panther Point watchdog timer",	10 },
166ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT6,     "Intel Panther Point watchdog timer",	10 },
167ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT7,     "Intel Panther Point watchdog timer",	10 },
168ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT8,     "Intel Panther Point watchdog timer",	10 },
169ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT9,     "Intel Panther Point watchdog timer",	10 },
170ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT10,    "Intel Panther Point watchdog timer",	10 },
171ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT11,    "Intel Panther Point watchdog timer",	10 },
172ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT12,    "Intel Panther Point watchdog timer",	10 },
173ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT13,    "Intel Panther Point watchdog timer",	10 },
174ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT14,    "Intel Panther Point watchdog timer",	10 },
175ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT15,    "Intel Panther Point watchdog timer",	10 },
176ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT16,    "Intel Panther Point watchdog timer",	10 },
177ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT17,    "Intel Panther Point watchdog timer",	10 },
178ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT18,    "Intel Panther Point watchdog timer",	10 },
179ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT19,    "Intel Panther Point watchdog timer",	10 },
180ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT20,    "Intel Panther Point watchdog timer",	10 },
181ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT21,    "Intel Panther Point watchdog timer",	10 },
182ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT22,    "Intel Panther Point watchdog timer",	10 },
183ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT23,    "Intel Panther Point watchdog timer",	10 },
184ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT24,    "Intel Panther Point watchdog timer",	10 },
185ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT25,    "Intel Panther Point watchdog timer",	10 },
186ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT26,    "Intel Panther Point watchdog timer",	10 },
187ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT27,    "Intel Panther Point watchdog timer",	10 },
188ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT28,    "Intel Panther Point watchdog timer",	10 },
189ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT29,    "Intel Panther Point watchdog timer",	10 },
190ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT30,    "Intel Panther Point watchdog timer",	10 },
191ace1ab86SFrancois Tigeot 	{ DEVICEID_PPT31,    "Intel Panther Point watchdog timer",	10 },
192*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT0,     "Intel Lynx Point watchdog timer",		10 },
193*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT1,     "Intel Lynx Point watchdog timer",		10 },
194*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT2,     "Intel Lynx Point watchdog timer",		10 },
195*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT3,     "Intel Lynx Point watchdog timer",		10 },
196*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT4,     "Intel Lynx Point watchdog timer",		10 },
197*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT5,     "Intel Lynx Point watchdog timer",		10 },
198*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT6,     "Intel Lynx Point watchdog timer",		10 },
199*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT7,     "Intel Lynx Point watchdog timer",		10 },
200*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT8,     "Intel Lynx Point watchdog timer",		10 },
201*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT9,     "Intel Lynx Point watchdog timer",		10 },
202*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT10,    "Intel Lynx Point watchdog timer",		10 },
203*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT11,    "Intel Lynx Point watchdog timer",		10 },
204*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT12,    "Intel Lynx Point watchdog timer",		10 },
205*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT13,    "Intel Lynx Point watchdog timer",		10 },
206*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT14,    "Intel Lynx Point watchdog timer",		10 },
207*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT15,    "Intel Lynx Point watchdog timer",		10 },
208*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT16,    "Intel Lynx Point watchdog timer",		10 },
209*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT17,    "Intel Lynx Point watchdog timer",		10 },
210*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT18,    "Intel Lynx Point watchdog timer",		10 },
211*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT19,    "Intel Lynx Point watchdog timer",		10 },
212*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT20,    "Intel Lynx Point watchdog timer",		10 },
213*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT21,    "Intel Lynx Point watchdog timer",		10 },
214*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT22,    "Intel Lynx Point watchdog timer",		10 },
215*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT23,    "Intel Lynx Point watchdog timer",		10 },
216*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT24,    "Intel Lynx Point watchdog timer",		10 },
217*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT25,    "Intel Lynx Point watchdog timer",		10 },
218*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT26,    "Intel Lynx Point watchdog timer",		10 },
219*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT27,    "Intel Lynx Point watchdog timer",		10 },
220*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT28,    "Intel Lynx Point watchdog timer",		10 },
221*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT29,    "Intel Lynx Point watchdog timer",		10 },
222*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT30,    "Intel Lynx Point watchdog timer",		10 },
223*35d6cbceSFrançois Tigeot 	{ DEVICEID_LPT31,    "Intel Lynx Point watchdog timer",		10 },
224*35d6cbceSFrançois Tigeot 	{ DEVICEID_WCPT2,    "Intel Wildcat Point watchdog timer",	10 },
225*35d6cbceSFrançois Tigeot 	{ DEVICEID_WCPT4,    "Intel Wildcat Point watchdog timer",	10 },
226*35d6cbceSFrançois Tigeot 	{ DEVICEID_WCPT6,    "Intel Wildcat Point watchdog timer",	10 },
227ace1ab86SFrancois Tigeot 	{ DEVICEID_DH89XXCC_LPC,  "Intel DH89xxCC watchdog timer",	10 },
228*35d6cbceSFrançois Tigeot 	{ DEVICEID_COLETOCRK_LPC, "Intel Coleto Creek watchdog timer",  10 },
229ace1ab86SFrancois Tigeot 	{ 0, NULL, 0 },
230ace1ab86SFrancois Tigeot };
231ace1ab86SFrancois Tigeot 
232ace1ab86SFrancois Tigeot static devclass_t ichwd_devclass;
233ace1ab86SFrancois Tigeot 
234ace1ab86SFrancois Tigeot static struct ichwd_softc ichwd_sc;
235ace1ab86SFrancois Tigeot 
236ace1ab86SFrancois Tigeot #define ichwd_read_tco_1(sc, off) \
237ace1ab86SFrancois Tigeot 	bus_read_1((sc)->tco_res, (off))
238ace1ab86SFrancois Tigeot #define ichwd_read_tco_2(sc, off) \
239ace1ab86SFrancois Tigeot 	bus_read_2((sc)->tco_res, (off))
240ace1ab86SFrancois Tigeot #define ichwd_read_tco_4(sc, off) \
241ace1ab86SFrancois Tigeot 	bus_read_4((sc)->tco_res, (off))
242ace1ab86SFrancois Tigeot #define ichwd_read_smi_4(sc, off) \
243ace1ab86SFrancois Tigeot 	bus_read_4((sc)->smi_res, (off))
244ace1ab86SFrancois Tigeot #define ichwd_read_gcs_4(sc, off) \
245ace1ab86SFrancois Tigeot 	bus_read_4((sc)->gcs_res, (off))
246ace1ab86SFrancois Tigeot 
247ace1ab86SFrancois Tigeot #define ichwd_write_tco_1(sc, off, val) \
248ace1ab86SFrancois Tigeot 	bus_write_1((sc)->tco_res, (off), (val))
249ace1ab86SFrancois Tigeot #define ichwd_write_tco_2(sc, off, val) \
250ace1ab86SFrancois Tigeot 	bus_write_2((sc)->tco_res, (off), (val))
251ace1ab86SFrancois Tigeot #define ichwd_write_tco_4(sc, off, val) \
252ace1ab86SFrancois Tigeot 	bus_write_4((sc)->tco_res, (off), (val))
253ace1ab86SFrancois Tigeot #define ichwd_write_smi_4(sc, off, val) \
254ace1ab86SFrancois Tigeot 	bus_write_4((sc)->smi_res, (off), (val))
255ace1ab86SFrancois Tigeot #define ichwd_write_gcs_4(sc, off, val) \
256ace1ab86SFrancois Tigeot 	bus_write_4((sc)->gcs_res, (off), (val))
257ace1ab86SFrancois Tigeot 
258ace1ab86SFrancois Tigeot #define ichwd_verbose_printf(dev, ...) \
259ace1ab86SFrancois Tigeot 	do {						\
260ace1ab86SFrancois Tigeot 		if (bootverbose)			\
261ace1ab86SFrancois Tigeot 			device_printf(dev, __VA_ARGS__);\
262ace1ab86SFrancois Tigeot 	} while (0)
263ace1ab86SFrancois Tigeot 
264ace1ab86SFrancois Tigeot /*
265ace1ab86SFrancois Tigeot  * Disable the watchdog timeout SMI handler.
266ace1ab86SFrancois Tigeot  *
267ace1ab86SFrancois Tigeot  * Apparently, some BIOSes install handlers that reset or disable the
268ace1ab86SFrancois Tigeot  * watchdog timer instead of resetting the system, so we disable the SMI
269ace1ab86SFrancois Tigeot  * (by clearing the SMI_TCO_EN bit of the SMI_EN register) to prevent this
270ace1ab86SFrancois Tigeot  * from happening.
271ace1ab86SFrancois Tigeot  */
272ace1ab86SFrancois Tigeot static __inline void
ichwd_smi_disable(struct ichwd_softc * sc)273ace1ab86SFrancois Tigeot ichwd_smi_disable(struct ichwd_softc *sc)
274ace1ab86SFrancois Tigeot {
275ace1ab86SFrancois Tigeot 	ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) & ~SMI_TCO_EN);
276ace1ab86SFrancois Tigeot }
277ace1ab86SFrancois Tigeot 
278ace1ab86SFrancois Tigeot /*
279ace1ab86SFrancois Tigeot  * Enable the watchdog timeout SMI handler.  See above for details.
280ace1ab86SFrancois Tigeot  */
281ace1ab86SFrancois Tigeot static __inline void
ichwd_smi_enable(struct ichwd_softc * sc)282ace1ab86SFrancois Tigeot ichwd_smi_enable(struct ichwd_softc *sc)
283ace1ab86SFrancois Tigeot {
284ace1ab86SFrancois Tigeot 	ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) | SMI_TCO_EN);
285ace1ab86SFrancois Tigeot }
286ace1ab86SFrancois Tigeot 
287ace1ab86SFrancois Tigeot /*
288ace1ab86SFrancois Tigeot  * Check if the watchdog SMI triggering is enabled.
289ace1ab86SFrancois Tigeot  */
290ace1ab86SFrancois Tigeot static __inline int
ichwd_smi_is_enabled(struct ichwd_softc * sc)291ace1ab86SFrancois Tigeot ichwd_smi_is_enabled(struct ichwd_softc *sc)
292ace1ab86SFrancois Tigeot {
293ace1ab86SFrancois Tigeot 	return ((ichwd_read_smi_4(sc, SMI_EN) & SMI_TCO_EN) != 0);
294ace1ab86SFrancois Tigeot }
295ace1ab86SFrancois Tigeot 
296ace1ab86SFrancois Tigeot /*
297ace1ab86SFrancois Tigeot  * Reset the watchdog status bits.
298ace1ab86SFrancois Tigeot  */
299ace1ab86SFrancois Tigeot static __inline void
ichwd_sts_reset(struct ichwd_softc * sc)300ace1ab86SFrancois Tigeot ichwd_sts_reset(struct ichwd_softc *sc)
301ace1ab86SFrancois Tigeot {
302ace1ab86SFrancois Tigeot 	/*
303ace1ab86SFrancois Tigeot 	 * The watchdog status bits are set to 1 by the hardware to
304ace1ab86SFrancois Tigeot 	 * indicate various conditions.  They can be cleared by software
305ace1ab86SFrancois Tigeot 	 * by writing a 1, not a 0.
306ace1ab86SFrancois Tigeot 	 */
307ace1ab86SFrancois Tigeot 	ichwd_write_tco_2(sc, TCO1_STS, TCO_TIMEOUT);
308ace1ab86SFrancois Tigeot 	/*
309ace1ab86SFrancois Tigeot 	 * According to Intel's docs, clearing SECOND_TO_STS and BOOT_STS must
310ace1ab86SFrancois Tigeot 	 * be done in two separate operations.
311ace1ab86SFrancois Tigeot 	 */
312ace1ab86SFrancois Tigeot 	ichwd_write_tco_2(sc, TCO2_STS, TCO_SECOND_TO_STS);
313ace1ab86SFrancois Tigeot 	ichwd_write_tco_2(sc, TCO2_STS, TCO_BOOT_STS);
314ace1ab86SFrancois Tigeot }
315ace1ab86SFrancois Tigeot 
316ace1ab86SFrancois Tigeot /*
317ace1ab86SFrancois Tigeot  * Enable the watchdog timer by clearing the TCO_TMR_HALT bit in the
318ace1ab86SFrancois Tigeot  * TCO1_CNT register.  This is complicated by the need to preserve bit 9
319ace1ab86SFrancois Tigeot  * of that same register, and the requirement that all other bits must be
320ace1ab86SFrancois Tigeot  * written back as zero.
321ace1ab86SFrancois Tigeot  */
322ace1ab86SFrancois Tigeot static __inline void
ichwd_tmr_enable(struct ichwd_softc * sc)323ace1ab86SFrancois Tigeot ichwd_tmr_enable(struct ichwd_softc *sc)
324ace1ab86SFrancois Tigeot {
325ace1ab86SFrancois Tigeot 	uint16_t cnt;
326ace1ab86SFrancois Tigeot 
327ace1ab86SFrancois Tigeot 	cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
328ace1ab86SFrancois Tigeot 	ichwd_write_tco_2(sc, TCO1_CNT, cnt & ~TCO_TMR_HALT);
329ace1ab86SFrancois Tigeot 	sc->active = 1;
330ace1ab86SFrancois Tigeot 	ichwd_verbose_printf(sc->device, "timer enabled\n");
331ace1ab86SFrancois Tigeot }
332ace1ab86SFrancois Tigeot 
333ace1ab86SFrancois Tigeot /*
334ace1ab86SFrancois Tigeot  * Disable the watchdog timer.  See above for details.
335ace1ab86SFrancois Tigeot  */
336ace1ab86SFrancois Tigeot static __inline void
ichwd_tmr_disable(struct ichwd_softc * sc)337ace1ab86SFrancois Tigeot ichwd_tmr_disable(struct ichwd_softc *sc)
338ace1ab86SFrancois Tigeot {
339ace1ab86SFrancois Tigeot 	uint16_t cnt;
340ace1ab86SFrancois Tigeot 
341ace1ab86SFrancois Tigeot 	cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
342ace1ab86SFrancois Tigeot 	ichwd_write_tco_2(sc, TCO1_CNT, cnt | TCO_TMR_HALT);
343ace1ab86SFrancois Tigeot 	sc->active = 0;
344ace1ab86SFrancois Tigeot 	ichwd_verbose_printf(sc->device, "timer disabled\n");
345ace1ab86SFrancois Tigeot }
346ace1ab86SFrancois Tigeot 
347ace1ab86SFrancois Tigeot /*
348ace1ab86SFrancois Tigeot  * Reload the watchdog timer: writing anything to any of the lower five
349ace1ab86SFrancois Tigeot  * bits of the TCO_RLD register reloads the timer from the last value
350ace1ab86SFrancois Tigeot  * written to TCO_TMR.
351ace1ab86SFrancois Tigeot  */
352ace1ab86SFrancois Tigeot static __inline void
ichwd_tmr_reload(struct ichwd_softc * sc)353ace1ab86SFrancois Tigeot ichwd_tmr_reload(struct ichwd_softc *sc)
354ace1ab86SFrancois Tigeot {
355ace1ab86SFrancois Tigeot 	if (sc->ich_version <= 5)
356ace1ab86SFrancois Tigeot 		ichwd_write_tco_1(sc, TCO_RLD, 1);
357ace1ab86SFrancois Tigeot 	else
358ace1ab86SFrancois Tigeot 		ichwd_write_tco_2(sc, TCO_RLD, 1);
359ace1ab86SFrancois Tigeot }
360ace1ab86SFrancois Tigeot 
361ace1ab86SFrancois Tigeot /*
362ace1ab86SFrancois Tigeot  * Set the initial timeout value.  Note that this must always be followed
363ace1ab86SFrancois Tigeot  * by a reload.
364ace1ab86SFrancois Tigeot  */
365ace1ab86SFrancois Tigeot static __inline void
ichwd_tmr_set(struct ichwd_softc * sc,unsigned int timeout)366ace1ab86SFrancois Tigeot ichwd_tmr_set(struct ichwd_softc *sc, unsigned int timeout)
367ace1ab86SFrancois Tigeot {
368ace1ab86SFrancois Tigeot 
369ace1ab86SFrancois Tigeot 	if (timeout < TCO_RLD_TMR_MIN)
370ace1ab86SFrancois Tigeot 		timeout = TCO_RLD_TMR_MIN;
371ace1ab86SFrancois Tigeot 
372ace1ab86SFrancois Tigeot 	if (sc->ich_version <= 5) {
373ace1ab86SFrancois Tigeot 		uint8_t tmr_val8 = ichwd_read_tco_1(sc, TCO_TMR1);
374ace1ab86SFrancois Tigeot 
375ace1ab86SFrancois Tigeot 		tmr_val8 &= (~TCO_RLD1_TMR_MAX & 0xff);
376ace1ab86SFrancois Tigeot 		if (timeout > TCO_RLD1_TMR_MAX)
377ace1ab86SFrancois Tigeot 			timeout = TCO_RLD1_TMR_MAX;
378ace1ab86SFrancois Tigeot 		tmr_val8 |= timeout;
379ace1ab86SFrancois Tigeot 		ichwd_write_tco_1(sc, TCO_TMR1, tmr_val8);
380ace1ab86SFrancois Tigeot 	} else {
381ace1ab86SFrancois Tigeot 		uint16_t tmr_val16 = ichwd_read_tco_2(sc, TCO_TMR2);
382ace1ab86SFrancois Tigeot 
383ace1ab86SFrancois Tigeot 		tmr_val16 &= (~TCO_RLD2_TMR_MAX & 0xffff);
384ace1ab86SFrancois Tigeot 		if (timeout > TCO_RLD2_TMR_MAX)
385ace1ab86SFrancois Tigeot 			timeout = TCO_RLD2_TMR_MAX;
386ace1ab86SFrancois Tigeot 		tmr_val16 |= timeout;
387ace1ab86SFrancois Tigeot 		ichwd_write_tco_2(sc, TCO_TMR2, tmr_val16);
388ace1ab86SFrancois Tigeot 	}
389ace1ab86SFrancois Tigeot 
390ace1ab86SFrancois Tigeot 	sc->timeout = timeout;
391ace1ab86SFrancois Tigeot 
392ace1ab86SFrancois Tigeot 	ichwd_verbose_printf(sc->device, "timeout set to %u ticks\n", timeout);
393ace1ab86SFrancois Tigeot }
394ace1ab86SFrancois Tigeot 
395ace1ab86SFrancois Tigeot static __inline int
ichwd_clear_noreboot(struct ichwd_softc * sc)396ace1ab86SFrancois Tigeot ichwd_clear_noreboot(struct ichwd_softc *sc)
397ace1ab86SFrancois Tigeot {
398ace1ab86SFrancois Tigeot 	uint32_t status;
399ace1ab86SFrancois Tigeot 	int rc = 0;
400ace1ab86SFrancois Tigeot 
401ace1ab86SFrancois Tigeot 	/* try to clear the NO_REBOOT bit */
402ace1ab86SFrancois Tigeot 	if (sc->ich_version <= 5) {
403ace1ab86SFrancois Tigeot 		status = pci_read_config(sc->ich, ICH_GEN_STA, 1);
404ace1ab86SFrancois Tigeot 		status &= ~ICH_GEN_STA_NO_REBOOT;
405ace1ab86SFrancois Tigeot 		pci_write_config(sc->ich, ICH_GEN_STA, status, 1);
406ace1ab86SFrancois Tigeot 		status = pci_read_config(sc->ich, ICH_GEN_STA, 1);
407ace1ab86SFrancois Tigeot 		if (status & ICH_GEN_STA_NO_REBOOT)
408ace1ab86SFrancois Tigeot 			rc = EIO;
409ace1ab86SFrancois Tigeot 	} else {
410ace1ab86SFrancois Tigeot 		status = ichwd_read_gcs_4(sc, 0);
411ace1ab86SFrancois Tigeot 		status &= ~ICH_GCS_NO_REBOOT;
412ace1ab86SFrancois Tigeot 		ichwd_write_gcs_4(sc, 0, status);
413ace1ab86SFrancois Tigeot 		status = ichwd_read_gcs_4(sc, 0);
414ace1ab86SFrancois Tigeot 		if (status & ICH_GCS_NO_REBOOT)
415ace1ab86SFrancois Tigeot 			rc = EIO;
416ace1ab86SFrancois Tigeot 	}
417ace1ab86SFrancois Tigeot 
418ace1ab86SFrancois Tigeot 	if (rc)
419ace1ab86SFrancois Tigeot 		device_printf(sc->device,
420ace1ab86SFrancois Tigeot 		    "ICH WDT present but disabled in BIOS or hardware\n");
421ace1ab86SFrancois Tigeot 
422ace1ab86SFrancois Tigeot 	return (rc);
423ace1ab86SFrancois Tigeot }
424ace1ab86SFrancois Tigeot 
425ace1ab86SFrancois Tigeot static device_t
ichwd_find_ich_lpc_bridge(struct ichwd_device ** id_p)426ace1ab86SFrancois Tigeot ichwd_find_ich_lpc_bridge(struct ichwd_device **id_p)
427ace1ab86SFrancois Tigeot {
428ace1ab86SFrancois Tigeot 	struct ichwd_device *id;
429ace1ab86SFrancois Tigeot 	device_t ich = NULL;
430ace1ab86SFrancois Tigeot 
431ace1ab86SFrancois Tigeot 	/* look for an ICH LPC interface bridge */
432ace1ab86SFrancois Tigeot 	for (id = ichwd_devices; id->desc != NULL; ++id)
433ace1ab86SFrancois Tigeot 		if ((ich = pci_find_device(VENDORID_INTEL, id->device)) != NULL)
434ace1ab86SFrancois Tigeot 			break;
435ace1ab86SFrancois Tigeot 
436ace1ab86SFrancois Tigeot 	if (ich == NULL)
437ace1ab86SFrancois Tigeot 		return (NULL);
438ace1ab86SFrancois Tigeot 
439ace1ab86SFrancois Tigeot 	ichwd_verbose_printf(ich, "found ICH%d or equivalent chipset: %s\n",
440ace1ab86SFrancois Tigeot 	    id->version, id->desc);
441ace1ab86SFrancois Tigeot 
442ace1ab86SFrancois Tigeot 	if (id_p)
443ace1ab86SFrancois Tigeot 		*id_p = id;
444ace1ab86SFrancois Tigeot 
445ace1ab86SFrancois Tigeot 	return (ich);
446ace1ab86SFrancois Tigeot }
447ace1ab86SFrancois Tigeot 
448ace1ab86SFrancois Tigeot /*
449ace1ab86SFrancois Tigeot  * Look for an ICH LPC interface bridge.  If one is found, register an
450ace1ab86SFrancois Tigeot  * ichwd device.  There can be only one.
451ace1ab86SFrancois Tigeot  */
452ace1ab86SFrancois Tigeot static void
ichwd_identify(driver_t * driver,device_t parent)453ace1ab86SFrancois Tigeot ichwd_identify(driver_t *driver, device_t parent)
454ace1ab86SFrancois Tigeot {
455ace1ab86SFrancois Tigeot 	struct ichwd_device *id_p;
456ace1ab86SFrancois Tigeot 	device_t ich = NULL;
457ace1ab86SFrancois Tigeot 	device_t dev;
458ace1ab86SFrancois Tigeot 	uint32_t rcba;
459ace1ab86SFrancois Tigeot 	int rc;
460ace1ab86SFrancois Tigeot 
461ace1ab86SFrancois Tigeot 	ich = ichwd_find_ich_lpc_bridge(&id_p);
462ace1ab86SFrancois Tigeot 	if (ich == NULL)
463ace1ab86SFrancois Tigeot 		return;
464ace1ab86SFrancois Tigeot 
465ace1ab86SFrancois Tigeot 	/* good, add child to bus */
466ace1ab86SFrancois Tigeot 	if ((dev = device_find_child(parent, driver->name, 0)) == NULL)
467ace1ab86SFrancois Tigeot 		dev = BUS_ADD_CHILD(parent, parent, 0, driver->name, 0);
468ace1ab86SFrancois Tigeot 
469ace1ab86SFrancois Tigeot 	if (dev == NULL)
470ace1ab86SFrancois Tigeot 		return;
471ace1ab86SFrancois Tigeot 
472ace1ab86SFrancois Tigeot 	device_set_desc_copy(dev, id_p->desc);
473ace1ab86SFrancois Tigeot 
474ace1ab86SFrancois Tigeot 	if (id_p->version >= 6) {
475ace1ab86SFrancois Tigeot 		/* get RCBA (root complex base address) */
476ace1ab86SFrancois Tigeot 		rcba = pci_read_config(ich, ICH_RCBA, 4);
477ace1ab86SFrancois Tigeot 		rc = bus_set_resource(ich, SYS_RES_MEMORY, 0,
478ace1ab86SFrancois Tigeot 		    (rcba & 0xffffc000) + ICH_GCS_OFFSET, ICH_GCS_SIZE, -1);
479ace1ab86SFrancois Tigeot 		if (rc)
480ace1ab86SFrancois Tigeot 			ichwd_verbose_printf(dev,
481ace1ab86SFrancois Tigeot 			    "Can not set memory resource for RCBA\n");
482ace1ab86SFrancois Tigeot 	}
483ace1ab86SFrancois Tigeot }
484ace1ab86SFrancois Tigeot 
485ace1ab86SFrancois Tigeot static int
ich_watchdog(void * unused,int period)486ace1ab86SFrancois Tigeot ich_watchdog(void *unused, int period)
487ace1ab86SFrancois Tigeot {
488ace1ab86SFrancois Tigeot 	unsigned int timeout;
489ace1ab86SFrancois Tigeot 
490ace1ab86SFrancois Tigeot 	/* convert from seconds to WDT ticks */
491ace1ab86SFrancois Tigeot 	timeout = (period * 1000) / ICHWD_TICK;
492ace1ab86SFrancois Tigeot 
493ace1ab86SFrancois Tigeot 	ichwd_tmr_set(&ichwd_sc, timeout);
494ace1ab86SFrancois Tigeot 	ichwd_tmr_reload(&ichwd_sc);
495ace1ab86SFrancois Tigeot 
496ace1ab86SFrancois Tigeot 	return period;
497ace1ab86SFrancois Tigeot }
498ace1ab86SFrancois Tigeot 
499ace1ab86SFrancois Tigeot static struct watchdog ich_wdog = {
500ace1ab86SFrancois Tigeot 	.name		=	"Intel ICH",
501ace1ab86SFrancois Tigeot 	.wdog_fn	=	ich_watchdog,
502ace1ab86SFrancois Tigeot 	.arg		=	NULL,
503ace1ab86SFrancois Tigeot 	.period_max	=	(TCO_RLD1_TMR_MAX * ICHWD_TICK) / 1000,
504ace1ab86SFrancois Tigeot };
505ace1ab86SFrancois Tigeot 
506ace1ab86SFrancois Tigeot static int
ichwd_probe(device_t dev)507ace1ab86SFrancois Tigeot ichwd_probe(device_t dev)
508ace1ab86SFrancois Tigeot {
509ace1ab86SFrancois Tigeot 
510ace1ab86SFrancois Tigeot 	/* Do not claim some ISA PnP device by accident. */
511ace1ab86SFrancois Tigeot 	if (isa_get_logicalid(dev) != 0)
512ace1ab86SFrancois Tigeot 		return (ENXIO);
513ace1ab86SFrancois Tigeot 	return (0);
514ace1ab86SFrancois Tigeot }
515ace1ab86SFrancois Tigeot 
516ace1ab86SFrancois Tigeot static int
ichwd_attach(device_t dev)517ace1ab86SFrancois Tigeot ichwd_attach(device_t dev)
518ace1ab86SFrancois Tigeot {
519ace1ab86SFrancois Tigeot 	struct ichwd_softc *sc;
520ace1ab86SFrancois Tigeot 	struct ichwd_device *id_p;
521ace1ab86SFrancois Tigeot 	device_t ich;
522ace1ab86SFrancois Tigeot 	unsigned int pmbase = 0;
523ace1ab86SFrancois Tigeot 
524ace1ab86SFrancois Tigeot 	sc = &ichwd_sc;
525ace1ab86SFrancois Tigeot 	sc->device = dev;
526ace1ab86SFrancois Tigeot 
527ace1ab86SFrancois Tigeot 	ich = ichwd_find_ich_lpc_bridge(&id_p);
528ace1ab86SFrancois Tigeot 	if (ich == NULL) {
529ace1ab86SFrancois Tigeot 		device_printf(sc->device, "Can not find ICH device.\n");
530ace1ab86SFrancois Tigeot 		goto fail;
531ace1ab86SFrancois Tigeot 	}
532ace1ab86SFrancois Tigeot 	sc->ich = ich;
533ace1ab86SFrancois Tigeot 	sc->ich_version = id_p->version;
534ace1ab86SFrancois Tigeot 
535ace1ab86SFrancois Tigeot 	/* get ACPI base address */
536ace1ab86SFrancois Tigeot 	pmbase = pci_read_config(ich, ICH_PMBASE, 2) & ICH_PMBASE_MASK;
537ace1ab86SFrancois Tigeot 	if (pmbase == 0) {
538ace1ab86SFrancois Tigeot 		device_printf(dev, "ICH PMBASE register is empty\n");
539ace1ab86SFrancois Tigeot 		goto fail;
540ace1ab86SFrancois Tigeot 	}
541ace1ab86SFrancois Tigeot 
542ace1ab86SFrancois Tigeot 	/* allocate I/O register space */
543ace1ab86SFrancois Tigeot 	sc->smi_rid = 0;
544ace1ab86SFrancois Tigeot 	sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid,
545ace1ab86SFrancois Tigeot 	    pmbase + SMI_BASE, pmbase + SMI_BASE + SMI_LEN - 1, SMI_LEN,
546ace1ab86SFrancois Tigeot 	    RF_ACTIVE | RF_SHAREABLE);
547ace1ab86SFrancois Tigeot 	if (sc->smi_res == NULL) {
548ace1ab86SFrancois Tigeot 		device_printf(dev, "unable to reserve SMI registers\n");
549ace1ab86SFrancois Tigeot 		goto fail;
550ace1ab86SFrancois Tigeot 	}
551ace1ab86SFrancois Tigeot 
552ace1ab86SFrancois Tigeot 	sc->tco_rid = 1;
553ace1ab86SFrancois Tigeot 	sc->tco_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->tco_rid,
554ace1ab86SFrancois Tigeot 	    pmbase + TCO_BASE, pmbase + TCO_BASE + TCO_LEN - 1, TCO_LEN,
555ace1ab86SFrancois Tigeot 	    RF_ACTIVE | RF_SHAREABLE);
556ace1ab86SFrancois Tigeot 	if (sc->tco_res == NULL) {
557ace1ab86SFrancois Tigeot 		device_printf(dev, "unable to reserve TCO registers\n");
558ace1ab86SFrancois Tigeot 		goto fail;
559ace1ab86SFrancois Tigeot 	}
560ace1ab86SFrancois Tigeot 
561ace1ab86SFrancois Tigeot 	sc->gcs_rid = 0;
562ace1ab86SFrancois Tigeot 	if (sc->ich_version >= 6) {
563ace1ab86SFrancois Tigeot 		sc->gcs_res = bus_alloc_resource_any(ich, SYS_RES_MEMORY,
564ace1ab86SFrancois Tigeot 		    &sc->gcs_rid, RF_ACTIVE|RF_SHAREABLE);
565ace1ab86SFrancois Tigeot 		if (sc->gcs_res == NULL) {
566ace1ab86SFrancois Tigeot 			device_printf(dev, "unable to reserve GCS registers\n");
567ace1ab86SFrancois Tigeot 			goto fail;
568ace1ab86SFrancois Tigeot 		}
569ace1ab86SFrancois Tigeot 	}
570ace1ab86SFrancois Tigeot 
571ace1ab86SFrancois Tigeot 	if (ichwd_clear_noreboot(sc) != 0)
572ace1ab86SFrancois Tigeot 		goto fail;
573ace1ab86SFrancois Tigeot 
574ace1ab86SFrancois Tigeot 	ichwd_verbose_printf(dev, "%s (ICH%d or equivalent)\n",
575ace1ab86SFrancois Tigeot 	    device_get_desc(dev), sc->ich_version);
576ace1ab86SFrancois Tigeot 
577ace1ab86SFrancois Tigeot 	/*
578ace1ab86SFrancois Tigeot 	 * Determine if we are coming up after a watchdog-induced reset.  Some
579ace1ab86SFrancois Tigeot 	 * BIOSes may clear this bit at bootup, preventing us from reporting
580ace1ab86SFrancois Tigeot 	 * this case on such systems.  We clear this bit in ichwd_sts_reset().
581ace1ab86SFrancois Tigeot 	 */
582ace1ab86SFrancois Tigeot 	if ((ichwd_read_tco_2(sc, TCO2_STS) & TCO_SECOND_TO_STS) != 0)
583ace1ab86SFrancois Tigeot 		device_printf(dev,
584ace1ab86SFrancois Tigeot 		    "resuming after hardware watchdog timeout\n");
585ace1ab86SFrancois Tigeot 
586ace1ab86SFrancois Tigeot 	/* reset the watchdog status registers */
587ace1ab86SFrancois Tigeot 	ichwd_sts_reset(sc);
588ace1ab86SFrancois Tigeot 
589ace1ab86SFrancois Tigeot 	/* make sure the WDT starts out inactive */
590ace1ab86SFrancois Tigeot 	ichwd_tmr_disable(sc);
591ace1ab86SFrancois Tigeot 
592ace1ab86SFrancois Tigeot 	/* register the watchdog event handler */
593ace1ab86SFrancois Tigeot 	wdog_register(&ich_wdog);
594ace1ab86SFrancois Tigeot 
595ace1ab86SFrancois Tigeot 	/* disable the SMI handler */
596ace1ab86SFrancois Tigeot 	sc->smi_enabled = ichwd_smi_is_enabled(sc);
597ace1ab86SFrancois Tigeot 	ichwd_smi_disable(sc);
598ace1ab86SFrancois Tigeot 
599ace1ab86SFrancois Tigeot 	/* and enable the watchdog */
600ace1ab86SFrancois Tigeot 	ichwd_tmr_enable(sc);
601ace1ab86SFrancois Tigeot 
602ace1ab86SFrancois Tigeot 	return (0);
603ace1ab86SFrancois Tigeot  fail:
604ace1ab86SFrancois Tigeot 	sc = device_get_softc(dev);
605ace1ab86SFrancois Tigeot 	if (sc->tco_res != NULL)
606ace1ab86SFrancois Tigeot 		bus_release_resource(dev, SYS_RES_IOPORT,
607ace1ab86SFrancois Tigeot 		    sc->tco_rid, sc->tco_res);
608ace1ab86SFrancois Tigeot 	if (sc->smi_res != NULL)
609ace1ab86SFrancois Tigeot 		bus_release_resource(dev, SYS_RES_IOPORT,
610ace1ab86SFrancois Tigeot 		    sc->smi_rid, sc->smi_res);
611ace1ab86SFrancois Tigeot 	if (sc->gcs_res != NULL)
612ace1ab86SFrancois Tigeot 		bus_release_resource(ich, SYS_RES_MEMORY,
613ace1ab86SFrancois Tigeot 		    sc->gcs_rid, sc->gcs_res);
614ace1ab86SFrancois Tigeot 
615ace1ab86SFrancois Tigeot 	return (ENXIO);
616ace1ab86SFrancois Tigeot }
617ace1ab86SFrancois Tigeot 
618ace1ab86SFrancois Tigeot static int
ichwd_detach(device_t dev)619ace1ab86SFrancois Tigeot ichwd_detach(device_t dev)
620ace1ab86SFrancois Tigeot {
621ace1ab86SFrancois Tigeot 	struct ichwd_softc *sc;
622ace1ab86SFrancois Tigeot 	device_t ich = NULL;
623ace1ab86SFrancois Tigeot 
624ace1ab86SFrancois Tigeot 	sc = &ichwd_sc;
625ace1ab86SFrancois Tigeot 
626ace1ab86SFrancois Tigeot 	/* halt the watchdog timer */
627ace1ab86SFrancois Tigeot 	if (sc->active)
628ace1ab86SFrancois Tigeot 		ichwd_tmr_disable(sc);
629ace1ab86SFrancois Tigeot 
630ace1ab86SFrancois Tigeot 	/* enable the SMI handler */
631ace1ab86SFrancois Tigeot 	if (sc->smi_enabled != 0)
632ace1ab86SFrancois Tigeot 		ichwd_smi_enable(sc);
633ace1ab86SFrancois Tigeot 
634ace1ab86SFrancois Tigeot 	/* deregister event handler */
635ace1ab86SFrancois Tigeot 	wdog_unregister(&ich_wdog);
636ace1ab86SFrancois Tigeot 
637ace1ab86SFrancois Tigeot 	/* reset the watchdog status registers */
638ace1ab86SFrancois Tigeot 	ichwd_sts_reset(sc);
639ace1ab86SFrancois Tigeot 
640ace1ab86SFrancois Tigeot 	/* deallocate I/O register space */
641ace1ab86SFrancois Tigeot 	bus_release_resource(dev, SYS_RES_IOPORT, sc->tco_rid, sc->tco_res);
642ace1ab86SFrancois Tigeot 	bus_release_resource(dev, SYS_RES_IOPORT, sc->smi_rid, sc->smi_res);
643ace1ab86SFrancois Tigeot 
644ace1ab86SFrancois Tigeot 	/* deallocate memory resource */
645ace1ab86SFrancois Tigeot 	ich = ichwd_find_ich_lpc_bridge(NULL);
646ace1ab86SFrancois Tigeot 	if (sc->gcs_res && ich)
647ace1ab86SFrancois Tigeot 		bus_release_resource(ich, SYS_RES_MEMORY, sc->gcs_rid, sc->gcs_res);
648ace1ab86SFrancois Tigeot 
649ace1ab86SFrancois Tigeot 	return (0);
650ace1ab86SFrancois Tigeot }
651ace1ab86SFrancois Tigeot 
652ace1ab86SFrancois Tigeot static device_method_t ichwd_methods[] = {
653ace1ab86SFrancois Tigeot 	DEVMETHOD(device_identify, ichwd_identify),
654ace1ab86SFrancois Tigeot 	DEVMETHOD(device_probe,	ichwd_probe),
655ace1ab86SFrancois Tigeot 	DEVMETHOD(device_attach, ichwd_attach),
656ace1ab86SFrancois Tigeot 	DEVMETHOD(device_detach, ichwd_detach),
657ace1ab86SFrancois Tigeot 	DEVMETHOD(device_shutdown, ichwd_detach),
658d3c9c58eSSascha Wildner 	DEVMETHOD_END
659ace1ab86SFrancois Tigeot };
660ace1ab86SFrancois Tigeot 
661ace1ab86SFrancois Tigeot static driver_t ichwd_driver = {
662ace1ab86SFrancois Tigeot 	"ichwd",
663ace1ab86SFrancois Tigeot 	ichwd_methods,
664ace1ab86SFrancois Tigeot 	0
665ace1ab86SFrancois Tigeot };
666ace1ab86SFrancois Tigeot 
667ace1ab86SFrancois Tigeot DRIVER_MODULE(ichwd, isa, ichwd_driver, ichwd_devclass, NULL, NULL);
668ace1ab86SFrancois Tigeot MODULE_VERSION(ichwd, 1);
669