15022f21bSAndriy Gapon /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 45022f21bSAndriy Gapon * Copyright (c) 2009 Andriy Gapon <avg@FreeBSD.org> 55022f21bSAndriy Gapon * All rights reserved. 65022f21bSAndriy Gapon * 75022f21bSAndriy Gapon * Redistribution and use in source and binary forms, with or without 85022f21bSAndriy Gapon * modification, are permitted provided that the following conditions 95022f21bSAndriy Gapon * are met: 105022f21bSAndriy Gapon * 1. Redistributions of source code must retain the above copyright 115022f21bSAndriy Gapon * notice, this list of conditions and the following disclaimer. 125022f21bSAndriy Gapon * 2. Redistributions in binary form must reproduce the above copyright 135022f21bSAndriy Gapon * notice, this list of conditions and the following disclaimer in the 145022f21bSAndriy Gapon * documentation and/or other materials provided with the distribution. 155022f21bSAndriy Gapon * 165022f21bSAndriy Gapon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 175022f21bSAndriy Gapon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 185022f21bSAndriy Gapon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 195022f21bSAndriy Gapon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 205022f21bSAndriy Gapon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 215022f21bSAndriy Gapon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 225022f21bSAndriy Gapon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 235022f21bSAndriy Gapon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 245022f21bSAndriy Gapon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 255022f21bSAndriy Gapon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 265022f21bSAndriy Gapon * SUCH DAMAGE. 275022f21bSAndriy Gapon */ 285022f21bSAndriy Gapon 295022f21bSAndriy Gapon /* 30d1817e7dSAndriy Gapon * This is a driver for watchdog timer present in AMD SB600/SB7xx/SB8xx 31d1817e7dSAndriy Gapon * southbridges. 325022f21bSAndriy Gapon * Please see the following specifications for the descriptions of the 335022f21bSAndriy Gapon * registers and flags: 345022f21bSAndriy Gapon * - AMD SB600 Register Reference Guide, Public Version, Rev. 3.03 (SB600 RRG) 355022f21bSAndriy Gapon * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/46155_sb600_rrg_pub_3.03.pdf 365022f21bSAndriy Gapon * - AMD SB700/710/750 Register Reference Guide (RRG) 375022f21bSAndriy Gapon * http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf 385022f21bSAndriy Gapon * - AMD SB700/710/750 Register Programming Requirements (RPR) 395022f21bSAndriy Gapon * http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf 40d1817e7dSAndriy Gapon * - AMD SB800-Series Southbridges Register Reference Guide (RRG) 41d1817e7dSAndriy Gapon * http://support.amd.com/us/Embedded_TechDocs/45482.pdf 425022f21bSAndriy Gapon * Please see the following for Watchdog Resource Table specification: 435022f21bSAndriy Gapon * - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT) 445022f21bSAndriy Gapon * http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx 45d1817e7dSAndriy Gapon * AMD SB600/SB7xx/SB8xx watchdog hardware seems to conform to the above 46d1817e7dSAndriy Gapon * specifications, but the table hasn't been spotted in the wild yet. 475022f21bSAndriy Gapon */ 485022f21bSAndriy Gapon 495022f21bSAndriy Gapon #include <sys/cdefs.h> 5066e77f8dSEitan Adler #include "opt_amdsbwd.h" 5166e77f8dSEitan Adler 525022f21bSAndriy Gapon #include <sys/param.h> 53e2e050c8SConrad Meyer #include <sys/eventhandler.h> 545022f21bSAndriy Gapon #include <sys/kernel.h> 555022f21bSAndriy Gapon #include <sys/module.h> 565022f21bSAndriy Gapon #include <sys/systm.h> 575022f21bSAndriy Gapon #include <sys/sysctl.h> 585022f21bSAndriy Gapon #include <sys/bus.h> 595022f21bSAndriy Gapon #include <machine/bus.h> 605022f21bSAndriy Gapon #include <sys/rman.h> 61decf9c5fSKonstantin Belousov #include <machine/cputypes.h> 62decf9c5fSKonstantin Belousov #include <machine/md_var.h> 635022f21bSAndriy Gapon #include <machine/resource.h> 645022f21bSAndriy Gapon #include <sys/watchdog.h> 655022f21bSAndriy Gapon 665022f21bSAndriy Gapon #include <dev/pci/pcivar.h> 673673f713SAndriy Gapon #include <dev/amdsbwd/amd_chipset.h> 685022f21bSAndriy Gapon #include <isa/isavar.h> 695022f21bSAndriy Gapon 703673f713SAndriy Gapon /* 713673f713SAndriy Gapon * Registers in the Watchdog IO space. 723673f713SAndriy Gapon * See SB7xx RRG 2.3.4, WDRT. 733673f713SAndriy Gapon */ 745022f21bSAndriy Gapon #define AMDSB_WD_CTRL 0x00 755022f21bSAndriy Gapon #define AMDSB_WD_RUN 0x01 765022f21bSAndriy Gapon #define AMDSB_WD_FIRED 0x02 775022f21bSAndriy Gapon #define AMDSB_WD_SHUTDOWN 0x04 785022f21bSAndriy Gapon #define AMDSB_WD_DISABLE 0x08 795022f21bSAndriy Gapon #define AMDSB_WD_RESERVED 0x70 805022f21bSAndriy Gapon #define AMDSB_WD_RELOAD 0x80 815022f21bSAndriy Gapon #define AMDSB_WD_COUNT 0x04 825022f21bSAndriy Gapon #define AMDSB_WD_COUNT_MASK 0xffff 835022f21bSAndriy Gapon #define AMDSB_WDIO_REG_WIDTH 4 845022f21bSAndriy Gapon 855022f21bSAndriy Gapon #define amdsbwd_verbose_printf(dev, ...) \ 865022f21bSAndriy Gapon do { \ 875022f21bSAndriy Gapon if (bootverbose) \ 885022f21bSAndriy Gapon device_printf(dev, __VA_ARGS__);\ 895022f21bSAndriy Gapon } while (0) 905022f21bSAndriy Gapon 915022f21bSAndriy Gapon struct amdsbwd_softc { 925022f21bSAndriy Gapon device_t dev; 935022f21bSAndriy Gapon eventhandler_tag ev_tag; 945022f21bSAndriy Gapon struct resource *res_ctrl; 955022f21bSAndriy Gapon struct resource *res_count; 965022f21bSAndriy Gapon int rid_ctrl; 975022f21bSAndriy Gapon int rid_count; 985022f21bSAndriy Gapon int ms_per_tick; 995022f21bSAndriy Gapon int max_ticks; 1005022f21bSAndriy Gapon int active; 1015022f21bSAndriy Gapon unsigned int timeout; 1025022f21bSAndriy Gapon }; 1035022f21bSAndriy Gapon 1045022f21bSAndriy Gapon static void amdsbwd_identify(driver_t *driver, device_t parent); 1055022f21bSAndriy Gapon static int amdsbwd_probe(device_t dev); 1065022f21bSAndriy Gapon static int amdsbwd_attach(device_t dev); 1075022f21bSAndriy Gapon static int amdsbwd_detach(device_t dev); 1089a042dbcSAndriy Gapon static int amdsbwd_suspend(device_t dev); 1099a042dbcSAndriy Gapon static int amdsbwd_resume(device_t dev); 1105022f21bSAndriy Gapon 1115022f21bSAndriy Gapon static device_method_t amdsbwd_methods[] = { 1125022f21bSAndriy Gapon DEVMETHOD(device_identify, amdsbwd_identify), 1135022f21bSAndriy Gapon DEVMETHOD(device_probe, amdsbwd_probe), 1145022f21bSAndriy Gapon DEVMETHOD(device_attach, amdsbwd_attach), 1155022f21bSAndriy Gapon DEVMETHOD(device_detach, amdsbwd_detach), 1169a042dbcSAndriy Gapon DEVMETHOD(device_suspend, amdsbwd_suspend), 1179a042dbcSAndriy Gapon DEVMETHOD(device_resume, amdsbwd_resume), 1185022f21bSAndriy Gapon #if 0 1195022f21bSAndriy Gapon DEVMETHOD(device_shutdown, amdsbwd_detach), 1205022f21bSAndriy Gapon #endif 12161bfd867SSofian Brabez DEVMETHOD_END 1225022f21bSAndriy Gapon }; 1235022f21bSAndriy Gapon 1245022f21bSAndriy Gapon static driver_t amdsbwd_driver = { 1255022f21bSAndriy Gapon "amdsbwd", 1265022f21bSAndriy Gapon amdsbwd_methods, 1275022f21bSAndriy Gapon sizeof(struct amdsbwd_softc) 1285022f21bSAndriy Gapon }; 1295022f21bSAndriy Gapon 13083a273efSJohn Baldwin DRIVER_MODULE(amdsbwd, isa, amdsbwd_driver, NULL, NULL); 1315022f21bSAndriy Gapon 1325022f21bSAndriy Gapon static uint8_t 1335022f21bSAndriy Gapon pmio_read(struct resource *res, uint8_t reg) 1345022f21bSAndriy Gapon { 1355022f21bSAndriy Gapon bus_write_1(res, 0, reg); /* Index */ 1365022f21bSAndriy Gapon return (bus_read_1(res, 1)); /* Data */ 1375022f21bSAndriy Gapon } 1385022f21bSAndriy Gapon 1395022f21bSAndriy Gapon static void 1405022f21bSAndriy Gapon pmio_write(struct resource *res, uint8_t reg, uint8_t val) 1415022f21bSAndriy Gapon { 1425022f21bSAndriy Gapon bus_write_1(res, 0, reg); /* Index */ 1435022f21bSAndriy Gapon bus_write_1(res, 1, val); /* Data */ 1445022f21bSAndriy Gapon } 1455022f21bSAndriy Gapon 1465022f21bSAndriy Gapon static uint32_t 1475022f21bSAndriy Gapon wdctrl_read(struct amdsbwd_softc *sc) 1485022f21bSAndriy Gapon { 1495022f21bSAndriy Gapon return (bus_read_4(sc->res_ctrl, 0)); 1505022f21bSAndriy Gapon } 1515022f21bSAndriy Gapon 1525022f21bSAndriy Gapon static void 1535022f21bSAndriy Gapon wdctrl_write(struct amdsbwd_softc *sc, uint32_t val) 1545022f21bSAndriy Gapon { 1555022f21bSAndriy Gapon bus_write_4(sc->res_ctrl, 0, val); 1565022f21bSAndriy Gapon } 1575022f21bSAndriy Gapon 1585022f21bSAndriy Gapon static __unused uint32_t 1595022f21bSAndriy Gapon wdcount_read(struct amdsbwd_softc *sc) 1605022f21bSAndriy Gapon { 1615022f21bSAndriy Gapon return (bus_read_4(sc->res_count, 0)); 1625022f21bSAndriy Gapon } 1635022f21bSAndriy Gapon 1645022f21bSAndriy Gapon static void 1655022f21bSAndriy Gapon wdcount_write(struct amdsbwd_softc *sc, uint32_t val) 1665022f21bSAndriy Gapon { 1675022f21bSAndriy Gapon bus_write_4(sc->res_count, 0, val); 1685022f21bSAndriy Gapon } 1695022f21bSAndriy Gapon 1705022f21bSAndriy Gapon static void 1715022f21bSAndriy Gapon amdsbwd_tmr_enable(struct amdsbwd_softc *sc) 1725022f21bSAndriy Gapon { 1735022f21bSAndriy Gapon uint32_t val; 1745022f21bSAndriy Gapon 1755022f21bSAndriy Gapon val = wdctrl_read(sc); 1765022f21bSAndriy Gapon val |= AMDSB_WD_RUN; 1775022f21bSAndriy Gapon wdctrl_write(sc, val); 1785022f21bSAndriy Gapon sc->active = 1; 1795022f21bSAndriy Gapon amdsbwd_verbose_printf(sc->dev, "timer enabled\n"); 1805022f21bSAndriy Gapon } 1815022f21bSAndriy Gapon 1825022f21bSAndriy Gapon static void 1835022f21bSAndriy Gapon amdsbwd_tmr_disable(struct amdsbwd_softc *sc) 1845022f21bSAndriy Gapon { 1855022f21bSAndriy Gapon uint32_t val; 1865022f21bSAndriy Gapon 1875022f21bSAndriy Gapon val = wdctrl_read(sc); 1885022f21bSAndriy Gapon val &= ~AMDSB_WD_RUN; 1895022f21bSAndriy Gapon wdctrl_write(sc, val); 1905022f21bSAndriy Gapon sc->active = 0; 1915022f21bSAndriy Gapon amdsbwd_verbose_printf(sc->dev, "timer disabled\n"); 1925022f21bSAndriy Gapon } 1935022f21bSAndriy Gapon 1945022f21bSAndriy Gapon static void 1955022f21bSAndriy Gapon amdsbwd_tmr_reload(struct amdsbwd_softc *sc) 1965022f21bSAndriy Gapon { 1975022f21bSAndriy Gapon uint32_t val; 1985022f21bSAndriy Gapon 1995022f21bSAndriy Gapon val = wdctrl_read(sc); 2005022f21bSAndriy Gapon val |= AMDSB_WD_RELOAD; 2015022f21bSAndriy Gapon wdctrl_write(sc, val); 2025022f21bSAndriy Gapon } 2035022f21bSAndriy Gapon 2045022f21bSAndriy Gapon static void 2055022f21bSAndriy Gapon amdsbwd_tmr_set(struct amdsbwd_softc *sc, uint16_t timeout) 2065022f21bSAndriy Gapon { 2075022f21bSAndriy Gapon 2085022f21bSAndriy Gapon timeout &= AMDSB_WD_COUNT_MASK; 2095022f21bSAndriy Gapon wdcount_write(sc, timeout); 2105022f21bSAndriy Gapon sc->timeout = timeout; 2115022f21bSAndriy Gapon amdsbwd_verbose_printf(sc->dev, "timeout set to %u ticks\n", timeout); 2125022f21bSAndriy Gapon } 2135022f21bSAndriy Gapon 2145022f21bSAndriy Gapon static void 2155022f21bSAndriy Gapon amdsbwd_event(void *arg, unsigned int cmd, int *error) 2165022f21bSAndriy Gapon { 2175022f21bSAndriy Gapon struct amdsbwd_softc *sc = arg; 218cf38c8c6SAndriy Gapon uint64_t timeout; 2195022f21bSAndriy Gapon 220cf38c8c6SAndriy Gapon if (cmd != 0) { 221cf38c8c6SAndriy Gapon timeout = 0; 2225022f21bSAndriy Gapon cmd &= WD_INTERVAL; 223cf38c8c6SAndriy Gapon if (cmd >= WD_TO_1MS) { 224cf38c8c6SAndriy Gapon timeout = (uint64_t)1 << (cmd - WD_TO_1MS); 225cf38c8c6SAndriy Gapon timeout = timeout / sc->ms_per_tick; 226cf38c8c6SAndriy Gapon } 227cf38c8c6SAndriy Gapon /* For a too short timeout use 1 tick. */ 228cf38c8c6SAndriy Gapon if (timeout == 0) 229cf38c8c6SAndriy Gapon timeout = 1; 230cf38c8c6SAndriy Gapon /* For a too long timeout stop the timer. */ 2315022f21bSAndriy Gapon if (timeout > sc->max_ticks) 232cf38c8c6SAndriy Gapon timeout = 0; 233cf38c8c6SAndriy Gapon } else { 234cf38c8c6SAndriy Gapon timeout = 0; 235cf38c8c6SAndriy Gapon } 236cf38c8c6SAndriy Gapon 237cf38c8c6SAndriy Gapon if (timeout != 0) { 238cf38c8c6SAndriy Gapon if (timeout != sc->timeout) 2395022f21bSAndriy Gapon amdsbwd_tmr_set(sc, timeout); 2405022f21bSAndriy Gapon if (!sc->active) 2415022f21bSAndriy Gapon amdsbwd_tmr_enable(sc); 2425022f21bSAndriy Gapon amdsbwd_tmr_reload(sc); 2435022f21bSAndriy Gapon *error = 0; 2445022f21bSAndriy Gapon } else { 2455022f21bSAndriy Gapon if (sc->active) 2465022f21bSAndriy Gapon amdsbwd_tmr_disable(sc); 2475022f21bSAndriy Gapon } 2485022f21bSAndriy Gapon } 2495022f21bSAndriy Gapon 2505022f21bSAndriy Gapon static void 2515022f21bSAndriy Gapon amdsbwd_identify(driver_t *driver, device_t parent) 2525022f21bSAndriy Gapon { 2535022f21bSAndriy Gapon device_t child; 2545022f21bSAndriy Gapon device_t smb_dev; 2555022f21bSAndriy Gapon 256a8de37b0SEitan Adler if (resource_disabled("amdsbwd", 0)) 257a8de37b0SEitan Adler return; 2585022f21bSAndriy Gapon if (device_find_child(parent, "amdsbwd", -1) != NULL) 2595022f21bSAndriy Gapon return; 2605022f21bSAndriy Gapon 2615022f21bSAndriy Gapon /* 2625022f21bSAndriy Gapon * Try to identify SB600/SB7xx by PCI Device ID of SMBus device 2635022f21bSAndriy Gapon * that should be present at bus 0, device 20, function 0. 2645022f21bSAndriy Gapon */ 2655022f21bSAndriy Gapon smb_dev = pci_find_bsf(0, 20, 0); 2665022f21bSAndriy Gapon if (smb_dev == NULL) 2675022f21bSAndriy Gapon return; 268bc8b0193SAlexander Motin if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID && 2693673f713SAndriy Gapon pci_get_devid(smb_dev) != AMDFCH_SMBUS_DEVID && 270decf9c5fSKonstantin Belousov pci_get_devid(smb_dev) != AMDCZ_SMBUS_DEVID && 271decf9c5fSKonstantin Belousov pci_get_devid(smb_dev) != HYGONCZ_SMBUS_DEVID) 2725022f21bSAndriy Gapon return; 2735022f21bSAndriy Gapon 274*a05a6804SWarner Losh child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", DEVICE_UNIT_ANY); 2755022f21bSAndriy Gapon if (child == NULL) 2765022f21bSAndriy Gapon device_printf(parent, "add amdsbwd child failed\n"); 2775022f21bSAndriy Gapon } 2785022f21bSAndriy Gapon 279d1817e7dSAndriy Gapon static void 280d1817e7dSAndriy Gapon amdsbwd_probe_sb7xx(device_t dev, struct resource *pmres, uint32_t *addr) 281d1817e7dSAndriy Gapon { 2829626ccdeSAndriy Gapon uint8_t val; 283d1817e7dSAndriy Gapon int i; 284d1817e7dSAndriy Gapon 285d1817e7dSAndriy Gapon /* Report cause of previous reset for user's convenience. */ 286d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB_PM_RESET_STATUS0); 287d1817e7dSAndriy Gapon if (val != 0) 288d1817e7dSAndriy Gapon amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val); 289d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB_PM_RESET_STATUS1); 290d1817e7dSAndriy Gapon if (val != 0) 291d1817e7dSAndriy Gapon amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val); 292d1817e7dSAndriy Gapon if ((val & AMDSB_WD_RST_STS) != 0) 293d1817e7dSAndriy Gapon device_printf(dev, "Previous Reset was caused by Watchdog\n"); 294d1817e7dSAndriy Gapon 295d1817e7dSAndriy Gapon /* Find base address of memory mapped WDT registers. */ 296d1817e7dSAndriy Gapon for (*addr = 0, i = 0; i < 4; i++) { 297d1817e7dSAndriy Gapon *addr <<= 8; 298d1817e7dSAndriy Gapon *addr |= pmio_read(pmres, AMDSB_PM_WDT_BASE_MSB - i); 299d1817e7dSAndriy Gapon } 300bc8b0193SAlexander Motin *addr &= ~0x07u; 301bc8b0193SAlexander Motin 302d1817e7dSAndriy Gapon /* Set watchdog timer tick to 1s. */ 303d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB_PM_WDT_CTRL); 304d1817e7dSAndriy Gapon val &= ~AMDSB_WDT_RES_MASK; 305bc8b0193SAlexander Motin val |= AMDSB_WDT_RES_1S; 306d1817e7dSAndriy Gapon pmio_write(pmres, AMDSB_PM_WDT_CTRL, val); 307d1817e7dSAndriy Gapon 308d1817e7dSAndriy Gapon /* Enable watchdog device (in stopped state). */ 309d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB_PM_WDT_CTRL); 310d1817e7dSAndriy Gapon val &= ~AMDSB_WDT_DISABLE; 311d1817e7dSAndriy Gapon pmio_write(pmres, AMDSB_PM_WDT_CTRL, val); 312d1817e7dSAndriy Gapon 313d1817e7dSAndriy Gapon /* 314d1817e7dSAndriy Gapon * XXX TODO: Ensure that watchdog decode is enabled 315d1817e7dSAndriy Gapon * (register 0x41, bit 3). 316d1817e7dSAndriy Gapon */ 317d1817e7dSAndriy Gapon device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer"); 318d1817e7dSAndriy Gapon } 319d1817e7dSAndriy Gapon 320d1817e7dSAndriy Gapon static void 321d1817e7dSAndriy Gapon amdsbwd_probe_sb8xx(device_t dev, struct resource *pmres, uint32_t *addr) 322d1817e7dSAndriy Gapon { 32334577ddbSAndriy Gapon uint32_t val; 324d1817e7dSAndriy Gapon int i; 325d1817e7dSAndriy Gapon 326d1817e7dSAndriy Gapon /* Report cause of previous reset for user's convenience. */ 32734577ddbSAndriy Gapon 32834577ddbSAndriy Gapon val = pmio_read(pmres, AMDSB8_PM_RESET_CTRL); 32934577ddbSAndriy Gapon if ((val & AMDSB8_RST_STS_DIS) != 0) { 33034577ddbSAndriy Gapon val &= ~AMDSB8_RST_STS_DIS; 33134577ddbSAndriy Gapon pmio_write(pmres, AMDSB8_PM_RESET_CTRL, val); 33234577ddbSAndriy Gapon } 33334577ddbSAndriy Gapon val = 0; 33434577ddbSAndriy Gapon for (i = 3; i >= 0; i--) { 33534577ddbSAndriy Gapon val <<= 8; 33634577ddbSAndriy Gapon val |= pmio_read(pmres, AMDSB8_PM_RESET_STATUS + i); 33734577ddbSAndriy Gapon } 338d1817e7dSAndriy Gapon if (val != 0) 33934577ddbSAndriy Gapon amdsbwd_verbose_printf(dev, "ResetStatus = 0x%08x\n", val); 340d1817e7dSAndriy Gapon if ((val & AMDSB8_WD_RST_STS) != 0) 341d1817e7dSAndriy Gapon device_printf(dev, "Previous Reset was caused by Watchdog\n"); 342d1817e7dSAndriy Gapon 343d1817e7dSAndriy Gapon /* Find base address of memory mapped WDT registers. */ 344d1817e7dSAndriy Gapon for (*addr = 0, i = 0; i < 4; i++) { 345d1817e7dSAndriy Gapon *addr <<= 8; 346d1817e7dSAndriy Gapon *addr |= pmio_read(pmres, AMDSB8_PM_WDT_EN + 3 - i); 347d1817e7dSAndriy Gapon } 348d1817e7dSAndriy Gapon *addr &= ~0x07u; 349d1817e7dSAndriy Gapon 350d1817e7dSAndriy Gapon /* Set watchdog timer tick to 1s. */ 351d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL); 352d1817e7dSAndriy Gapon val &= ~AMDSB8_WDT_RES_MASK; 353d1817e7dSAndriy Gapon val |= AMDSB8_WDT_1HZ; 354d1817e7dSAndriy Gapon pmio_write(pmres, AMDSB8_PM_WDT_CTRL, val); 355d1817e7dSAndriy Gapon #ifdef AMDSBWD_DEBUG 356d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL); 3579626ccdeSAndriy Gapon amdsbwd_verbose_printf(dev, "AMDSB8_PM_WDT_CTRL value = %#04x\n", val); 358d1817e7dSAndriy Gapon #endif 359d1817e7dSAndriy Gapon 360d1817e7dSAndriy Gapon /* 361d1817e7dSAndriy Gapon * Enable watchdog device (in stopped state) 362d1817e7dSAndriy Gapon * and decoding of its address. 363d1817e7dSAndriy Gapon */ 364d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB8_PM_WDT_EN); 365d1817e7dSAndriy Gapon val &= ~AMDSB8_WDT_DISABLE; 366d1817e7dSAndriy Gapon val |= AMDSB8_WDT_DEC_EN; 367d1817e7dSAndriy Gapon pmio_write(pmres, AMDSB8_PM_WDT_EN, val); 368d1817e7dSAndriy Gapon #ifdef AMDSBWD_DEBUG 369d1817e7dSAndriy Gapon val = pmio_read(pmres, AMDSB8_PM_WDT_EN); 3709626ccdeSAndriy Gapon device_printf(dev, "AMDSB8_PM_WDT_EN value = %#04x\n", val); 371d1817e7dSAndriy Gapon #endif 372bc8b0193SAlexander Motin device_set_desc(dev, "AMD SB8xx/SB9xx/Axx Watchdog Timer"); 373d1817e7dSAndriy Gapon } 374d1817e7dSAndriy Gapon 3759626ccdeSAndriy Gapon static void 3763673f713SAndriy Gapon amdsbwd_probe_fch41(device_t dev, struct resource *pmres, uint32_t *addr) 3779626ccdeSAndriy Gapon { 3789626ccdeSAndriy Gapon uint8_t val; 3799626ccdeSAndriy Gapon 3809626ccdeSAndriy Gapon /* 3819626ccdeSAndriy Gapon * Enable decoding of watchdog MMIO address. 3829626ccdeSAndriy Gapon */ 3833673f713SAndriy Gapon val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN0); 3843673f713SAndriy Gapon val |= AMDFCH41_WDT_EN; 3853673f713SAndriy Gapon pmio_write(pmres, AMDFCH41_PM_DECODE_EN0, val); 3869626ccdeSAndriy Gapon #ifdef AMDSBWD_DEBUG 3873673f713SAndriy Gapon val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN0); 388cca0d3bbSAndriy Gapon device_printf(dev, "AMDFCH41_PM_DECODE_EN0 value = %#04x\n", val); 3899626ccdeSAndriy Gapon #endif 3909626ccdeSAndriy Gapon 391cca0d3bbSAndriy Gapon val = pmio_read(pmres, AMDFCH41_PM_ISA_CTRL); 392cca0d3bbSAndriy Gapon if ((val & AMDFCH41_MMIO_EN) != 0) { 393cca0d3bbSAndriy Gapon /* Fixed offset for the watchdog within ACPI MMIO range. */ 394cca0d3bbSAndriy Gapon amdsbwd_verbose_printf(dev, "ACPI MMIO range is enabled\n"); 395cca0d3bbSAndriy Gapon *addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_WDT_OFF; 396cca0d3bbSAndriy Gapon } else { 3979626ccdeSAndriy Gapon /* Special fixed MMIO range for the watchdog. */ 3983673f713SAndriy Gapon *addr = AMDFCH41_WDT_FIXED_ADDR; 3999626ccdeSAndriy Gapon } 4009626ccdeSAndriy Gapon 4019626ccdeSAndriy Gapon /* 4029626ccdeSAndriy Gapon * Set watchdog timer tick to 1s and 4039626ccdeSAndriy Gapon * enable the watchdog device (in stopped state). 4049626ccdeSAndriy Gapon */ 4053673f713SAndriy Gapon val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN3); 4063673f713SAndriy Gapon val &= ~AMDFCH41_WDT_RES_MASK; 4073673f713SAndriy Gapon val |= AMDFCH41_WDT_RES_1S; 4083673f713SAndriy Gapon val &= ~AMDFCH41_WDT_EN_MASK; 4093673f713SAndriy Gapon val |= AMDFCH41_WDT_ENABLE; 4103673f713SAndriy Gapon pmio_write(pmres, AMDFCH41_PM_DECODE_EN3, val); 4119626ccdeSAndriy Gapon #ifdef AMDSBWD_DEBUG 4123673f713SAndriy Gapon val = pmio_read(pmres, AMDFCH41_PM_DECODE_EN3); 4133673f713SAndriy Gapon amdsbwd_verbose_printf(dev, "AMDFCH41_PM_DECODE_EN3 value = %#04x\n", 4149626ccdeSAndriy Gapon val); 4159626ccdeSAndriy Gapon #endif 4161878529dSMark Johnston device_set_descf(dev, "%s FCH Rev 41h+ Watchdog Timer", 417decf9c5fSKonstantin Belousov cpu_vendor_id == CPU_VENDOR_HYGON ? "Hygon" : "AMD"); 4189626ccdeSAndriy Gapon } 4199626ccdeSAndriy Gapon 4205022f21bSAndriy Gapon static int 4215022f21bSAndriy Gapon amdsbwd_probe(device_t dev) 4225022f21bSAndriy Gapon { 4235022f21bSAndriy Gapon struct resource *res; 424d1817e7dSAndriy Gapon device_t smb_dev; 4255022f21bSAndriy Gapon uint32_t addr; 4265022f21bSAndriy Gapon int rid; 4275022f21bSAndriy Gapon int rc; 4289626ccdeSAndriy Gapon uint32_t devid; 4299626ccdeSAndriy Gapon uint8_t revid; 4305022f21bSAndriy Gapon 4315022f21bSAndriy Gapon /* Do not claim some ISA PnP device by accident. */ 4325022f21bSAndriy Gapon if (isa_get_logicalid(dev) != 0) 4335022f21bSAndriy Gapon return (ENXIO); 4345022f21bSAndriy Gapon 4355022f21bSAndriy Gapon rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX, 4365022f21bSAndriy Gapon AMDSB_PMIO_WIDTH); 4375022f21bSAndriy Gapon if (rc != 0) { 4385022f21bSAndriy Gapon device_printf(dev, "bus_set_resource for IO failed\n"); 4395022f21bSAndriy Gapon return (ENXIO); 4405022f21bSAndriy Gapon } 4415022f21bSAndriy Gapon rid = 0; 442eff83876SJustin Hibbits res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 443eff83876SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 4445022f21bSAndriy Gapon if (res == NULL) { 4455022f21bSAndriy Gapon device_printf(dev, "bus_alloc_resource for IO failed\n"); 4465022f21bSAndriy Gapon return (ENXIO); 4475022f21bSAndriy Gapon } 4485022f21bSAndriy Gapon 449d1817e7dSAndriy Gapon smb_dev = pci_find_bsf(0, 20, 0); 450d1817e7dSAndriy Gapon KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n")); 4519626ccdeSAndriy Gapon devid = pci_get_devid(smb_dev); 4529626ccdeSAndriy Gapon revid = pci_get_revid(smb_dev); 4539626ccdeSAndriy Gapon if (devid == AMDSB_SMBUS_DEVID && revid < AMDSB8_SMBUS_REVID) 454d1817e7dSAndriy Gapon amdsbwd_probe_sb7xx(dev, res, &addr); 4553673f713SAndriy Gapon else if (devid == AMDSB_SMBUS_DEVID || 4563673f713SAndriy Gapon (devid == AMDFCH_SMBUS_DEVID && revid < AMDFCH41_SMBUS_REVID) || 4573673f713SAndriy Gapon (devid == AMDCZ_SMBUS_DEVID && revid < AMDCZ49_SMBUS_REVID)) 458d1817e7dSAndriy Gapon amdsbwd_probe_sb8xx(dev, res, &addr); 4599626ccdeSAndriy Gapon else 4603673f713SAndriy Gapon amdsbwd_probe_fch41(dev, res, &addr); 4615022f21bSAndriy Gapon 462d1817e7dSAndriy Gapon bus_release_resource(dev, SYS_RES_IOPORT, rid, res); 463d1817e7dSAndriy Gapon bus_delete_resource(dev, SYS_RES_IOPORT, rid); 464d1817e7dSAndriy Gapon 4655022f21bSAndriy Gapon amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr); 4665022f21bSAndriy Gapon rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL, 4675022f21bSAndriy Gapon AMDSB_WDIO_REG_WIDTH); 4685022f21bSAndriy Gapon if (rc != 0) { 4695022f21bSAndriy Gapon device_printf(dev, "bus_set_resource for control failed\n"); 4705022f21bSAndriy Gapon return (ENXIO); 4715022f21bSAndriy Gapon } 4725022f21bSAndriy Gapon rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, addr + AMDSB_WD_COUNT, 4735022f21bSAndriy Gapon AMDSB_WDIO_REG_WIDTH); 4745022f21bSAndriy Gapon if (rc != 0) { 4755022f21bSAndriy Gapon device_printf(dev, "bus_set_resource for count failed\n"); 4765022f21bSAndriy Gapon return (ENXIO); 4775022f21bSAndriy Gapon } 4785022f21bSAndriy Gapon 4795022f21bSAndriy Gapon return (0); 4805022f21bSAndriy Gapon } 4815022f21bSAndriy Gapon 4825022f21bSAndriy Gapon static int 4835022f21bSAndriy Gapon amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc) 4845022f21bSAndriy Gapon { 485d1817e7dSAndriy Gapon 4865022f21bSAndriy Gapon sc->max_ticks = UINT16_MAX; 4875022f21bSAndriy Gapon sc->rid_ctrl = 0; 4885022f21bSAndriy Gapon sc->rid_count = 1; 4895022f21bSAndriy Gapon 490d1817e7dSAndriy Gapon sc->ms_per_tick = 1000; 491d1817e7dSAndriy Gapon 4925022f21bSAndriy Gapon sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 4935022f21bSAndriy Gapon &sc->rid_ctrl, RF_ACTIVE); 4945022f21bSAndriy Gapon if (sc->res_ctrl == NULL) { 4955022f21bSAndriy Gapon device_printf(dev, "bus_alloc_resource for ctrl failed\n"); 4965022f21bSAndriy Gapon return (ENXIO); 4975022f21bSAndriy Gapon } 4985022f21bSAndriy Gapon sc->res_count = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 4995022f21bSAndriy Gapon &sc->rid_count, RF_ACTIVE); 5005022f21bSAndriy Gapon if (sc->res_count == NULL) { 5015022f21bSAndriy Gapon device_printf(dev, "bus_alloc_resource for count failed\n"); 5025022f21bSAndriy Gapon return (ENXIO); 5035022f21bSAndriy Gapon } 5045022f21bSAndriy Gapon return (0); 5055022f21bSAndriy Gapon } 5065022f21bSAndriy Gapon 5075022f21bSAndriy Gapon static int 5085022f21bSAndriy Gapon amdsbwd_attach(device_t dev) 5095022f21bSAndriy Gapon { 5105022f21bSAndriy Gapon struct amdsbwd_softc *sc; 5115022f21bSAndriy Gapon int rc; 5125022f21bSAndriy Gapon 5135022f21bSAndriy Gapon sc = device_get_softc(dev); 5145022f21bSAndriy Gapon sc->dev = dev; 5155022f21bSAndriy Gapon 5165022f21bSAndriy Gapon rc = amdsbwd_attach_sb(dev, sc); 5175022f21bSAndriy Gapon if (rc != 0) 5185022f21bSAndriy Gapon goto fail; 5195022f21bSAndriy Gapon 520d1817e7dSAndriy Gapon #ifdef AMDSBWD_DEBUG 521d1817e7dSAndriy Gapon device_printf(dev, "wd ctrl = %#04x\n", wdctrl_read(sc)); 522d1817e7dSAndriy Gapon device_printf(dev, "wd count = %#04x\n", wdcount_read(sc)); 523d1817e7dSAndriy Gapon #endif 524d1817e7dSAndriy Gapon 5255022f21bSAndriy Gapon /* Setup initial state of Watchdog Control. */ 5265022f21bSAndriy Gapon wdctrl_write(sc, AMDSB_WD_FIRED); 5275022f21bSAndriy Gapon 5285022f21bSAndriy Gapon if (wdctrl_read(sc) & AMDSB_WD_DISABLE) { 5295022f21bSAndriy Gapon device_printf(dev, "watchdog hardware is disabled\n"); 5305022f21bSAndriy Gapon goto fail; 5315022f21bSAndriy Gapon } 5325022f21bSAndriy Gapon 5335022f21bSAndriy Gapon sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, amdsbwd_event, sc, 5345022f21bSAndriy Gapon EVENTHANDLER_PRI_ANY); 5355022f21bSAndriy Gapon 5365022f21bSAndriy Gapon return (0); 5375022f21bSAndriy Gapon 5385022f21bSAndriy Gapon fail: 5395022f21bSAndriy Gapon amdsbwd_detach(dev); 5405022f21bSAndriy Gapon return (ENXIO); 5415022f21bSAndriy Gapon } 5425022f21bSAndriy Gapon 5435022f21bSAndriy Gapon static int 5445022f21bSAndriy Gapon amdsbwd_detach(device_t dev) 5455022f21bSAndriy Gapon { 5465022f21bSAndriy Gapon struct amdsbwd_softc *sc; 5475022f21bSAndriy Gapon 5485022f21bSAndriy Gapon sc = device_get_softc(dev); 5495022f21bSAndriy Gapon if (sc->ev_tag != NULL) 5505022f21bSAndriy Gapon EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 5515022f21bSAndriy Gapon 5525022f21bSAndriy Gapon if (sc->active) 5535022f21bSAndriy Gapon amdsbwd_tmr_disable(sc); 5545022f21bSAndriy Gapon 5555022f21bSAndriy Gapon if (sc->res_ctrl != NULL) 5565022f21bSAndriy Gapon bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ctrl, 5575022f21bSAndriy Gapon sc->res_ctrl); 5585022f21bSAndriy Gapon 5595022f21bSAndriy Gapon if (sc->res_count != NULL) 5605022f21bSAndriy Gapon bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_count, 5615022f21bSAndriy Gapon sc->res_count); 5625022f21bSAndriy Gapon 5635022f21bSAndriy Gapon return (0); 5645022f21bSAndriy Gapon } 5655022f21bSAndriy Gapon 5669a042dbcSAndriy Gapon static int 5679a042dbcSAndriy Gapon amdsbwd_suspend(device_t dev) 5689a042dbcSAndriy Gapon { 5699a042dbcSAndriy Gapon struct amdsbwd_softc *sc; 5709a042dbcSAndriy Gapon uint32_t val; 5719a042dbcSAndriy Gapon 5729a042dbcSAndriy Gapon sc = device_get_softc(dev); 5739a042dbcSAndriy Gapon val = wdctrl_read(sc); 5749a042dbcSAndriy Gapon val &= ~AMDSB_WD_RUN; 5759a042dbcSAndriy Gapon wdctrl_write(sc, val); 5769a042dbcSAndriy Gapon return (0); 5779a042dbcSAndriy Gapon } 5789a042dbcSAndriy Gapon 5799a042dbcSAndriy Gapon static int 5809a042dbcSAndriy Gapon amdsbwd_resume(device_t dev) 5819a042dbcSAndriy Gapon { 5829a042dbcSAndriy Gapon struct amdsbwd_softc *sc; 5839a042dbcSAndriy Gapon 5849a042dbcSAndriy Gapon sc = device_get_softc(dev); 5859a042dbcSAndriy Gapon wdctrl_write(sc, AMDSB_WD_FIRED); 5869a042dbcSAndriy Gapon if (sc->active) { 5879a042dbcSAndriy Gapon amdsbwd_tmr_set(sc, sc->timeout); 5889a042dbcSAndriy Gapon amdsbwd_tmr_enable(sc); 5899a042dbcSAndriy Gapon amdsbwd_tmr_reload(sc); 5909a042dbcSAndriy Gapon } 5919a042dbcSAndriy Gapon return (0); 5929a042dbcSAndriy Gapon } 593