1 /* $NetBSD: tco.c,v 1.10 2023/04/12 06:39:15 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Minoura Makoto and Matthew R. Green. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Intel I/O Controller Hub (ICHn) watchdog timer 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: tco.c,v 1.10 2023/04/12 06:39:15 riastradh Exp $"); 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/device.h> 43 #include <sys/timetc.h> 44 #include <sys/module.h> 45 46 #include <dev/pci/pcivar.h> 47 #include <dev/pci/pcireg.h> 48 #include <dev/ic/i82801lpcreg.h> 49 50 #include <dev/sysmon/sysmonvar.h> 51 52 #include <arch/x86/pci/tco.h> 53 54 #include "pcibvar.h" 55 56 struct tco_softc { 57 struct sysmon_wdog sc_smw; 58 bus_space_tag_t sc_pmt; 59 bus_space_handle_t sc_pmh; 60 bus_space_tag_t sc_rcbat; 61 bus_space_handle_t sc_rcbah; 62 struct pcib_softc * sc_pcib; 63 pci_chipset_tag_t sc_pc; 64 bus_space_tag_t sc_tcot; 65 bus_space_handle_t sc_tcoh; 66 int (*sc_set_noreboot)(device_t, bool); 67 int sc_armed; 68 unsigned int sc_min_t; 69 unsigned int sc_max_t; 70 int sc_version; 71 bool sc_attached; 72 }; 73 74 static int tco_match(device_t, cfdata_t, void *); 75 static void tco_attach(device_t, device_t, void *); 76 static int tco_detach(device_t, int); 77 78 static bool tco_suspend(device_t, const pmf_qual_t *); 79 80 static int tcotimer_setmode(struct sysmon_wdog *); 81 static int tcotimer_tickle(struct sysmon_wdog *); 82 static void tcotimer_stop(struct tco_softc *); 83 static void tcotimer_start(struct tco_softc *); 84 static void tcotimer_status_reset(struct tco_softc *); 85 static int tcotimer_disable_noreboot(device_t); 86 87 CFATTACH_DECL3_NEW(tco, sizeof(struct tco_softc), 88 tco_match, tco_attach, tco_detach, NULL, NULL, NULL, 0); 89 90 /* 91 * Autoconf callbacks. 92 */ 93 static int 94 tco_match(device_t parent, cfdata_t match, void *aux) 95 { 96 struct tco_attach_args *ta = aux; 97 98 switch (ta->ta_version) { 99 case TCO_VERSION_SMBUS: 100 break; 101 case TCO_VERSION_RCBA: 102 case TCO_VERSION_PCIB: 103 if (ta->ta_pmt == 0) 104 return 0; 105 break; 106 default: 107 return 0; 108 } 109 110 return 1; 111 } 112 113 static void 114 tco_attach(device_t parent, device_t self, void *aux) 115 { 116 struct tco_softc *sc = device_private(self); 117 struct tco_attach_args *ta = aux; 118 uint32_t ioreg; 119 120 /* Retrieve bus info shared with parent/siblings */ 121 sc->sc_version = ta->ta_version; 122 sc->sc_pmt = ta->ta_pmt; 123 sc->sc_pmh = ta->ta_pmh; 124 sc->sc_rcbat = ta->ta_rcbat; 125 sc->sc_rcbah = ta->ta_rcbah; 126 sc->sc_pcib = ta->ta_pcib; 127 128 aprint_normal(": TCO (watchdog) timer configured.\n"); 129 aprint_naive("\n"); 130 131 switch (sc->sc_version) { 132 case TCO_VERSION_SMBUS: 133 sc->sc_tcot = ta->ta_tcot; 134 sc->sc_tcoh = ta->ta_tcoh; 135 sc->sc_set_noreboot = ta->ta_set_noreboot; 136 break; 137 case TCO_VERSION_RCBA: 138 case TCO_VERSION_PCIB: 139 sc->sc_tcot = sc->sc_pmt; 140 if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE, 141 TCO_REGSIZE, &sc->sc_tcoh)) { 142 aprint_error_dev(self, "failed to map TCO\n"); 143 return; 144 } 145 break; 146 } 147 148 /* Explicitly stop the TCO timer. */ 149 tcotimer_stop(sc); 150 151 /* 152 * Enable TCO timeout SMI only if the hardware reset does not 153 * work. We don't know what the SMBIOS does. 154 */ 155 ioreg = bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN); 156 aprint_debug_dev(self, "SMI_EN=0x%08x\n", ioreg); 157 ioreg &= ~PMC_SMI_EN_TCO_EN; 158 159 /* 160 * Clear the No Reboot (NR) bit. If this fails, enabling the TCO_EN bit 161 * in the SMI_EN register is the last chance. 162 */ 163 if (tcotimer_disable_noreboot(self)) { 164 ioreg |= PMC_SMI_EN_TCO_EN; 165 } 166 if ((ioreg & PMC_SMI_EN_GBL_SMI_EN) != 0) { 167 aprint_debug_dev(self, "SMI_EN:=0x%08x\n", ioreg); 168 bus_space_write_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN, ioreg); 169 aprint_debug_dev(self, "SMI_EN=0x%08x\n", 170 bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN)); 171 } 172 173 /* Reset the watchdog status registers. */ 174 tcotimer_status_reset(sc); 175 176 /* 177 * Register the driver with the sysmon watchdog framework. 178 */ 179 sc->sc_smw.smw_name = device_xname(self); 180 sc->sc_smw.smw_cookie = sc; 181 sc->sc_smw.smw_setmode = tcotimer_setmode; 182 sc->sc_smw.smw_tickle = tcotimer_tickle; 183 184 /* 185 * ICH6 or newer are limited to 2ticks min and 613ticks max. 186 * 1sec 367secs 187 * 188 * ICH5 or older are limited to 4ticks min and 39ticks max. 189 * 2secs 23secs 190 */ 191 switch (sc->sc_version) { 192 case TCO_VERSION_SMBUS: 193 case TCO_VERSION_RCBA: 194 sc->sc_max_t = TCOTIMER2_MAX_TICK; 195 sc->sc_min_t = TCOTIMER2_MIN_TICK; 196 break; 197 case TCO_VERSION_PCIB: 198 sc->sc_max_t = TCOTIMER_MAX_TICK; 199 sc->sc_min_t = TCOTIMER_MIN_TICK; 200 break; 201 } 202 sc->sc_smw.smw_period = tcotimer_tick_to_second(sc->sc_max_t); 203 204 aprint_verbose_dev(self, "Min/Max interval %u/%u seconds\n", 205 tcotimer_tick_to_second(sc->sc_min_t), 206 tcotimer_tick_to_second(sc->sc_max_t)); 207 208 if (sysmon_wdog_register(&sc->sc_smw)) 209 aprint_error_dev(self, "unable to register TCO timer" 210 "as a sysmon watchdog device.\n"); 211 212 if (!pmf_device_register(self, tco_suspend, NULL)) 213 aprint_error_dev(self, "unable to register with pmf\n"); 214 215 sc->sc_attached = true; 216 } 217 218 static int 219 tco_detach(device_t self, int flags) 220 { 221 struct tco_softc *sc = device_private(self); 222 int rc; 223 224 if (!sc->sc_attached) 225 return 0; 226 227 if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) { 228 if (rc == ERESTART) 229 rc = EINTR; 230 return rc; 231 } 232 233 /* Explicitly stop the TCO timer. */ 234 tcotimer_stop(sc); 235 236 /* XXX Set No Reboot? */ 237 238 pmf_device_deregister(self); 239 240 return 0; 241 } 242 243 static bool 244 tco_suspend(device_t self, const pmf_qual_t *quals) 245 { 246 struct tco_softc *sc = device_private(self); 247 248 /* Allow suspend only if watchdog is not armed */ 249 250 return ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED); 251 } 252 253 /* 254 * Sysmon watchdog callbacks. 255 */ 256 static int 257 tcotimer_setmode(struct sysmon_wdog *smw) 258 { 259 struct tco_softc *sc = smw->smw_cookie; 260 unsigned int period; 261 uint16_t ich6period = 0; 262 uint8_t ich5period = 0; 263 264 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 265 /* Stop the TCO timer. */ 266 tcotimer_stop(sc); 267 } else { 268 period = tcotimer_second_to_tick(smw->smw_period); 269 if (period < sc->sc_min_t || period > sc->sc_max_t) 270 return EINVAL; 271 272 /* Stop the TCO timer, */ 273 tcotimer_stop(sc); 274 275 /* set the timeout, */ 276 switch (sc->sc_version) { 277 case TCO_VERSION_SMBUS: 278 case TCO_VERSION_RCBA: 279 /* ICH6 or newer */ 280 ich6period = bus_space_read_2(sc->sc_tcot, sc->sc_tcoh, 281 TCO_TMR2); 282 ich6period &= 0xfc00; 283 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, 284 TCO_TMR2, ich6period | period); 285 break; 286 case TCO_VERSION_PCIB: 287 /* ICH5 or older */ 288 ich5period = bus_space_read_1(sc->sc_tcot, sc->sc_tcoh, 289 TCO_TMR); 290 ich5period &= 0xc0; 291 bus_space_write_1(sc->sc_tcot, sc->sc_tcoh, 292 TCO_TMR, ich5period | period); 293 break; 294 } 295 296 /* and start/reload the timer. */ 297 tcotimer_start(sc); 298 tcotimer_tickle(smw); 299 } 300 301 return 0; 302 } 303 304 static int 305 tcotimer_tickle(struct sysmon_wdog *smw) 306 { 307 struct tco_softc *sc = smw->smw_cookie; 308 309 /* any value is allowed */ 310 switch (sc->sc_version) { 311 case TCO_VERSION_SMBUS: 312 case TCO_VERSION_RCBA: 313 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO_RLD, 1); 314 break; 315 case TCO_VERSION_PCIB: 316 bus_space_write_1(sc->sc_tcot, sc->sc_tcoh, TCO_RLD, 1); 317 break; 318 } 319 320 return 0; 321 } 322 323 static void 324 tcotimer_stop(struct tco_softc *sc) 325 { 326 uint16_t ioreg; 327 328 ioreg = bus_space_read_2(sc->sc_tcot, sc->sc_tcoh, TCO1_CNT); 329 ioreg |= TCO1_CNT_TCO_TMR_HLT; 330 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO1_CNT, ioreg); 331 } 332 333 static void 334 tcotimer_start(struct tco_softc *sc) 335 { 336 uint16_t ioreg; 337 338 ioreg = bus_space_read_2(sc->sc_tcot, sc->sc_tcoh, TCO1_CNT); 339 ioreg &= ~TCO1_CNT_TCO_TMR_HLT; 340 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO1_CNT, ioreg); 341 } 342 343 static void 344 tcotimer_status_reset(struct tco_softc *sc) 345 { 346 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO1_STS, 347 TCO1_STS_TIMEOUT); 348 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO2_STS, 349 TCO2_STS_BOOT_STS); 350 bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO2_STS, 351 TCO2_STS_SECONDS_TO_STS); 352 } 353 354 /* 355 * Clear the No Reboot (NR) bit, this enables reboots when the timer 356 * reaches the timeout for the second time. 357 */ 358 static int 359 tcotimer_disable_noreboot(device_t self) 360 { 361 struct tco_softc *sc = device_private(self); 362 int error = EINVAL; 363 364 switch (sc->sc_version) { 365 case TCO_VERSION_SMBUS: 366 error = (*sc->sc_set_noreboot)(self, false); 367 if (error) 368 goto error; 369 break; 370 case TCO_VERSION_RCBA: { 371 uint32_t status; 372 373 status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah, 374 LPCIB_GCS_OFFSET); 375 status &= ~LPCIB_GCS_NO_REBOOT; 376 bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, 377 LPCIB_GCS_OFFSET, status); 378 status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah, 379 LPCIB_GCS_OFFSET); 380 if (status & LPCIB_GCS_NO_REBOOT) 381 goto error; 382 break; 383 } 384 case TCO_VERSION_PCIB: { 385 pcireg_t pcireg; 386 387 pcireg = pci_conf_read(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag, 388 LPCIB_PCI_GEN_STA); 389 if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT) { 390 /* TCO timeout reset is disabled; try to enable it */ 391 pcireg &= ~LPCIB_PCI_GEN_STA_NO_REBOOT; 392 pci_conf_write(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag, 393 LPCIB_PCI_GEN_STA, pcireg); 394 if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT) 395 goto error; 396 } 397 break; 398 } 399 } 400 401 return 0; 402 error: 403 aprint_error_dev(self, "TCO timer reboot disabled by hardware; " 404 "hope SMBIOS properly handles it.\n"); 405 return error; 406 } 407 408 MODULE(MODULE_CLASS_DRIVER, tco, "sysmon_wdog"); 409 410 #ifdef _MODULE 411 #include "ioconf.c" 412 #endif 413 414 static int 415 tco_modcmd(modcmd_t cmd, void *arg) 416 { 417 int ret = 0; 418 419 switch (cmd) { 420 case MODULE_CMD_INIT: 421 #ifdef _MODULE 422 ret = config_init_component(cfdriver_ioconf_tco, 423 cfattach_ioconf_tco, 424 cfdata_ioconf_tco); 425 #endif 426 break; 427 case MODULE_CMD_FINI: 428 #ifdef _MODULE 429 ret = config_fini_component(cfdriver_ioconf_tco, 430 cfattach_ioconf_tco, 431 cfdata_ioconf_tco); 432 #endif 433 break; 434 default: 435 ret = ENOTTY; 436 } 437 438 return ret; 439 } 440