xref: /netbsd-src/sys/arch/i386/pci/geodewdg.c (revision 71fbb921c37470c5e74fa34a26169d8da0db6c1b)
1*71fbb921Smsaitoh /*	$NetBSD: geodewdg.c,v 1.12 2016/07/11 11:31:49 msaitoh Exp $	*/
209b51ec9Skardel 
309b51ec9Skardel /*-
409b51ec9Skardel  * Copyright (c) 2005 David Young.  All rights reserved.
509b51ec9Skardel  *
609b51ec9Skardel  * This code was written by David Young.
709b51ec9Skardel  *
809b51ec9Skardel  * Redistribution and use in source and binary forms, with or without
909b51ec9Skardel  * modification, are permitted provided that the following conditions
1009b51ec9Skardel  * are met:
1109b51ec9Skardel  * 1. Redistributions of source code must retain the above copyright
1209b51ec9Skardel  *    notice, this list of conditions and the following disclaimer.
1309b51ec9Skardel  * 2. Redistributions in binary form must reproduce the above copyright
1409b51ec9Skardel  *    notice, this list of conditions and the following disclaimer in the
1509b51ec9Skardel  *    documentation and/or other materials provided with the distribution.
1609b51ec9Skardel  *
1709b51ec9Skardel  * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
1809b51ec9Skardel  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1909b51ec9Skardel  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
2009b51ec9Skardel  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DAVID
2109b51ec9Skardel  * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2209b51ec9Skardel  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2309b51ec9Skardel  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2409b51ec9Skardel  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2509b51ec9Skardel  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2609b51ec9Skardel  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2709b51ec9Skardel  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
2809b51ec9Skardel  * OF SUCH DAMAGE.
2909b51ec9Skardel  */
3009b51ec9Skardel /*-
3109b51ec9Skardel  * Copyright (c) 2002 The NetBSD Foundation, Inc.
3209b51ec9Skardel  * All rights reserved.
3309b51ec9Skardel  *
3409b51ec9Skardel  * This code is derived from software contributed to The NetBSD Foundation
3509b51ec9Skardel  * by Jason R. Thorpe.
3609b51ec9Skardel  *
3709b51ec9Skardel  * Redistribution and use in source and binary forms, with or without
3809b51ec9Skardel  * modification, are permitted provided that the following conditions
3909b51ec9Skardel  * are met:
4009b51ec9Skardel  * 1. Redistributions of source code must retain the above copyright
4109b51ec9Skardel  *    notice, this list of conditions and the following disclaimer.
4209b51ec9Skardel  * 2. Redistributions in binary form must reproduce the above copyright
4309b51ec9Skardel  *    notice, this list of conditions and the following disclaimer in the
4409b51ec9Skardel  *    documentation and/or other materials provided with the distribution.
4509b51ec9Skardel  *
4609b51ec9Skardel  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
4709b51ec9Skardel  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4809b51ec9Skardel  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4909b51ec9Skardel  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5009b51ec9Skardel  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5109b51ec9Skardel  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5209b51ec9Skardel  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5309b51ec9Skardel  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5409b51ec9Skardel  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5509b51ec9Skardel  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5609b51ec9Skardel  * POSSIBILITY OF SUCH DAMAGE.
5709b51ec9Skardel  */
5809b51ec9Skardel 
5909b51ec9Skardel /*
6009b51ec9Skardel  * Device driver for the watchdog timer built into the
6109b51ec9Skardel  * AMD Geode SC1100 processor.
6209b51ec9Skardel  */
6309b51ec9Skardel 
6409b51ec9Skardel #include <sys/cdefs.h>
6509b51ec9Skardel 
66*71fbb921Smsaitoh __KERNEL_RCSID(0, "$NetBSD: geodewdg.c,v 1.12 2016/07/11 11:31:49 msaitoh Exp $");
6709b51ec9Skardel 
6809b51ec9Skardel #include <sys/param.h>
6909b51ec9Skardel #include <sys/systm.h>
7009b51ec9Skardel #include <sys/device.h>
7109b51ec9Skardel #include <sys/wdog.h>
7209b51ec9Skardel #include <uvm/uvm_extern.h>
73f5b064eeSdyoung #include <sys/bus.h>
7409b51ec9Skardel #include <dev/pci/pcivar.h>
7509b51ec9Skardel #include <dev/pci/pcidevs.h>
7609b51ec9Skardel #include <arch/i386/pci/geodevar.h>
7709b51ec9Skardel #include <arch/i386/pci/geodereg.h>
7809b51ec9Skardel #include <dev/sysmon/sysmonvar.h>
7909b51ec9Skardel 
8009b51ec9Skardel #ifdef GEODE_DEBUG
8109b51ec9Skardel #define	GEODE_DPRINTF(__x) printf __x
8209b51ec9Skardel #else /* GEODE_DEBUG */
8309b51ec9Skardel #define	GEODE_DPRINTF(__x) /* nothing */
8409b51ec9Skardel #endif
8509b51ec9Skardel 
8609b51ec9Skardel struct geode_wdog_softc {
8709b51ec9Skardel 	struct geode_gcb_softc *sc_gcb_dev;
8809b51ec9Skardel 
8909b51ec9Skardel 	uint16_t		sc_countdown;
9009b51ec9Skardel 	uint8_t			sc_prescale;
9109b51ec9Skardel 	struct sysmon_wdog      sc_smw;
9209b51ec9Skardel };
9309b51ec9Skardel 
9409b51ec9Skardel static int attached = 0;
9509b51ec9Skardel 
9609b51ec9Skardel static void
geode_wdog_disable(struct geode_wdog_softc * sc)9709b51ec9Skardel geode_wdog_disable(struct geode_wdog_softc *sc)
9809b51ec9Skardel {
9909b51ec9Skardel 	uint16_t wdcnfg;
10009b51ec9Skardel 
10109b51ec9Skardel 	/* cancel any pending countdown */
10209b51ec9Skardel 	sc->sc_countdown = 0;
103d4755581Sdyoung 	bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
104d4755581Sdyoung 	    SC1100_GCB_WDTO, 0);
10509b51ec9Skardel 	/* power-down clock */
106d4755581Sdyoung 	wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
107d4755581Sdyoung 	    sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
10809b51ec9Skardel 
10909b51ec9Skardel 	GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
11009b51ec9Skardel 
11109b51ec9Skardel 	wdcnfg |= SC1100_WDCNFG_WD32KPD;
11209b51ec9Skardel 	wdcnfg &= ~(SC1100_WDCNFG_WDTYPE2_MASK | SC1100_WDCNFG_WDTYPE1_MASK);
11309b51ec9Skardel 	/* This no-op is for the reader's benefit. */
11409b51ec9Skardel         wdcnfg |= SC1100_WDCNFG_WDTYPE1_NOACTION |
11509b51ec9Skardel                   SC1100_WDCNFG_WDTYPE2_NOACTION;
116d4755581Sdyoung 	bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
117d4755581Sdyoung 	    SC1100_GCB_WDCNFG, wdcnfg);
11809b51ec9Skardel 
11909b51ec9Skardel 	GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
12009b51ec9Skardel }
12109b51ec9Skardel 
12209b51ec9Skardel static void
geode_wdog_enable(struct geode_wdog_softc * sc)12309b51ec9Skardel geode_wdog_enable(struct geode_wdog_softc *sc)
12409b51ec9Skardel {
12509b51ec9Skardel 	uint16_t wdcnfg;
12609b51ec9Skardel 
12709b51ec9Skardel 	/* power-up clock and set prescale */
128d4755581Sdyoung 	wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
129d4755581Sdyoung 	    sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
13009b51ec9Skardel 
13109b51ec9Skardel 	GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
13209b51ec9Skardel 
13309b51ec9Skardel 	wdcnfg &= ~(SC1100_WDCNFG_WD32KPD | SC1100_WDCNFG_WDPRES_MASK |
13409b51ec9Skardel 	            SC1100_WDCNFG_WDTYPE1_MASK | SC1100_WDCNFG_WDTYPE2_MASK);
1358cd106d3Sdyoung 	wdcnfg |= __SHIFTIN(sc->sc_prescale, SC1100_WDCNFG_WDPRES_MASK);
13609b51ec9Skardel         wdcnfg |= SC1100_WDCNFG_WDTYPE1_RESET | SC1100_WDCNFG_WDTYPE2_NOACTION;
13709b51ec9Skardel 
138d4755581Sdyoung 	bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
139d4755581Sdyoung 	    SC1100_GCB_WDCNFG, wdcnfg);
14009b51ec9Skardel 
14109b51ec9Skardel 	GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
14209b51ec9Skardel }
14309b51ec9Skardel 
14409b51ec9Skardel static void
geode_wdog_reset(struct geode_wdog_softc * sc)14509b51ec9Skardel geode_wdog_reset(struct geode_wdog_softc *sc)
14609b51ec9Skardel {
14709b51ec9Skardel 	/* set countdown */
148d4755581Sdyoung 	bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
149d4755581Sdyoung 	    SC1100_GCB_WDTO, sc->sc_countdown);
15009b51ec9Skardel }
15109b51ec9Skardel 
15209b51ec9Skardel static int
geode_wdog_tickle(struct sysmon_wdog * smw)15309b51ec9Skardel geode_wdog_tickle(struct sysmon_wdog *smw)
15409b51ec9Skardel {
15509b51ec9Skardel 	int s;
15609b51ec9Skardel 	struct geode_wdog_softc *sc = smw->smw_cookie;
15709b51ec9Skardel 
15809b51ec9Skardel 	s = splhigh();
15909b51ec9Skardel 	geode_wdog_reset(sc);
16009b51ec9Skardel 	splx(s);
16109b51ec9Skardel 	return 0;
16209b51ec9Skardel }
16309b51ec9Skardel 
16409b51ec9Skardel static int
geode_wdog_setmode(struct sysmon_wdog * smw)16509b51ec9Skardel geode_wdog_setmode(struct sysmon_wdog *smw)
16609b51ec9Skardel {
16709b51ec9Skardel 	struct geode_wdog_softc *sc = smw->smw_cookie;
16809b51ec9Skardel 	uint32_t ticks;
16909b51ec9Skardel 	int prescale, s;
17009b51ec9Skardel 
17109b51ec9Skardel 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
17209b51ec9Skardel 		s = splhigh();
17309b51ec9Skardel 		geode_wdog_disable(sc);
17409b51ec9Skardel 		splx(s);
17509b51ec9Skardel 		return 0;
17609b51ec9Skardel 	}
17709b51ec9Skardel 	if (smw->smw_period == WDOG_PERIOD_DEFAULT)
17809b51ec9Skardel 		smw->smw_period = 32;
17909b51ec9Skardel 	else if (smw->smw_period > SC1100_WDIVL_MAX) /* too big? */
18009b51ec9Skardel 		return EINVAL;
18109b51ec9Skardel 
18209b51ec9Skardel 	GEODE_DPRINTF(("%s: period %u\n", __func__, smw->smw_period));
18309b51ec9Skardel 
18409b51ec9Skardel 	ticks = smw->smw_period * SC1100_WDCLK_HZ;
18509b51ec9Skardel 
18609b51ec9Skardel 	GEODE_DPRINTF(("%s: ticks0 %" PRIu32 "\n", __func__, ticks));
18709b51ec9Skardel 
18809b51ec9Skardel 	for (prescale = 0; ticks > UINT16_MAX; prescale++)
18909b51ec9Skardel 		ticks /= 2;
19009b51ec9Skardel 
19109b51ec9Skardel 	GEODE_DPRINTF(("%s: ticks %" PRIu32 "\n", __func__, ticks));
19209b51ec9Skardel 	GEODE_DPRINTF(("%s: prescale %d\n", __func__, prescale));
19309b51ec9Skardel 
19409b51ec9Skardel 	KASSERT(prescale <= SC1100_WDCNFG_WDPRES_MAX);
19509b51ec9Skardel 	KASSERT(ticks <= UINT16_MAX);
19609b51ec9Skardel 
19709b51ec9Skardel 	s = splhigh();
19809b51ec9Skardel 
19909b51ec9Skardel 	sc->sc_prescale = (uint8_t)prescale;
20009b51ec9Skardel 	sc->sc_countdown = (uint16_t)ticks;
20109b51ec9Skardel 
20209b51ec9Skardel 	geode_wdog_enable(sc);
20309b51ec9Skardel 
20409b51ec9Skardel 	geode_wdog_reset(sc);
20509b51ec9Skardel 
20609b51ec9Skardel 	splx(s);
20709b51ec9Skardel 	return 0;
20809b51ec9Skardel }
20909b51ec9Skardel 
21009b51ec9Skardel static int
geode_wdog_match(device_t parent,cfdata_t match,void * aux)211ed38b748Sxtraeme geode_wdog_match(device_t parent, cfdata_t match, void *aux)
21209b51ec9Skardel {
21309b51ec9Skardel 	return !attached;
21409b51ec9Skardel }
21509b51ec9Skardel 
21609b51ec9Skardel static void
geode_wdog_attach(device_t parent,device_t self,void * aux)217d4755581Sdyoung geode_wdog_attach(device_t parent, device_t self, void *aux)
21809b51ec9Skardel {
219d4755581Sdyoung 	struct geode_wdog_softc *sc = device_private(self);
22009b51ec9Skardel 	uint8_t wdsts;
22109b51ec9Skardel 
22209b51ec9Skardel 	aprint_naive(": Watchdog Timer\n");
22309b51ec9Skardel 	aprint_normal(": AMD Geode SC1100 Watchdog Timer\n");
22409b51ec9Skardel 
22509b51ec9Skardel 
22609b51ec9Skardel 	/*
22709b51ec9Skardel 	 * Hook up the watchdog timer.
22809b51ec9Skardel 	 */
229d4755581Sdyoung 	sc->sc_gcb_dev = device_private(parent);
230d4755581Sdyoung 	sc->sc_smw.smw_name = device_xname(self);
23109b51ec9Skardel 	sc->sc_smw.smw_cookie = sc;
23209b51ec9Skardel 	sc->sc_smw.smw_setmode = geode_wdog_setmode;
23309b51ec9Skardel 	sc->sc_smw.smw_tickle = geode_wdog_tickle;
23409b51ec9Skardel 	sc->sc_smw.smw_period = 32;
23509b51ec9Skardel 
23609b51ec9Skardel 	/*
23709b51ec9Skardel 	 * Determine cause of the last reset, and issue a warning if it
23809b51ec9Skardel 	 * was due to watchdog expiry.
23909b51ec9Skardel 	 */
240*71fbb921Smsaitoh 	wdsts = bus_space_read_1(sc->sc_gcb_dev->sc_iot,
241*71fbb921Smsaitoh 	    sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDSTS);
24209b51ec9Skardel 
243d4755581Sdyoung 	GEODE_DPRINTF(("%s: status %#02" PRIx8 "\n", device_xname(self),
24409b51ec9Skardel 	    wdsts));
24509b51ec9Skardel 
24609b51ec9Skardel 	if (wdsts & SC1100_WDSTS_WDRST)
24709b51ec9Skardel 		aprint_error(
24809b51ec9Skardel 		    "%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
249d4755581Sdyoung 		    device_xname(self));
25009b51ec9Skardel 
25109b51ec9Skardel 	/* reset WDOVF by writing 1 to it */
252d4755581Sdyoung 	bus_space_write_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
253d4755581Sdyoung 	    SC1100_GCB_WDSTS, wdsts & SC1100_WDSTS_WDOVF);
25409b51ec9Skardel 
25509b51ec9Skardel 	if (sysmon_wdog_register(&sc->sc_smw) != 0)
25609b51ec9Skardel 		aprint_error("%s: unable to register watchdog with sysmon\n",
257d4755581Sdyoung 		    device_xname(self));
25809b51ec9Skardel 
25909b51ec9Skardel 	/* cancel any pending countdown */
26009b51ec9Skardel 	geode_wdog_disable(sc);
26109b51ec9Skardel 
26209b51ec9Skardel 	attached = 1;
26309b51ec9Skardel }
26409b51ec9Skardel 
2658d9f58eaSdyoung static int
geode_wdog_detach(device_t self,int flags)2668d9f58eaSdyoung geode_wdog_detach(device_t self, int flags)
2678d9f58eaSdyoung {
2688d9f58eaSdyoung 	int rc;
2698d9f58eaSdyoung 	struct geode_wdog_softc *sc = device_private(self);
2708d9f58eaSdyoung 
2718d9f58eaSdyoung 	if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) {
2728d9f58eaSdyoung 		if (rc == ERESTART)
2738d9f58eaSdyoung 			rc = EINTR;
2748d9f58eaSdyoung 		return rc;
2758d9f58eaSdyoung 	}
2768d9f58eaSdyoung 
2778d9f58eaSdyoung 	/* cancel any pending countdown */
2788d9f58eaSdyoung 	geode_wdog_disable(sc);
2798d9f58eaSdyoung 
2808d9f58eaSdyoung 	attached = 0;
2818d9f58eaSdyoung 
2828d9f58eaSdyoung 	return 0;
2838d9f58eaSdyoung }
2848d9f58eaSdyoung 
285ed38b748Sxtraeme CFATTACH_DECL_NEW(geodewdog, sizeof(struct geode_wdog_softc),
2868d9f58eaSdyoung 	      geode_wdog_match, geode_wdog_attach, geode_wdog_detach, NULL);
287