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