xref: /freebsd/sys/dev/amdsbwd/amdsbwd.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
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