/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Minoura Makoto and Matthew R. Green.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
switch (sc->sc_version) {
case TCO_VERSION_SMBUS:
sc->sc_tcot = ta->ta_tcot;
sc->sc_tcoh = ta->ta_tcoh;
sc->sc_set_noreboot = ta->ta_set_noreboot;
break;
case TCO_VERSION_RCBA:
case TCO_VERSION_PCIB:
sc->sc_tcot = sc->sc_pmt;
if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE,
TCO_REGSIZE, &sc->sc_tcoh)) {
aprint_error_dev(self, "failed to map TCO\n");
return;
}
break;
}
/* Explicitly stop the TCO timer. */
tcotimer_stop(sc);
/*
* Enable TCO timeout SMI only if the hardware reset does not
* work. We don't know what the SMBIOS does.
*/
ioreg = bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN);
aprint_debug_dev(self, "SMI_EN=0x%08x\n", ioreg);
ioreg &= ~PMC_SMI_EN_TCO_EN;
/*
* Clear the No Reboot (NR) bit. If this fails, enabling the TCO_EN bit
* in the SMI_EN register is the last chance.
*/
if (tcotimer_disable_noreboot(self)) {
ioreg |= PMC_SMI_EN_TCO_EN;
}
if ((ioreg & PMC_SMI_EN_GBL_SMI_EN) != 0) {
aprint_debug_dev(self, "SMI_EN:=0x%08x\n", ioreg);
bus_space_write_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN, ioreg);
aprint_debug_dev(self, "SMI_EN=0x%08x\n",
bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN));
}
/* Reset the watchdog status registers. */
tcotimer_status_reset(sc);
/*
* Register the driver with the sysmon watchdog framework.
*/
sc->sc_smw.smw_name = device_xname(self);
sc->sc_smw.smw_cookie = sc;
sc->sc_smw.smw_setmode = tcotimer_setmode;
sc->sc_smw.smw_tickle = tcotimer_tickle;
/*
* ICH6 or newer are limited to 2ticks min and 613ticks max.
* 1sec 367secs
*
* ICH5 or older are limited to 4ticks min and 39ticks max.
* 2secs 23secs
*/
switch (sc->sc_version) {
case TCO_VERSION_SMBUS:
case TCO_VERSION_RCBA:
sc->sc_max_t = TCOTIMER2_MAX_TICK;
sc->sc_min_t = TCOTIMER2_MIN_TICK;
break;
case TCO_VERSION_PCIB:
sc->sc_max_t = TCOTIMER_MAX_TICK;
sc->sc_min_t = TCOTIMER_MIN_TICK;
break;
}
sc->sc_smw.smw_period = tcotimer_tick_to_second(sc->sc_max_t);
/* any value is allowed */
switch (sc->sc_version) {
case TCO_VERSION_SMBUS:
case TCO_VERSION_RCBA:
bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO_RLD, 1);
break;
case TCO_VERSION_PCIB:
bus_space_write_1(sc->sc_tcot, sc->sc_tcoh, TCO_RLD, 1);
break;
}
/*
* Clear the No Reboot (NR) bit, this enables reboots when the timer
* reaches the timeout for the second time.
*/
static int
tcotimer_disable_noreboot(device_t self)
{
struct tco_softc *sc = device_private(self);
int error = EINVAL;
switch (sc->sc_version) {
case TCO_VERSION_SMBUS:
error = (*sc->sc_set_noreboot)(self, false);
if (error)
goto error;
break;
case TCO_VERSION_RCBA: {
uint32_t status;
status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
LPCIB_GCS_OFFSET);
status &= ~LPCIB_GCS_NO_REBOOT;
bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah,
LPCIB_GCS_OFFSET, status);
status = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
LPCIB_GCS_OFFSET);
if (status & LPCIB_GCS_NO_REBOOT)
goto error;
break;
}
case TCO_VERSION_PCIB: {
pcireg_t pcireg;
pcireg = pci_conf_read(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag,
LPCIB_PCI_GEN_STA);
if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT) {
/* TCO timeout reset is disabled; try to enable it */
pcireg &= ~LPCIB_PCI_GEN_STA_NO_REBOOT;
pci_conf_write(sc->sc_pcib->sc_pc, sc->sc_pcib->sc_tag,
LPCIB_PCI_GEN_STA, pcireg);
if (pcireg & LPCIB_PCI_GEN_STA_NO_REBOOT)
goto error;
}
break;
}
}