1139749Simp/*-
2129124Sdes * Copyright (c) 2004 Texas A&M University
3129124Sdes * All rights reserved.
4129124Sdes *
5129124Sdes * Developer: Wm. Daryl Hawkins
6129124Sdes *
7129124Sdes * Redistribution and use in source and binary forms, with or without
8129124Sdes * modification, are permitted provided that the following conditions
9129124Sdes * are met:
10129124Sdes * 1. Redistributions of source code must retain the above copyright
11129124Sdes *    notice, this list of conditions and the following disclaimer.
12129124Sdes * 2. Redistributions in binary form must reproduce the above copyright
13129124Sdes *    notice, this list of conditions and the following disclaimer in the
14129124Sdes *    documentation and/or other materials provided with the distribution.
15129124Sdes *
16129124Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17129124Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18129124Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19129124Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20129124Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21129124Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22129124Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23129124Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24129124Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25129124Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26129124Sdes * SUCH DAMAGE.
27129124Sdes */
28129124Sdes
29129124Sdes/*
30129124Sdes * Intel ICH Watchdog Timer (WDT) driver
31129124Sdes *
32129124Sdes * Originally developed by Wm. Daryl Hawkins of Texas A&M
33129124Sdes * Heavily modified by <des@FreeBSD.org>
34129124Sdes *
35129124Sdes * This is a tricky one.  The ICH WDT can't be treated as a regular PCI
36129124Sdes * device as it's actually an integrated function of the ICH LPC interface
37129124Sdes * bridge.  Detection is also awkward, because we can only infer the
38129124Sdes * presence of the watchdog timer from the fact that the machine has an
39129124Sdes * ICH chipset, or, on ACPI 2.x systems, by the presence of the 'WDDT'
40129124Sdes * ACPI table (although this driver does not support the ACPI detection
41129124Sdes * method).
42129124Sdes *
43129124Sdes * There is one slight problem on non-ACPI or ACPI 1.x systems: we have no
44129124Sdes * way of knowing if the WDT is permanently disabled (either by the BIOS
45129124Sdes * or in hardware).
46129124Sdes *
47129124Sdes * The WDT is programmed through I/O registers in the ACPI I/O space.
48129124Sdes * Intel swears it's always at offset 0x60, so we use that.
49129124Sdes *
50129124Sdes * For details about the ICH WDT, see Intel Application Note AP-725
51129124Sdes * (document no. 292273-001).  The WDT is also described in the individual
52129124Sdes * chipset datasheets, e.g. Intel82801EB ICH5 / 82801ER ICH5R Datasheet
53129124Sdes * (document no. 252516-001) sections 9.10 and 9.11.
54171820Sdes *
55171820Sdes * ICH6/7/8 support by Takeharu KATO <takeharu1219@ybb.ne.jp>
56286890Sfabient * SoC PMC support by Denir Li <denir.li@cas-well.com>
57129124Sdes */
58129124Sdes
59129124Sdes#include <sys/cdefs.h>
60129124Sdes__FBSDID("$FreeBSD: stable/11/sys/dev/ichwd/ichwd.c 359361 2020-03-27 15:26:30Z jhibbits $");
61129124Sdes
62129124Sdes#include <sys/param.h>
63129124Sdes#include <sys/kernel.h>
64129879Sphk#include <sys/module.h>
65129124Sdes#include <sys/systm.h>
66129124Sdes#include <sys/bus.h>
67129124Sdes#include <machine/bus.h>
68129124Sdes#include <sys/rman.h>
69129124Sdes#include <machine/resource.h>
70129124Sdes#include <sys/watchdog.h>
71129124Sdes
72199015Savg#include <isa/isavar.h>
73129124Sdes#include <dev/pci/pcivar.h>
74129124Sdes
75129124Sdes#include <dev/ichwd/ichwd.h>
76129124Sdes
77342565Savg#include <x86/pci_cfgreg.h>
78342565Savg#include <dev/pci/pcivar.h>
79342565Savg#include <dev/pci/pci_private.h>
80342565Savg
81129124Sdesstatic struct ichwd_device ichwd_devices[] = {
82286890Sfabient	{ DEVICEID_82801AA,  "Intel 82801AA watchdog timer",	1, 1 },
83286890Sfabient	{ DEVICEID_82801AB,  "Intel 82801AB watchdog timer",	1, 1 },
84286890Sfabient	{ DEVICEID_82801BA,  "Intel 82801BA watchdog timer",	2, 1 },
85286890Sfabient	{ DEVICEID_82801BAM, "Intel 82801BAM watchdog timer",	2, 1 },
86286890Sfabient	{ DEVICEID_82801CA,  "Intel 82801CA watchdog timer",	3, 1 },
87286890Sfabient	{ DEVICEID_82801CAM, "Intel 82801CAM watchdog timer",	3, 1 },
88286890Sfabient	{ DEVICEID_82801DB,  "Intel 82801DB watchdog timer",	4, 1 },
89286890Sfabient	{ DEVICEID_82801DBM, "Intel 82801DBM watchdog timer",	4, 1 },
90286890Sfabient	{ DEVICEID_82801E,   "Intel 82801E watchdog timer",	5, 1 },
91286890Sfabient	{ DEVICEID_82801EB,  "Intel 82801EB watchdog timer",	5, 1 },
92286890Sfabient	{ DEVICEID_82801EBR, "Intel 82801EB/ER watchdog timer",	5, 1 },
93286890Sfabient	{ DEVICEID_6300ESB,  "Intel 6300ESB watchdog timer",	5, 1 },
94286890Sfabient	{ DEVICEID_82801FBR, "Intel 82801FB/FR watchdog timer",	6, 2 },
95286890Sfabient	{ DEVICEID_ICH6M,    "Intel ICH6M watchdog timer",	6, 2 },
96286890Sfabient	{ DEVICEID_ICH6W,    "Intel ICH6W watchdog timer",	6, 2 },
97286890Sfabient	{ DEVICEID_ICH7,     "Intel ICH7 watchdog timer",	7, 2 },
98286890Sfabient	{ DEVICEID_ICH7DH,   "Intel ICH7DH watchdog timer",	7, 2 },
99286890Sfabient	{ DEVICEID_ICH7M,    "Intel ICH7M watchdog timer",	7, 2 },
100286890Sfabient	{ DEVICEID_ICH7MDH,  "Intel ICH7MDH watchdog timer",	7, 2 },
101286890Sfabient	{ DEVICEID_NM10,     "Intel NM10 watchdog timer",	7, 2 },
102286890Sfabient	{ DEVICEID_ICH8,     "Intel ICH8 watchdog timer",	8, 2 },
103286890Sfabient	{ DEVICEID_ICH8DH,   "Intel ICH8DH watchdog timer",	8, 2 },
104286890Sfabient	{ DEVICEID_ICH8DO,   "Intel ICH8DO watchdog timer",	8, 2 },
105286890Sfabient	{ DEVICEID_ICH8M,    "Intel ICH8M watchdog timer",	8, 2 },
106286890Sfabient	{ DEVICEID_ICH8ME,   "Intel ICH8M-E watchdog timer",	8, 2 },
107286890Sfabient	{ DEVICEID_63XXESB,  "Intel 63XXESB watchdog timer",	8, 2 },
108286890Sfabient	{ DEVICEID_ICH9,     "Intel ICH9 watchdog timer",	9, 2 },
109286890Sfabient	{ DEVICEID_ICH9DH,   "Intel ICH9DH watchdog timer",	9, 2 },
110286890Sfabient	{ DEVICEID_ICH9DO,   "Intel ICH9DO watchdog timer",	9, 2 },
111286890Sfabient	{ DEVICEID_ICH9M,    "Intel ICH9M watchdog timer",	9, 2 },
112286890Sfabient	{ DEVICEID_ICH9ME,   "Intel ICH9M-E watchdog timer",	9, 2 },
113286890Sfabient	{ DEVICEID_ICH9R,    "Intel ICH9R watchdog timer",	9, 2 },
114286890Sfabient	{ DEVICEID_ICH10,    "Intel ICH10 watchdog timer",	10, 2 },
115286890Sfabient	{ DEVICEID_ICH10D,   "Intel ICH10D watchdog timer",	10, 2 },
116286890Sfabient	{ DEVICEID_ICH10DO,  "Intel ICH10DO watchdog timer",	10, 2 },
117286890Sfabient	{ DEVICEID_ICH10R,   "Intel ICH10R watchdog timer",	10, 2 },
118286890Sfabient	{ DEVICEID_PCH,      "Intel PCH watchdog timer",	10, 2 },
119286890Sfabient	{ DEVICEID_PCHM,     "Intel PCH watchdog timer",	10, 2 },
120286890Sfabient	{ DEVICEID_P55,      "Intel P55 watchdog timer",	10, 2 },
121286890Sfabient	{ DEVICEID_PM55,     "Intel PM55 watchdog timer",	10, 2 },
122286890Sfabient	{ DEVICEID_H55,      "Intel H55 watchdog timer",	10, 2 },
123286890Sfabient	{ DEVICEID_QM57,     "Intel QM57 watchdog timer",       10, 2 },
124286890Sfabient	{ DEVICEID_H57,      "Intel H57 watchdog timer",        10, 2 },
125286890Sfabient	{ DEVICEID_HM55,     "Intel HM55 watchdog timer",       10, 2 },
126286890Sfabient	{ DEVICEID_Q57,      "Intel Q57 watchdog timer",        10, 2 },
127286890Sfabient	{ DEVICEID_HM57,     "Intel HM57 watchdog timer",       10, 2 },
128286890Sfabient	{ DEVICEID_PCHMSFF,  "Intel PCHMSFF watchdog timer",    10, 2 },
129286890Sfabient	{ DEVICEID_QS57,     "Intel QS57 watchdog timer",       10, 2 },
130286890Sfabient	{ DEVICEID_3400,     "Intel 3400 watchdog timer",       10, 2 },
131286890Sfabient	{ DEVICEID_3420,     "Intel 3420 watchdog timer",       10, 2 },
132286890Sfabient	{ DEVICEID_3450,     "Intel 3450 watchdog timer",       10, 2 },
133286890Sfabient	{ DEVICEID_CPT0,     "Intel Cougar Point watchdog timer",	10, 2 },
134286890Sfabient	{ DEVICEID_CPT1,     "Intel Cougar Point watchdog timer",	10, 2 },
135286890Sfabient	{ DEVICEID_CPT2,     "Intel Cougar Point watchdog timer",	10, 2 },
136286890Sfabient	{ DEVICEID_CPT3,     "Intel Cougar Point watchdog timer",	10, 2 },
137286890Sfabient	{ DEVICEID_CPT4,     "Intel Cougar Point watchdog timer",	10, 2 },
138286890Sfabient	{ DEVICEID_CPT5,     "Intel Cougar Point watchdog timer",	10, 2 },
139286890Sfabient	{ DEVICEID_CPT6,     "Intel Cougar Point watchdog timer",	10, 2 },
140286890Sfabient	{ DEVICEID_CPT7,     "Intel Cougar Point watchdog timer",	10, 2 },
141286890Sfabient	{ DEVICEID_CPT8,     "Intel Cougar Point watchdog timer",	10, 2 },
142286890Sfabient	{ DEVICEID_CPT9,     "Intel Cougar Point watchdog timer",	10, 2 },
143286890Sfabient	{ DEVICEID_CPT10,    "Intel Cougar Point watchdog timer",	10, 2 },
144286890Sfabient	{ DEVICEID_CPT11,    "Intel Cougar Point watchdog timer",	10, 2 },
145286890Sfabient	{ DEVICEID_CPT12,    "Intel Cougar Point watchdog timer",	10, 2 },
146286890Sfabient	{ DEVICEID_CPT13,    "Intel Cougar Point watchdog timer",	10, 2 },
147286890Sfabient	{ DEVICEID_CPT14,    "Intel Cougar Point watchdog timer",	10, 2 },
148286890Sfabient	{ DEVICEID_CPT15,    "Intel Cougar Point watchdog timer",	10, 2 },
149286890Sfabient	{ DEVICEID_CPT16,    "Intel Cougar Point watchdog timer",	10, 2 },
150286890Sfabient	{ DEVICEID_CPT17,    "Intel Cougar Point watchdog timer",	10, 2 },
151286890Sfabient	{ DEVICEID_CPT18,    "Intel Cougar Point watchdog timer",	10, 2 },
152286890Sfabient	{ DEVICEID_CPT19,    "Intel Cougar Point watchdog timer",	10, 2 },
153286890Sfabient	{ DEVICEID_CPT20,    "Intel Cougar Point watchdog timer",	10, 2 },
154286890Sfabient	{ DEVICEID_CPT21,    "Intel Cougar Point watchdog timer",	10, 2 },
155286890Sfabient	{ DEVICEID_CPT22,    "Intel Cougar Point watchdog timer",	10, 2 },
156286890Sfabient	{ DEVICEID_CPT23,    "Intel Cougar Point watchdog timer",	10, 2 },
157286890Sfabient	{ DEVICEID_CPT24,    "Intel Cougar Point watchdog timer",	10, 2 },
158286890Sfabient	{ DEVICEID_CPT25,    "Intel Cougar Point watchdog timer",	10, 2 },
159286890Sfabient	{ DEVICEID_CPT26,    "Intel Cougar Point watchdog timer",	10, 2 },
160286890Sfabient	{ DEVICEID_CPT27,    "Intel Cougar Point watchdog timer",	10, 2 },
161286890Sfabient	{ DEVICEID_CPT28,    "Intel Cougar Point watchdog timer",	10, 2 },
162286890Sfabient	{ DEVICEID_CPT29,    "Intel Cougar Point watchdog timer",	10, 2 },
163286890Sfabient	{ DEVICEID_CPT30,    "Intel Cougar Point watchdog timer",	10, 2 },
164286890Sfabient	{ DEVICEID_CPT31,    "Intel Cougar Point watchdog timer",	10, 2 },
165286890Sfabient	{ DEVICEID_PATSBURG_LPC1, "Intel Patsburg watchdog timer",	10, 2 },
166286890Sfabient	{ DEVICEID_PATSBURG_LPC2, "Intel Patsburg watchdog timer",	10, 2 },
167286890Sfabient	{ DEVICEID_PPT0,     "Intel Panther Point watchdog timer",	10, 2 },
168286890Sfabient	{ DEVICEID_PPT1,     "Intel Panther Point watchdog timer",	10, 2 },
169286890Sfabient	{ DEVICEID_PPT2,     "Intel Panther Point watchdog timer",	10, 2 },
170286890Sfabient	{ DEVICEID_PPT3,     "Intel Panther Point watchdog timer",	10, 2 },
171286890Sfabient	{ DEVICEID_PPT4,     "Intel Panther Point watchdog timer",	10, 2 },
172286890Sfabient	{ DEVICEID_PPT5,     "Intel Panther Point watchdog timer",	10, 2 },
173286890Sfabient	{ DEVICEID_PPT6,     "Intel Panther Point watchdog timer",	10, 2 },
174286890Sfabient	{ DEVICEID_PPT7,     "Intel Panther Point watchdog timer",	10, 2 },
175286890Sfabient	{ DEVICEID_PPT8,     "Intel Panther Point watchdog timer",	10, 2 },
176286890Sfabient	{ DEVICEID_PPT9,     "Intel Panther Point watchdog timer",	10, 2 },
177286890Sfabient	{ DEVICEID_PPT10,    "Intel Panther Point watchdog timer",	10, 2 },
178286890Sfabient	{ DEVICEID_PPT11,    "Intel Panther Point watchdog timer",	10, 2 },
179286890Sfabient	{ DEVICEID_PPT12,    "Intel Panther Point watchdog timer",	10, 2 },
180286890Sfabient	{ DEVICEID_PPT13,    "Intel Panther Point watchdog timer",	10, 2 },
181286890Sfabient	{ DEVICEID_PPT14,    "Intel Panther Point watchdog timer",	10, 2 },
182286890Sfabient	{ DEVICEID_PPT15,    "Intel Panther Point watchdog timer",	10, 2 },
183286890Sfabient	{ DEVICEID_PPT16,    "Intel Panther Point watchdog timer",	10, 2 },
184286890Sfabient	{ DEVICEID_PPT17,    "Intel Panther Point watchdog timer",	10, 2 },
185286890Sfabient	{ DEVICEID_PPT18,    "Intel Panther Point watchdog timer",	10, 2 },
186286890Sfabient	{ DEVICEID_PPT19,    "Intel Panther Point watchdog timer",	10, 2 },
187286890Sfabient	{ DEVICEID_PPT20,    "Intel Panther Point watchdog timer",	10, 2 },
188286890Sfabient	{ DEVICEID_PPT21,    "Intel Panther Point watchdog timer",	10, 2 },
189286890Sfabient	{ DEVICEID_PPT22,    "Intel Panther Point watchdog timer",	10, 2 },
190286890Sfabient	{ DEVICEID_PPT23,    "Intel Panther Point watchdog timer",	10, 2 },
191286890Sfabient	{ DEVICEID_PPT24,    "Intel Panther Point watchdog timer",	10, 2 },
192286890Sfabient	{ DEVICEID_PPT25,    "Intel Panther Point watchdog timer",	10, 2 },
193286890Sfabient	{ DEVICEID_PPT26,    "Intel Panther Point watchdog timer",	10, 2 },
194286890Sfabient	{ DEVICEID_PPT27,    "Intel Panther Point watchdog timer",	10, 2 },
195286890Sfabient	{ DEVICEID_PPT28,    "Intel Panther Point watchdog timer",	10, 2 },
196286890Sfabient	{ DEVICEID_PPT29,    "Intel Panther Point watchdog timer",	10, 2 },
197286890Sfabient	{ DEVICEID_PPT30,    "Intel Panther Point watchdog timer",	10, 2 },
198286890Sfabient	{ DEVICEID_PPT31,    "Intel Panther Point watchdog timer",	10, 2 },
199286890Sfabient	{ DEVICEID_LPT0,     "Intel Lynx Point watchdog timer",		10, 2 },
200286890Sfabient	{ DEVICEID_LPT1,     "Intel Lynx Point watchdog timer",		10, 2 },
201286890Sfabient	{ DEVICEID_LPT2,     "Intel Lynx Point watchdog timer",		10, 2 },
202286890Sfabient	{ DEVICEID_LPT3,     "Intel Lynx Point watchdog timer",		10, 2 },
203286890Sfabient	{ DEVICEID_LPT4,     "Intel Lynx Point watchdog timer",		10, 2 },
204286890Sfabient	{ DEVICEID_LPT5,     "Intel Lynx Point watchdog timer",		10, 2 },
205286890Sfabient	{ DEVICEID_LPT6,     "Intel Lynx Point watchdog timer",		10, 2 },
206286890Sfabient	{ DEVICEID_LPT7,     "Intel Lynx Point watchdog timer",		10, 2 },
207286890Sfabient	{ DEVICEID_LPT8,     "Intel Lynx Point watchdog timer",		10, 2 },
208286890Sfabient	{ DEVICEID_LPT9,     "Intel Lynx Point watchdog timer",		10, 2 },
209286890Sfabient	{ DEVICEID_LPT10,    "Intel Lynx Point watchdog timer",		10, 2 },
210286890Sfabient	{ DEVICEID_LPT11,    "Intel Lynx Point watchdog timer",		10, 2 },
211286890Sfabient	{ DEVICEID_LPT12,    "Intel Lynx Point watchdog timer",		10, 2 },
212286890Sfabient	{ DEVICEID_LPT13,    "Intel Lynx Point watchdog timer",		10, 2 },
213286890Sfabient	{ DEVICEID_LPT14,    "Intel Lynx Point watchdog timer",		10, 2 },
214286890Sfabient	{ DEVICEID_LPT15,    "Intel Lynx Point watchdog timer",		10, 2 },
215286890Sfabient	{ DEVICEID_LPT16,    "Intel Lynx Point watchdog timer",		10, 2 },
216286890Sfabient	{ DEVICEID_LPT17,    "Intel Lynx Point watchdog timer",		10, 2 },
217286890Sfabient	{ DEVICEID_LPT18,    "Intel Lynx Point watchdog timer",		10, 2 },
218286890Sfabient	{ DEVICEID_LPT19,    "Intel Lynx Point watchdog timer",		10, 2 },
219286890Sfabient	{ DEVICEID_LPT20,    "Intel Lynx Point watchdog timer",		10, 2 },
220286890Sfabient	{ DEVICEID_LPT21,    "Intel Lynx Point watchdog timer",		10, 2 },
221286890Sfabient	{ DEVICEID_LPT22,    "Intel Lynx Point watchdog timer",		10, 2 },
222286890Sfabient	{ DEVICEID_LPT23,    "Intel Lynx Point watchdog timer",		10, 2 },
223286890Sfabient	{ DEVICEID_LPT24,    "Intel Lynx Point watchdog timer",		10, 2 },
224286890Sfabient	{ DEVICEID_LPT25,    "Intel Lynx Point watchdog timer",		10, 2 },
225286890Sfabient	{ DEVICEID_LPT26,    "Intel Lynx Point watchdog timer",		10, 2 },
226286890Sfabient	{ DEVICEID_LPT27,    "Intel Lynx Point watchdog timer",		10, 2 },
227286890Sfabient	{ DEVICEID_LPT28,    "Intel Lynx Point watchdog timer",		10, 2 },
228286890Sfabient	{ DEVICEID_LPT29,    "Intel Lynx Point watchdog timer",		10, 2 },
229286890Sfabient	{ DEVICEID_LPT30,    "Intel Lynx Point watchdog timer",		10, 2 },
230286890Sfabient	{ DEVICEID_LPT31,    "Intel Lynx Point watchdog timer",		10, 2 },
231286890Sfabient	{ DEVICEID_WCPT1,    "Intel Wildcat Point watchdog timer",	10, 2 },
232286890Sfabient	{ DEVICEID_WCPT2,    "Intel Wildcat Point watchdog timer",	10, 2 },
233286890Sfabient	{ DEVICEID_WCPT3,    "Intel Wildcat Point watchdog timer",	10, 2 },
234286890Sfabient	{ DEVICEID_WCPT4,    "Intel Wildcat Point watchdog timer",	10, 2 },
235286890Sfabient	{ DEVICEID_WCPT6,    "Intel Wildcat Point watchdog timer",	10, 2 },
236286890Sfabient	{ DEVICEID_WBG0,     "Intel Wellsburg watchdog timer",		10, 2 },
237286890Sfabient	{ DEVICEID_WBG1,     "Intel Wellsburg watchdog timer",		10, 2 },
238286890Sfabient	{ DEVICEID_WBG2,     "Intel Wellsburg watchdog timer",		10, 2 },
239286890Sfabient	{ DEVICEID_WBG3,     "Intel Wellsburg watchdog timer",		10, 2 },
240286890Sfabient	{ DEVICEID_WBG4,     "Intel Wellsburg watchdog timer",		10, 2 },
241286890Sfabient	{ DEVICEID_WBG5,     "Intel Wellsburg watchdog timer",		10, 2 },
242286890Sfabient	{ DEVICEID_WBG6,     "Intel Wellsburg watchdog timer",		10, 2 },
243286890Sfabient	{ DEVICEID_WBG7,     "Intel Wellsburg watchdog timer",		10, 2 },
244286890Sfabient	{ DEVICEID_WBG8,     "Intel Wellsburg watchdog timer",		10, 2 },
245286890Sfabient	{ DEVICEID_WBG9,     "Intel Wellsburg watchdog timer",		10, 2 },
246286890Sfabient	{ DEVICEID_WBG10,    "Intel Wellsburg watchdog timer",		10, 2 },
247286890Sfabient	{ DEVICEID_WBG11,    "Intel Wellsburg watchdog timer",		10, 2 },
248286890Sfabient	{ DEVICEID_WBG12,    "Intel Wellsburg watchdog timer",		10, 2 },
249286890Sfabient	{ DEVICEID_WBG13,    "Intel Wellsburg watchdog timer",		10, 2 },
250286890Sfabient	{ DEVICEID_WBG14,    "Intel Wellsburg watchdog timer",		10, 2 },
251286890Sfabient	{ DEVICEID_WBG15,    "Intel Wellsburg watchdog timer",		10, 2 },
252286890Sfabient	{ DEVICEID_WBG16,    "Intel Wellsburg watchdog timer",		10, 2 },
253286890Sfabient	{ DEVICEID_WBG17,    "Intel Wellsburg watchdog timer",		10, 2 },
254286890Sfabient	{ DEVICEID_WBG18,    "Intel Wellsburg watchdog timer",		10, 2 },
255286890Sfabient	{ DEVICEID_WBG19,    "Intel Wellsburg watchdog timer",		10, 2 },
256286890Sfabient	{ DEVICEID_WBG20,    "Intel Wellsburg watchdog timer",		10, 2 },
257286890Sfabient	{ DEVICEID_WBG21,    "Intel Wellsburg watchdog timer",		10, 2 },
258286890Sfabient	{ DEVICEID_WBG22,    "Intel Wellsburg watchdog timer",		10, 2 },
259286890Sfabient	{ DEVICEID_WBG23,    "Intel Wellsburg watchdog timer",		10, 2 },
260286890Sfabient	{ DEVICEID_WBG24,    "Intel Wellsburg watchdog timer",		10, 2 },
261286890Sfabient	{ DEVICEID_WBG25,    "Intel Wellsburg watchdog timer",		10, 2 },
262286890Sfabient	{ DEVICEID_WBG26,    "Intel Wellsburg watchdog timer",		10, 2 },
263286890Sfabient	{ DEVICEID_WBG27,    "Intel Wellsburg watchdog timer",		10, 2 },
264286890Sfabient	{ DEVICEID_WBG28,    "Intel Wellsburg watchdog timer",		10, 2 },
265286890Sfabient	{ DEVICEID_WBG29,    "Intel Wellsburg watchdog timer",		10, 2 },
266286890Sfabient	{ DEVICEID_WBG30,    "Intel Wellsburg watchdog timer",		10, 2 },
267286890Sfabient	{ DEVICEID_WBG31,    "Intel Wellsburg watchdog timer",		10, 2 },
268286890Sfabient	{ DEVICEID_LPT_LP0,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
269286890Sfabient	{ DEVICEID_LPT_LP1,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
270286890Sfabient	{ DEVICEID_LPT_LP2,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
271286890Sfabient	{ DEVICEID_LPT_LP3,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
272286890Sfabient	{ DEVICEID_LPT_LP4,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
273286890Sfabient	{ DEVICEID_LPT_LP5,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
274286890Sfabient	{ DEVICEID_LPT_LP6,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
275286890Sfabient	{ DEVICEID_LPT_LP7,  "Intel Lynx Point-LP watchdog timer",	10, 2 },
276286890Sfabient	{ DEVICEID_WCPT_LP1, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
277286890Sfabient	{ DEVICEID_WCPT_LP2, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
278286890Sfabient	{ DEVICEID_WCPT_LP3, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
279286890Sfabient	{ DEVICEID_WCPT_LP5, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
280286890Sfabient	{ DEVICEID_WCPT_LP6, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
281286890Sfabient	{ DEVICEID_WCPT_LP7, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
282286890Sfabient	{ DEVICEID_WCPT_LP9, "Intel Wildcat Point-LP watchdog timer",	10, 2 },
283286890Sfabient	{ DEVICEID_DH89XXCC_LPC,  "Intel DH89xxCC watchdog timer",	10, 2 },
284286890Sfabient	{ DEVICEID_COLETOCRK_LPC, "Intel Coleto Creek watchdog timer",  10, 2 },
285286890Sfabient	{ DEVICEID_AVN0,     "Intel Avoton/Rangeley SoC watchdog timer",10, 3 },
286286890Sfabient	{ DEVICEID_AVN1,     "Intel Avoton/Rangeley SoC watchdog timer",10, 3 },
287286890Sfabient	{ DEVICEID_AVN2,     "Intel Avoton/Rangeley SoC watchdog timer",10, 3 },
288286890Sfabient	{ DEVICEID_AVN3,     "Intel Avoton/Rangeley SoC watchdog timer",10, 3 },
289286890Sfabient	{ DEVICEID_BAYTRAIL, "Intel Bay Trail SoC watchdog timer",	10, 3 },
290286890Sfabient	{ DEVICEID_BRASWELL, "Intel Braswell SoC watchdog timer",	10, 3 },
291286890Sfabient	{ 0, NULL, 0, 0 },
292129124Sdes};
293129124Sdes
294340182Savgstatic struct ichwd_device ichwd_smb_devices[] = {
295340182Savg	{ DEVICEID_LEWISBURG_SMB, "Lewisburg watchdog timer",		10, 4 },
296342567Savg	{ DEVICEID_SRPTLP_SMB,    "Sunrise Point-LP watchdog timer",	10, 4 },
297359361Sjhibbits	{ DEVICEID_C3000,         "Intel Atom C3000 watchdog timer",	10, 4 },
298340182Savg	{ 0, NULL, 0, 0 },
299340182Savg};
300340182Savg
301129124Sdesstatic devclass_t ichwd_devclass;
302129124Sdes
303155785Sambrisko#define ichwd_read_tco_1(sc, off) \
304229598Sjhb	bus_read_1((sc)->tco_res, (off))
305155785Sambrisko#define ichwd_read_tco_2(sc, off) \
306229598Sjhb	bus_read_2((sc)->tco_res, (off))
307155785Sambrisko#define ichwd_read_tco_4(sc, off) \
308229598Sjhb	bus_read_4((sc)->tco_res, (off))
309171820Sdes#define ichwd_read_smi_4(sc, off) \
310229598Sjhb	bus_read_4((sc)->smi_res, (off))
311171820Sdes#define ichwd_read_gcs_4(sc, off) \
312229598Sjhb	bus_read_4((sc)->gcs_res, (off))
313286890Sfabient/* NB: TCO version 3 devices use the gcs_res resource for the PMC register. */
314286890Sfabient#define ichwd_read_pmc_4(sc, off) \
315286890Sfabient	bus_read_4((sc)->gcs_res, (off))
316342565Savg#define ichwd_read_gc_4(sc, off) \
317342565Savg	bus_read_4((sc)->gc_res, (off))
318155785Sambrisko
319155785Sambrisko#define ichwd_write_tco_1(sc, off, val) \
320229598Sjhb	bus_write_1((sc)->tco_res, (off), (val))
321155785Sambrisko#define ichwd_write_tco_2(sc, off, val) \
322229598Sjhb	bus_write_2((sc)->tco_res, (off), (val))
323155785Sambrisko#define ichwd_write_tco_4(sc, off, val) \
324229598Sjhb	bus_write_4((sc)->tco_res, (off), (val))
325155785Sambrisko#define ichwd_write_smi_4(sc, off, val) \
326229598Sjhb	bus_write_4((sc)->smi_res, (off), (val))
327171820Sdes#define ichwd_write_gcs_4(sc, off, val) \
328229598Sjhb	bus_write_4((sc)->gcs_res, (off), (val))
329286890Sfabient/* NB: TCO version 3 devices use the gcs_res resource for the PMC register. */
330286890Sfabient#define ichwd_write_pmc_4(sc, off, val) \
331286890Sfabient	bus_write_4((sc)->gcs_res, (off), (val))
332342565Savg#define ichwd_write_gc_4(sc, off, val) \
333342565Savg	bus_write_4((sc)->gc_res, (off), (val))
334129124Sdes
335171820Sdes#define ichwd_verbose_printf(dev, ...) \
336171820Sdes	do {						\
337171820Sdes		if (bootverbose)			\
338171820Sdes			device_printf(dev, __VA_ARGS__);\
339171820Sdes	} while (0)
340171820Sdes
341190030Sdes/*
342190030Sdes * Disable the watchdog timeout SMI handler.
343190030Sdes *
344190030Sdes * Apparently, some BIOSes install handlers that reset or disable the
345190030Sdes * watchdog timer instead of resetting the system, so we disable the SMI
346190030Sdes * (by clearing the SMI_TCO_EN bit of the SMI_EN register) to prevent this
347190030Sdes * from happening.
348190030Sdes */
349129124Sdesstatic __inline void
350190030Sdesichwd_smi_disable(struct ichwd_softc *sc)
351129124Sdes{
352155785Sambrisko	ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) & ~SMI_TCO_EN);
353129124Sdes}
354129124Sdes
355190030Sdes/*
356190030Sdes * Enable the watchdog timeout SMI handler.  See above for details.
357190030Sdes */
358129124Sdesstatic __inline void
359190030Sdesichwd_smi_enable(struct ichwd_softc *sc)
360129124Sdes{
361155785Sambrisko	ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) | SMI_TCO_EN);
362129124Sdes}
363129124Sdes
364190030Sdes/*
365221016Sattilio * Check if the watchdog SMI triggering is enabled.
366221016Sattilio */
367221016Sattiliostatic __inline int
368221016Sattilioichwd_smi_is_enabled(struct ichwd_softc *sc)
369221016Sattilio{
370221016Sattilio	return ((ichwd_read_smi_4(sc, SMI_EN) & SMI_TCO_EN) != 0);
371221016Sattilio}
372221016Sattilio
373221016Sattilio/*
374190030Sdes * Reset the watchdog status bits.
375190030Sdes */
376129124Sdesstatic __inline void
377129124Sdesichwd_sts_reset(struct ichwd_softc *sc)
378129124Sdes{
379190030Sdes	/*
380190030Sdes	 * The watchdog status bits are set to 1 by the hardware to
381190030Sdes	 * indicate various conditions.  They can be cleared by software
382190030Sdes	 * by writing a 1, not a 0.
383190030Sdes	 */
384155785Sambrisko	ichwd_write_tco_2(sc, TCO1_STS, TCO_TIMEOUT);
385221010Sdes	/*
386221010Sdes	 * According to Intel's docs, clearing SECOND_TO_STS and BOOT_STS must
387215868Sattilio	 * be done in two separate operations.
388190030Sdes	 */
389215868Sattilio	ichwd_write_tco_2(sc, TCO2_STS, TCO_SECOND_TO_STS);
390340182Savg	if (sc->tco_version < 4)
391340182Savg		ichwd_write_tco_2(sc, TCO2_STS, TCO_BOOT_STS);
392129124Sdes}
393129124Sdes
394190030Sdes/*
395190030Sdes * Enable the watchdog timer by clearing the TCO_TMR_HALT bit in the
396190030Sdes * TCO1_CNT register.  This is complicated by the need to preserve bit 9
397190030Sdes * of that same register, and the requirement that all other bits must be
398190030Sdes * written back as zero.
399190030Sdes */
400129124Sdesstatic __inline void
401129124Sdesichwd_tmr_enable(struct ichwd_softc *sc)
402129124Sdes{
403129124Sdes	uint16_t cnt;
404129124Sdes
405155785Sambrisko	cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
406155785Sambrisko	ichwd_write_tco_2(sc, TCO1_CNT, cnt & ~TCO_TMR_HALT);
407129124Sdes	sc->active = 1;
408171820Sdes	ichwd_verbose_printf(sc->device, "timer enabled\n");
409129124Sdes}
410129124Sdes
411190030Sdes/*
412190030Sdes * Disable the watchdog timer.  See above for details.
413190030Sdes */
414129124Sdesstatic __inline void
415129124Sdesichwd_tmr_disable(struct ichwd_softc *sc)
416129124Sdes{
417129124Sdes	uint16_t cnt;
418129124Sdes
419155785Sambrisko	cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
420155785Sambrisko	ichwd_write_tco_2(sc, TCO1_CNT, cnt | TCO_TMR_HALT);
421129124Sdes	sc->active = 0;
422171820Sdes	ichwd_verbose_printf(sc->device, "timer disabled\n");
423129124Sdes}
424129124Sdes
425190030Sdes/*
426190030Sdes * Reload the watchdog timer: writing anything to any of the lower five
427190030Sdes * bits of the TCO_RLD register reloads the timer from the last value
428190030Sdes * written to TCO_TMR.
429190030Sdes */
430129124Sdesstatic __inline void
431129124Sdesichwd_tmr_reload(struct ichwd_softc *sc)
432129124Sdes{
433286890Sfabient	if (sc->tco_version == 1)
434171820Sdes		ichwd_write_tco_1(sc, TCO_RLD, 1);
435171820Sdes	else
436171820Sdes		ichwd_write_tco_2(sc, TCO_RLD, 1);
437129124Sdes}
438129124Sdes
439190030Sdes/*
440190030Sdes * Set the initial timeout value.  Note that this must always be followed
441190030Sdes * by a reload.
442190030Sdes */
443129124Sdesstatic __inline void
444171820Sdesichwd_tmr_set(struct ichwd_softc *sc, unsigned int timeout)
445129124Sdes{
446171820Sdes
447216298Sattilio	if (timeout < TCO_RLD_TMR_MIN)
448216298Sattilio		timeout = TCO_RLD_TMR_MIN;
449171820Sdes
450286890Sfabient	if (sc->tco_version == 1) {
451171820Sdes		uint8_t tmr_val8 = ichwd_read_tco_1(sc, TCO_TMR1);
452171820Sdes
453216298Sattilio		tmr_val8 &= (~TCO_RLD1_TMR_MAX & 0xff);
454216298Sattilio		if (timeout > TCO_RLD1_TMR_MAX)
455216298Sattilio			timeout = TCO_RLD1_TMR_MAX;
456171820Sdes		tmr_val8 |= timeout;
457171820Sdes		ichwd_write_tco_1(sc, TCO_TMR1, tmr_val8);
458171820Sdes	} else {
459171820Sdes		uint16_t tmr_val16 = ichwd_read_tco_2(sc, TCO_TMR2);
460171820Sdes
461216298Sattilio		tmr_val16 &= (~TCO_RLD2_TMR_MAX & 0xffff);
462216298Sattilio		if (timeout > TCO_RLD2_TMR_MAX)
463216298Sattilio			timeout = TCO_RLD2_TMR_MAX;
464171820Sdes		tmr_val16 |= timeout;
465171820Sdes		ichwd_write_tco_2(sc, TCO_TMR2, tmr_val16);
466171820Sdes	}
467171820Sdes
468129124Sdes	sc->timeout = timeout;
469171820Sdes
470171820Sdes	ichwd_verbose_printf(sc->device, "timeout set to %u ticks\n", timeout);
471129124Sdes}
472129124Sdes
473171820Sdesstatic __inline int
474171820Sdesichwd_clear_noreboot(struct ichwd_softc *sc)
475171820Sdes{
476171820Sdes	uint32_t status;
477171820Sdes	int rc = 0;
478171820Sdes
479171820Sdes	/* try to clear the NO_REBOOT bit */
480286890Sfabient	switch (sc->tco_version) {
481286890Sfabient	case 1:
482171820Sdes		status = pci_read_config(sc->ich, ICH_GEN_STA, 1);
483171820Sdes		status &= ~ICH_GEN_STA_NO_REBOOT;
484171820Sdes		pci_write_config(sc->ich, ICH_GEN_STA, status, 1);
485171820Sdes		status = pci_read_config(sc->ich, ICH_GEN_STA, 1);
486171820Sdes		if (status & ICH_GEN_STA_NO_REBOOT)
487171820Sdes			rc = EIO;
488286890Sfabient		break;
489286890Sfabient	case 2:
490171820Sdes		status = ichwd_read_gcs_4(sc, 0);
491171820Sdes		status &= ~ICH_GCS_NO_REBOOT;
492171820Sdes		ichwd_write_gcs_4(sc, 0, status);
493171820Sdes		status = ichwd_read_gcs_4(sc, 0);
494171820Sdes		if (status & ICH_GCS_NO_REBOOT)
495171820Sdes			rc = EIO;
496286890Sfabient		break;
497286890Sfabient	case 3:
498286890Sfabient		status = ichwd_read_pmc_4(sc, 0);
499286890Sfabient		status &= ~ICH_PMC_NO_REBOOT;
500286890Sfabient		ichwd_write_pmc_4(sc, 0, status);
501286890Sfabient		status = ichwd_read_pmc_4(sc, 0);
502286890Sfabient		if (status & ICH_PMC_NO_REBOOT)
503286890Sfabient			rc = EIO;
504286890Sfabient		break;
505340182Savg	case 4:
506342565Savg		status = ichwd_read_gc_4(sc, 0);
507342565Savg		status &= ~SMB_GC_NO_REBOOT;
508342565Savg		ichwd_write_gc_4(sc, 0, status);
509342565Savg		status = ichwd_read_gc_4(sc, 0);
510342565Savg		if (status & SMB_GC_NO_REBOOT)
511342565Savg			rc = EIO;
512340182Savg		break;
513286890Sfabient	default:
514286890Sfabient		ichwd_verbose_printf(sc->device,
515286890Sfabient		    "Unknown TCO Version: %d, can't set NO_REBOOT.\n",
516286890Sfabient		    sc->tco_version);
517286890Sfabient		break;
518171820Sdes	}
519171820Sdes
520171820Sdes	if (rc)
521171820Sdes		device_printf(sc->device,
522171820Sdes		    "ICH WDT present but disabled in BIOS or hardware\n");
523171820Sdes
524171820Sdes	return (rc);
525171820Sdes}
526171820Sdes
527129124Sdes/*
528190030Sdes * Watchdog event handler - called by the framework to enable or disable
529190030Sdes * the watchdog or change the initial timeout value.
530129124Sdes */
531129124Sdesstatic void
532129124Sdesichwd_event(void *arg, unsigned int cmd, int *error)
533129124Sdes{
534129124Sdes	struct ichwd_softc *sc = arg;
535129124Sdes	unsigned int timeout;
536129124Sdes
537165260Sn_hibma	/* convert from power-of-two-ns to WDT ticks */
538165260Sn_hibma	cmd &= WD_INTERVAL;
539323672Skib
540323672Skib	if (sc->tco_version == 3) {
541323672Skib		timeout = ((uint64_t)1 << cmd) / ICHWD_TCO_V3_TICK;
542323672Skib	} else {
543323672Skib		timeout = ((uint64_t)1 << cmd) / ICHWD_TICK;
544323672Skib	}
545323672Skib
546171820Sdes	if (cmd) {
547225340Sdelphij		if (!sc->active)
548225340Sdelphij			ichwd_tmr_enable(sc);
549225340Sdelphij		if (timeout != sc->timeout)
550165260Sn_hibma			ichwd_tmr_set(sc, timeout);
551165260Sn_hibma		ichwd_tmr_reload(sc);
552165260Sn_hibma		*error = 0;
553165260Sn_hibma	} else {
554129124Sdes		if (sc->active)
555129124Sdes			ichwd_tmr_disable(sc);
556129124Sdes	}
557129124Sdes}
558129124Sdes
559171820Sdesstatic device_t
560322115Smavichwd_find_ich_lpc_bridge(device_t isa, struct ichwd_device **id_p)
561171820Sdes{
562171820Sdes	struct ichwd_device *id;
563322115Smav	device_t isab, pci;
564322115Smav	uint16_t devid;
565129124Sdes
566322115Smav	/* Check whether parent ISA bridge looks familiar. */
567322115Smav	isab = device_get_parent(isa);
568322115Smav	pci = device_get_parent(isab);
569322115Smav	if (pci == NULL || device_get_devclass(pci) != devclass_find("pci"))
570171820Sdes		return (NULL);
571322115Smav	if (pci_get_vendor(isab) != VENDORID_INTEL)
572322115Smav		return (NULL);
573322115Smav	devid = pci_get_device(isab);
574322115Smav	for (id = ichwd_devices; id->desc != NULL; ++id) {
575322115Smav		if (devid == id->device) {
576322115Smav			if (id_p != NULL)
577322115Smav				*id_p = id;
578322115Smav			return (isab);
579322115Smav		}
580322115Smav	}
581171820Sdes
582322115Smav	return (NULL);
583171820Sdes}
584171820Sdes
585340182Savgstatic device_t
586340182Savgichwd_find_smb_dev(device_t isa, struct ichwd_device **id_p)
587340182Savg{
588340182Savg	struct ichwd_device *id;
589340182Savg	device_t isab, smb;
590340182Savg	uint16_t devid;
591340182Savg
592340182Savg	/*
593340182Savg	 * Check if SMBus controller provides TCO configuration.
594340182Savg	 * The controller's device and function are fixed and we expect
595340182Savg	 * it to be on the same bus as ISA bridge.
596340182Savg	 */
597340182Savg	isab = device_get_parent(isa);
598340182Savg	smb = pci_find_dbsf(pci_get_domain(isab), pci_get_bus(isab), 31, 4);
599340182Savg	if (smb == NULL)
600340182Savg		return (NULL);
601340182Savg	if (pci_get_vendor(smb) != VENDORID_INTEL)
602340182Savg		return (NULL);
603340182Savg	devid = pci_get_device(smb);
604340182Savg	for (id = ichwd_smb_devices; id->desc != NULL; ++id) {
605340182Savg		if (devid == id->device) {
606340182Savg			if (id_p != NULL)
607340182Savg				*id_p = id;
608340182Savg			return (smb);
609340182Savg		}
610340182Savg	}
611340182Savg
612340182Savg	return (NULL);
613340182Savg}
614340182Savg
615129124Sdes/*
616129124Sdes * Look for an ICH LPC interface bridge.  If one is found, register an
617129124Sdes * ichwd device.  There can be only one.
618129124Sdes */
619129124Sdesstatic void
620129124Sdesichwd_identify(driver_t *driver, device_t parent)
621129124Sdes{
622171820Sdes	struct ichwd_device *id_p;
623340182Savg	device_t ich, smb;
624129124Sdes	device_t dev;
625342565Savg	uint64_t base_address64;
626286890Sfabient	uint32_t base_address;
627340182Savg	uint32_t ctl;
628171820Sdes	int rc;
629129124Sdes
630322115Smav	ich = ichwd_find_ich_lpc_bridge(parent, &id_p);
631340182Savg	if (ich == NULL) {
632340182Savg		smb = ichwd_find_smb_dev(parent, &id_p);
633340182Savg		if (smb == NULL)
634340182Savg			return;
635340182Savg	}
636129124Sdes
637342569Savg	KASSERT(id_p->tco_version >= 1,
638342569Savg	    ("unexpected TCO version %d", id_p->tco_version));
639342569Savg	KASSERT(id_p->tco_version != 4 || smb != NULL,
640342569Savg	    ("could not find PCI SMBus device for TCOv4"));
641342569Savg	KASSERT(id_p->tco_version >= 4 || ich != NULL,
642342569Savg	    ("could not find PCI LPC bridge device for TCOv1-3"));
643342569Savg
644171820Sdes	/* good, add child to bus */
645171820Sdes	if ((dev = device_find_child(parent, driver->name, 0)) == NULL)
646171820Sdes		dev = BUS_ADD_CHILD(parent, 0, driver->name, 0);
647129124Sdes
648171820Sdes	if (dev == NULL)
649129124Sdes		return;
650129124Sdes
651286890Sfabient	switch (id_p->tco_version) {
652286890Sfabient	case 1:
653286890Sfabient		break;
654286890Sfabient	case 2:
655171820Sdes		/* get RCBA (root complex base address) */
656286890Sfabient		base_address = pci_read_config(ich, ICH_RCBA, 4);
657171820Sdes		rc = bus_set_resource(ich, SYS_RES_MEMORY, 0,
658286890Sfabient		    (base_address & 0xffffc000) + ICH_GCS_OFFSET,
659286890Sfabient		    ICH_GCS_SIZE);
660171820Sdes		if (rc)
661171820Sdes			ichwd_verbose_printf(dev,
662286890Sfabient			    "Can not set TCO v%d memory resource for RCBA\n",
663286890Sfabient			    id_p->tco_version);
664286890Sfabient		break;
665286890Sfabient	case 3:
666286890Sfabient		/* get PBASE (Power Management Controller base address) */
667286890Sfabient		base_address = pci_read_config(ich, ICH_PBASE, 4);
668286890Sfabient		rc = bus_set_resource(ich, SYS_RES_MEMORY, 0,
669286890Sfabient		    (base_address & 0xfffffe00) + ICH_PMC_OFFSET,
670286890Sfabient		    ICH_PMC_SIZE);
671286890Sfabient		if (rc)
672286890Sfabient			ichwd_verbose_printf(dev,
673286890Sfabient			    "Can not set TCO v%d memory resource for PBASE\n",
674286890Sfabient			    id_p->tco_version);
675286890Sfabient		break;
676340182Savg	case 4:
677340182Savg		/* Get TCO base address. */
678340182Savg		ctl = pci_read_config(smb, ICH_TCOCTL, 4);
679340182Savg		if ((ctl & ICH_TCOCTL_TCO_BASE_EN) == 0) {
680340182Savg			ichwd_verbose_printf(dev,
681340182Savg			    "TCO v%d decoding is not enabled\n",
682340182Savg			    id_p->tco_version);
683340182Savg			break;
684340182Savg		}
685340182Savg		base_address = pci_read_config(smb, ICH_TCOBASE, 4);
686340182Savg		rc = bus_set_resource(dev, SYS_RES_IOPORT, 0,
687340182Savg		    base_address & ICH_TCOBASE_ADDRMASK, ICH_TCOBASE_SIZE);
688340182Savg		if (rc != 0) {
689340182Savg			ichwd_verbose_printf(dev,
690340182Savg			    "Can not set TCO v%d I/O resource (err = %d)\n",
691340182Savg			    id_p->tco_version, rc);
692340182Savg		}
693342565Savg
694342565Savg		/*
695342565Savg		 * Unhide Primary to Sideband Bridge (P2SB) PCI device, so that
696342565Savg		 * we can discover the base address of Private Configuration
697342565Savg		 * Space via the bridge's BAR.
698342565Savg		 * Then hide back the bridge.
699342565Savg		 */
700342565Savg		pci_cfgregwrite(0, 31, 1, 0xe1, 0, 1);
701342565Savg		base_address64 = pci_cfgregread(0, 31, 1, SBREG_BAR + 4, 4);
702342565Savg		base_address64 <<= 32;
703342565Savg		base_address64 |= pci_cfgregread(0, 31, 1, SBREG_BAR, 4);
704342565Savg		base_address64 &= ~0xfull;
705342565Savg		pci_cfgregwrite(0, 31, 1, 0xe1, 1, 1);
706342565Savg
707342565Savg		/*
708342565Savg		 * No Reboot bit is in General Control register, offset 0xc,
709342565Savg		 * within the SMBus target port, ID 0xc6.
710342565Savg		 */
711342565Savg		base_address64 += PCR_REG_OFF(SMB_PORT_ID, SMB_GC_REG);
712342565Savg		rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, base_address64,
713342565Savg		    SMB_GC_SIZE);
714342565Savg		if (rc != 0) {
715342565Savg			ichwd_verbose_printf(dev,
716342565Savg			    "Can not set TCO v%d PCR I/O resource (err = %d)\n",
717342565Savg			    id_p->tco_version, rc);
718342565Savg		}
719342565Savg
720340182Savg		break;
721286890Sfabient	default:
722286890Sfabient		ichwd_verbose_printf(dev,
723286890Sfabient		    "Can not set unknown TCO v%d memory resource for unknown base address\n",
724286890Sfabient		    id_p->tco_version);
725286890Sfabient		break;
726129124Sdes	}
727129124Sdes}
728129124Sdes
729129124Sdesstatic int
730129256Sdesichwd_probe(device_t dev)
731129256Sdes{
732297255Smav	struct ichwd_device *id_p;
733171820Sdes
734199015Savg	/* Do not claim some ISA PnP device by accident. */
735199015Savg	if (isa_get_logicalid(dev) != 0)
736199015Savg		return (ENXIO);
737297255Smav
738340182Savg	if (ichwd_find_ich_lpc_bridge(device_get_parent(dev), &id_p) == NULL &&
739340182Savg	    ichwd_find_smb_dev(device_get_parent(dev), &id_p) == NULL)
740297255Smav		return (ENXIO);
741297255Smav
742297255Smav	device_set_desc_copy(dev, id_p->desc);
743129256Sdes	return (0);
744129256Sdes}
745129256Sdes
746129256Sdesstatic int
747340182Savgichwd_smb_attach(device_t dev)
748129124Sdes{
749129124Sdes	struct ichwd_softc *sc;
750171820Sdes	struct ichwd_device *id_p;
751340182Savg	device_t isab, pmdev;
752340182Savg	device_t smb;
753340182Savg	uint32_t acpi_base;
754340182Savg
755340182Savg	sc = device_get_softc(dev);
756340182Savg	smb = ichwd_find_smb_dev(device_get_parent(dev), &id_p);
757340182Savg	if (smb == NULL)
758340182Savg		return (ENXIO);
759340182Savg
760340182Savg	sc->ich_version = id_p->ich_version;
761340182Savg	sc->tco_version = id_p->tco_version;
762340182Savg
763340182Savg	/* Allocate TCO control I/O register space. */
764340182Savg	sc->tco_rid = 0;
765340182Savg	sc->tco_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->tco_rid,
766340182Savg	    RF_ACTIVE | RF_SHAREABLE);
767340182Savg	if (sc->tco_res == NULL) {
768340182Savg		device_printf(dev, "unable to reserve TCO registers\n");
769340182Savg		return (ENXIO);
770340182Savg	}
771340182Savg
772342565Savg	/*
773342565Savg	 * Allocate General Control I/O register in PCH
774342565Savg	 * Private Configuration Space (PCR).
775342565Savg	 */
776342565Savg	sc->gc_rid = 1;
777342565Savg	sc->gc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->gc_rid,
778342565Savg	    RF_ACTIVE | RF_SHAREABLE);
779342565Savg	if (sc->gc_res == NULL) {
780342565Savg		device_printf(dev, "unable to reserve hidden P2SB registers\n");
781342565Savg		return (ENXIO);
782342565Savg	}
783342565Savg
784340182Savg	/* Get ACPI base address. */
785340182Savg	isab = device_get_parent(device_get_parent(dev));
786340182Savg	pmdev = pci_find_dbsf(pci_get_domain(isab), pci_get_bus(isab), 31, 2);
787340182Savg	if (pmdev == NULL) {
788340182Savg		device_printf(dev, "unable to find Power Management device\n");
789340182Savg		return (ENXIO);
790340182Savg	}
791340182Savg	acpi_base = pci_read_config(pmdev, ICH_PMBASE, 4) & 0xffffff00;
792340182Savg	if (acpi_base == 0) {
793340182Savg		device_printf(dev, "ACPI base address is not set\n");
794340182Savg		return (ENXIO);
795340182Savg	}
796340182Savg
797340182Savg	/* Allocate SMI control I/O register space. */
798342565Savg	sc->smi_rid = 2;
799340182Savg	sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid,
800340182Savg	    acpi_base + SMI_BASE, acpi_base + SMI_BASE + SMI_LEN - 1, SMI_LEN,
801340182Savg	    RF_ACTIVE | RF_SHAREABLE);
802340182Savg	if (sc->smi_res == NULL) {
803340182Savg		device_printf(dev, "unable to reserve SMI registers\n");
804340182Savg		return (ENXIO);
805340182Savg	}
806340182Savg
807340182Savg	return (0);
808340182Savg}
809340182Savg
810340182Savgstatic int
811340182Savgichwd_lpc_attach(device_t dev)
812340182Savg{
813340182Savg	struct ichwd_softc *sc;
814340182Savg	struct ichwd_device *id_p;
815171820Sdes	device_t ich;
816171820Sdes	unsigned int pmbase = 0;
817129124Sdes
818129124Sdes	sc = device_get_softc(dev);
819129124Sdes
820322115Smav	ich = ichwd_find_ich_lpc_bridge(device_get_parent(dev), &id_p);
821340182Savg	if (ich == NULL)
822340182Savg		return (ENXIO);
823340182Savg
824171820Sdes	sc->ich = ich;
825286890Sfabient	sc->ich_version = id_p->ich_version;
826286890Sfabient	sc->tco_version = id_p->tco_version;
827171820Sdes
828171820Sdes	/* get ACPI base address */
829171820Sdes	pmbase = pci_read_config(ich, ICH_PMBASE, 2) & ICH_PMBASE_MASK;
830155785Sambrisko	if (pmbase == 0) {
831171820Sdes		device_printf(dev, "ICH PMBASE register is empty\n");
832340182Savg		return (ENXIO);
833155785Sambrisko	}
834155785Sambrisko
835129124Sdes	/* allocate I/O register space */
836155785Sambrisko	sc->smi_rid = 0;
837129124Sdes	sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid,
838171820Sdes	    pmbase + SMI_BASE, pmbase + SMI_BASE + SMI_LEN - 1, SMI_LEN,
839155785Sambrisko	    RF_ACTIVE | RF_SHAREABLE);
840129124Sdes	if (sc->smi_res == NULL) {
841129124Sdes		device_printf(dev, "unable to reserve SMI registers\n");
842340182Savg		return (ENXIO);
843129124Sdes	}
844155785Sambrisko
845155785Sambrisko	sc->tco_rid = 1;
846129124Sdes	sc->tco_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->tco_rid,
847171820Sdes	    pmbase + TCO_BASE, pmbase + TCO_BASE + TCO_LEN - 1, TCO_LEN,
848155785Sambrisko	    RF_ACTIVE | RF_SHAREABLE);
849129124Sdes	if (sc->tco_res == NULL) {
850129124Sdes		device_printf(dev, "unable to reserve TCO registers\n");
851340182Savg		return (ENXIO);
852129124Sdes	}
853171820Sdes
854171820Sdes	sc->gcs_rid = 0;
855286890Sfabient	if (sc->tco_version >= 2) {
856171820Sdes		sc->gcs_res = bus_alloc_resource_any(ich, SYS_RES_MEMORY,
857171820Sdes		    &sc->gcs_rid, RF_ACTIVE|RF_SHAREABLE);
858171820Sdes		if (sc->gcs_res == NULL) {
859171820Sdes			device_printf(dev, "unable to reserve GCS registers\n");
860340182Savg			return (ENXIO);
861171820Sdes		}
862171820Sdes	}
863171820Sdes
864340182Savg	return (0);
865340182Savg}
866340182Savg
867340182Savgstatic int
868340182Savgichwd_attach(device_t dev)
869340182Savg{
870340182Savg	struct ichwd_softc *sc;
871340182Savg
872340182Savg	sc = device_get_softc(dev);
873340182Savg	sc->device = dev;
874340182Savg
875340182Savg	if (ichwd_lpc_attach(dev) != 0 && ichwd_smb_attach(dev) != 0)
876340182Savg		goto fail;
877340182Savg
878171820Sdes	if (ichwd_clear_noreboot(sc) != 0)
879171820Sdes		goto fail;
880171820Sdes
881190030Sdes	/*
882216266Semaste	 * Determine if we are coming up after a watchdog-induced reset.  Some
883216266Semaste	 * BIOSes may clear this bit at bootup, preventing us from reporting
884216266Semaste	 * this case on such systems.  We clear this bit in ichwd_sts_reset().
885190030Sdes	 */
886215918Semaste	if ((ichwd_read_tco_2(sc, TCO2_STS) & TCO_SECOND_TO_STS) != 0)
887215868Sattilio		device_printf(dev,
888215868Sattilio		    "resuming after hardware watchdog timeout\n");
889190030Sdes
890155785Sambrisko	/* reset the watchdog status registers */
891129124Sdes	ichwd_sts_reset(sc);
892129124Sdes
893129124Sdes	/* make sure the WDT starts out inactive */
894129124Sdes	ichwd_tmr_disable(sc);
895129124Sdes
896129124Sdes	/* register the watchdog event handler */
897129124Sdes	sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ichwd_event, sc, 0);
898129124Sdes
899190030Sdes	/* disable the SMI handler */
900221016Sattilio	sc->smi_enabled = ichwd_smi_is_enabled(sc);
901190030Sdes	ichwd_smi_disable(sc);
902129124Sdes
903129124Sdes	return (0);
904129124Sdes fail:
905129124Sdes	sc = device_get_softc(dev);
906129124Sdes	if (sc->tco_res != NULL)
907129124Sdes		bus_release_resource(dev, SYS_RES_IOPORT,
908129124Sdes		    sc->tco_rid, sc->tco_res);
909129124Sdes	if (sc->smi_res != NULL)
910129124Sdes		bus_release_resource(dev, SYS_RES_IOPORT,
911129124Sdes		    sc->smi_rid, sc->smi_res);
912171820Sdes	if (sc->gcs_res != NULL)
913340182Savg		bus_release_resource(sc->ich, SYS_RES_MEMORY,
914171820Sdes		    sc->gcs_rid, sc->gcs_res);
915342565Savg	if (sc->gc_res != NULL)
916342565Savg		bus_release_resource(dev, SYS_RES_MEMORY,
917342565Savg		    sc->gc_rid, sc->gc_res);
918171820Sdes
919129124Sdes	return (ENXIO);
920129124Sdes}
921129124Sdes
922129124Sdesstatic int
923129124Sdesichwd_detach(device_t dev)
924129124Sdes{
925129124Sdes	struct ichwd_softc *sc;
926129124Sdes
927129124Sdes	sc = device_get_softc(dev);
928129124Sdes
929129124Sdes	/* halt the watchdog timer */
930129124Sdes	if (sc->active)
931129124Sdes		ichwd_tmr_disable(sc);
932129124Sdes
933190030Sdes	/* enable the SMI handler */
934221016Sattilio	if (sc->smi_enabled != 0)
935221016Sattilio		ichwd_smi_enable(sc);
936129124Sdes
937129124Sdes	/* deregister event handler */
938129124Sdes	if (sc->ev_tag != NULL)
939129124Sdes		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
940129124Sdes	sc->ev_tag = NULL;
941129124Sdes
942129124Sdes	/* reset the watchdog status registers */
943129124Sdes	ichwd_sts_reset(sc);
944129124Sdes
945129124Sdes	/* deallocate I/O register space */
946129124Sdes	bus_release_resource(dev, SYS_RES_IOPORT, sc->tco_rid, sc->tco_res);
947129124Sdes	bus_release_resource(dev, SYS_RES_IOPORT, sc->smi_rid, sc->smi_res);
948129124Sdes
949171820Sdes	/* deallocate memory resource */
950322115Smav	if (sc->gcs_res)
951322115Smav		bus_release_resource(sc->ich, SYS_RES_MEMORY, sc->gcs_rid,
952286890Sfabient		    sc->gcs_res);
953342565Savg	if (sc->gc_res)
954342565Savg		bus_release_resource(dev, SYS_RES_MEMORY, sc->gc_rid,
955342565Savg		    sc->gc_res);
956171820Sdes
957129124Sdes	return (0);
958129124Sdes}
959129124Sdes
960129124Sdesstatic device_method_t ichwd_methods[] = {
961129124Sdes	DEVMETHOD(device_identify, ichwd_identify),
962129124Sdes	DEVMETHOD(device_probe,	ichwd_probe),
963129124Sdes	DEVMETHOD(device_attach, ichwd_attach),
964129124Sdes	DEVMETHOD(device_detach, ichwd_detach),
965155785Sambrisko	DEVMETHOD(device_shutdown, ichwd_detach),
966129124Sdes	{0,0}
967129124Sdes};
968129124Sdes
969129124Sdesstatic driver_t ichwd_driver = {
970129124Sdes	"ichwd",
971129124Sdes	ichwd_methods,
972129124Sdes	sizeof(struct ichwd_softc),
973129124Sdes};
974129124Sdes
975197072Sn_hibmaDRIVER_MODULE(ichwd, isa, ichwd_driver, ichwd_devclass, NULL, NULL);
976