xref: /freebsd/sys/dev/viawd/viawd.c (revision 61af1d13936ec56808f62d13dd8698f73b440dc1)
1*61af1d13SFabien Thomas /*-
2*61af1d13SFabien Thomas  * Copyright (c) 2011 Fabien Thomas <fthomas@FreeBSD.org>
3*61af1d13SFabien Thomas  * All rights reserved.
4*61af1d13SFabien Thomas  *
5*61af1d13SFabien Thomas  * Redistribution and use in source and binary forms, with or without
6*61af1d13SFabien Thomas  * modification, are permitted provided that the following conditions
7*61af1d13SFabien Thomas  * are met:
8*61af1d13SFabien Thomas  * 1. Redistributions of source code must retain the above copyright
9*61af1d13SFabien Thomas  *    notice, this list of conditions and the following disclaimer.
10*61af1d13SFabien Thomas  * 2. Redistributions in binary form must reproduce the above copyright
11*61af1d13SFabien Thomas  *    notice, this list of conditions and the following disclaimer in the
12*61af1d13SFabien Thomas  *    documentation and/or other materials provided with the distribution.
13*61af1d13SFabien Thomas  *
14*61af1d13SFabien Thomas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*61af1d13SFabien Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*61af1d13SFabien Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*61af1d13SFabien Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*61af1d13SFabien Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*61af1d13SFabien Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*61af1d13SFabien Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*61af1d13SFabien Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*61af1d13SFabien Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*61af1d13SFabien Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*61af1d13SFabien Thomas  * SUCH DAMAGE.
25*61af1d13SFabien Thomas  */
26*61af1d13SFabien Thomas 
27*61af1d13SFabien Thomas #include <sys/cdefs.h>
28*61af1d13SFabien Thomas __FBSDID("$FreeBSD$");
29*61af1d13SFabien Thomas 
30*61af1d13SFabien Thomas #include <sys/param.h>
31*61af1d13SFabien Thomas #include <sys/kernel.h>
32*61af1d13SFabien Thomas #include <sys/module.h>
33*61af1d13SFabien Thomas #include <sys/systm.h>
34*61af1d13SFabien Thomas #include <sys/bus.h>
35*61af1d13SFabien Thomas #include <machine/bus.h>
36*61af1d13SFabien Thomas #include <sys/rman.h>
37*61af1d13SFabien Thomas #include <machine/resource.h>
38*61af1d13SFabien Thomas #include <sys/watchdog.h>
39*61af1d13SFabien Thomas 
40*61af1d13SFabien Thomas #include <isa/isavar.h>
41*61af1d13SFabien Thomas #include <dev/pci/pcivar.h>
42*61af1d13SFabien Thomas 
43*61af1d13SFabien Thomas #include "viawd.h"
44*61af1d13SFabien Thomas 
45*61af1d13SFabien Thomas #define viawd_read_wd_4(sc, off) \
46*61af1d13SFabien Thomas 	bus_space_read_4((sc)->wd_bst, (sc)->wd_bsh, (off))
47*61af1d13SFabien Thomas #define viawd_write_wd_4(sc, off, val) \
48*61af1d13SFabien Thomas 	bus_space_write_4((sc)->wd_bst, (sc)->wd_bsh, (off), (val))
49*61af1d13SFabien Thomas 
50*61af1d13SFabien Thomas static struct viawd_device viawd_devices[] = {
51*61af1d13SFabien Thomas 	{ DEVICEID_VT8251, "VIA VT8251 watchdog timer" },
52*61af1d13SFabien Thomas 	{ DEVICEID_CX700,  "VIA CX700 watchdog timer" },
53*61af1d13SFabien Thomas 	{ DEVICEID_VX800,  "VIA VX800 watchdog timer" },
54*61af1d13SFabien Thomas 	{ DEVICEID_VX855,  "VIA VX855 watchdog timer" },
55*61af1d13SFabien Thomas 	{ DEVICEID_VX900,  "VIA VX900 watchdog timer" },
56*61af1d13SFabien Thomas 	{ 0, NULL },
57*61af1d13SFabien Thomas };
58*61af1d13SFabien Thomas 
59*61af1d13SFabien Thomas static devclass_t viawd_devclass;
60*61af1d13SFabien Thomas 
61*61af1d13SFabien Thomas static device_t
62*61af1d13SFabien Thomas viawd_find(struct viawd_device **id_p)
63*61af1d13SFabien Thomas {
64*61af1d13SFabien Thomas 	struct viawd_device *id;
65*61af1d13SFabien Thomas 	device_t sb_dev = NULL;
66*61af1d13SFabien Thomas 
67*61af1d13SFabien Thomas 	/* Look for a supported VIA south bridge. */
68*61af1d13SFabien Thomas 	for (id = viawd_devices; id->desc != NULL; ++id)
69*61af1d13SFabien Thomas 		if ((sb_dev = pci_find_device(VENDORID_VIA, id->device)) != NULL)
70*61af1d13SFabien Thomas 			break;
71*61af1d13SFabien Thomas 
72*61af1d13SFabien Thomas 	if (sb_dev == NULL)
73*61af1d13SFabien Thomas 		return (NULL);
74*61af1d13SFabien Thomas 
75*61af1d13SFabien Thomas 	if (id_p != NULL)
76*61af1d13SFabien Thomas 		*id_p = id;
77*61af1d13SFabien Thomas 
78*61af1d13SFabien Thomas 	return (sb_dev);
79*61af1d13SFabien Thomas }
80*61af1d13SFabien Thomas 
81*61af1d13SFabien Thomas static void
82*61af1d13SFabien Thomas viawd_tmr_state(struct viawd_softc *sc, int enable)
83*61af1d13SFabien Thomas {
84*61af1d13SFabien Thomas 	uint32_t reg;
85*61af1d13SFabien Thomas 
86*61af1d13SFabien Thomas 	reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL);
87*61af1d13SFabien Thomas 	if (enable)
88*61af1d13SFabien Thomas 		reg |= VIAWD_MEM_CTRL_TRIGGER | VIAWD_MEM_CTRL_ENABLE;
89*61af1d13SFabien Thomas 	else
90*61af1d13SFabien Thomas 		reg &= ~VIAWD_MEM_CTRL_ENABLE;
91*61af1d13SFabien Thomas 	viawd_write_wd_4(sc, VIAWD_MEM_CTRL, reg);
92*61af1d13SFabien Thomas }
93*61af1d13SFabien Thomas 
94*61af1d13SFabien Thomas static void
95*61af1d13SFabien Thomas viawd_tmr_set(struct viawd_softc *sc, unsigned int timeout)
96*61af1d13SFabien Thomas {
97*61af1d13SFabien Thomas 
98*61af1d13SFabien Thomas 	/* Keep value in range. */
99*61af1d13SFabien Thomas 	if (timeout < VIAWD_MEM_COUNT_MIN)
100*61af1d13SFabien Thomas 		timeout = VIAWD_MEM_COUNT_MIN;
101*61af1d13SFabien Thomas 	else if (timeout > VIAWD_MEM_COUNT_MAX)
102*61af1d13SFabien Thomas 		timeout = VIAWD_MEM_COUNT_MAX;
103*61af1d13SFabien Thomas 
104*61af1d13SFabien Thomas 	viawd_write_wd_4(sc, VIAWD_MEM_COUNT, timeout);
105*61af1d13SFabien Thomas 	sc->timeout = timeout;
106*61af1d13SFabien Thomas }
107*61af1d13SFabien Thomas 
108*61af1d13SFabien Thomas /*
109*61af1d13SFabien Thomas  * Watchdog event handler - called by the framework to enable or disable
110*61af1d13SFabien Thomas  * the watchdog or change the initial timeout value.
111*61af1d13SFabien Thomas  */
112*61af1d13SFabien Thomas static void
113*61af1d13SFabien Thomas viawd_event(void *arg, unsigned int cmd, int *error)
114*61af1d13SFabien Thomas {
115*61af1d13SFabien Thomas 	struct viawd_softc *sc = arg;
116*61af1d13SFabien Thomas 	unsigned int timeout;
117*61af1d13SFabien Thomas 
118*61af1d13SFabien Thomas 	/* Convert from power-of-two-ns to second. */
119*61af1d13SFabien Thomas 	cmd &= WD_INTERVAL;
120*61af1d13SFabien Thomas 	timeout = ((uint64_t)1 << cmd) / 1000000000;
121*61af1d13SFabien Thomas 	if (cmd) {
122*61af1d13SFabien Thomas 		if (timeout != sc->timeout)
123*61af1d13SFabien Thomas 			viawd_tmr_set(sc, timeout);
124*61af1d13SFabien Thomas 		viawd_tmr_state(sc, 1);
125*61af1d13SFabien Thomas 		*error = 0;
126*61af1d13SFabien Thomas 	} else
127*61af1d13SFabien Thomas 		viawd_tmr_state(sc, 0);
128*61af1d13SFabien Thomas }
129*61af1d13SFabien Thomas 
130*61af1d13SFabien Thomas static void
131*61af1d13SFabien Thomas viawd_identify(driver_t *driver, device_t parent)
132*61af1d13SFabien Thomas {
133*61af1d13SFabien Thomas 	device_t dev;
134*61af1d13SFabien Thomas 	device_t sb_dev;
135*61af1d13SFabien Thomas 	struct viawd_device *id_p;
136*61af1d13SFabien Thomas 
137*61af1d13SFabien Thomas 	sb_dev = viawd_find(&id_p);
138*61af1d13SFabien Thomas 	if (sb_dev == NULL)
139*61af1d13SFabien Thomas 		return;
140*61af1d13SFabien Thomas 
141*61af1d13SFabien Thomas 	/* Good, add child to bus. */
142*61af1d13SFabien Thomas 	if ((dev = device_find_child(parent, driver->name, 0)) == NULL)
143*61af1d13SFabien Thomas 		dev = BUS_ADD_CHILD(parent, 0, driver->name, 0);
144*61af1d13SFabien Thomas 
145*61af1d13SFabien Thomas 	if (dev == NULL)
146*61af1d13SFabien Thomas 		return;
147*61af1d13SFabien Thomas 
148*61af1d13SFabien Thomas 	device_set_desc_copy(dev, id_p->desc);
149*61af1d13SFabien Thomas }
150*61af1d13SFabien Thomas 
151*61af1d13SFabien Thomas static int
152*61af1d13SFabien Thomas viawd_probe(device_t dev)
153*61af1d13SFabien Thomas {
154*61af1d13SFabien Thomas 
155*61af1d13SFabien Thomas 	/* Do not claim some ISA PnP device by accident. */
156*61af1d13SFabien Thomas 	if (isa_get_logicalid(dev) != 0)
157*61af1d13SFabien Thomas 		return (ENXIO);
158*61af1d13SFabien Thomas 	return (0);
159*61af1d13SFabien Thomas }
160*61af1d13SFabien Thomas 
161*61af1d13SFabien Thomas static int
162*61af1d13SFabien Thomas viawd_attach(device_t dev)
163*61af1d13SFabien Thomas {
164*61af1d13SFabien Thomas 	device_t sb_dev;
165*61af1d13SFabien Thomas 	struct viawd_softc *sc;
166*61af1d13SFabien Thomas 	struct viawd_device *id_p;
167*61af1d13SFabien Thomas 	uint32_t pmbase, reg;
168*61af1d13SFabien Thomas 
169*61af1d13SFabien Thomas 	sc = device_get_softc(dev);
170*61af1d13SFabien Thomas 	sc->dev = dev;
171*61af1d13SFabien Thomas 
172*61af1d13SFabien Thomas 	sb_dev = viawd_find(&id_p);
173*61af1d13SFabien Thomas 	if (sb_dev == NULL) {
174*61af1d13SFabien Thomas 		device_printf(dev, "Can not find watchdog device.\n");
175*61af1d13SFabien Thomas 		goto fail;
176*61af1d13SFabien Thomas 	}
177*61af1d13SFabien Thomas 	sc->sb_dev = sb_dev;
178*61af1d13SFabien Thomas 
179*61af1d13SFabien Thomas 	/* Get watchdog memory base. */
180*61af1d13SFabien Thomas 	pmbase = pci_read_config(sb_dev, VIAWD_CONFIG_BASE, 4);
181*61af1d13SFabien Thomas 	if (pmbase == 0) {
182*61af1d13SFabien Thomas 		device_printf(dev,
183*61af1d13SFabien Thomas 		    "Watchdog disabled in BIOS or hardware\n");
184*61af1d13SFabien Thomas 		goto fail;
185*61af1d13SFabien Thomas 	}
186*61af1d13SFabien Thomas 
187*61af1d13SFabien Thomas 	/* Allocate I/O register space. */
188*61af1d13SFabien Thomas 	sc->wd_rid = 0;
189*61af1d13SFabien Thomas 	sc->wd_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->wd_rid,
190*61af1d13SFabien Thomas 	    pmbase, pmbase + VIAWD_MEM_LEN - 1, VIAWD_MEM_LEN,
191*61af1d13SFabien Thomas 	    RF_ACTIVE | RF_SHAREABLE);
192*61af1d13SFabien Thomas 	if (sc->wd_res == NULL) {
193*61af1d13SFabien Thomas 		device_printf(dev, "Unable to map watchdog memory\n");
194*61af1d13SFabien Thomas 		goto fail;
195*61af1d13SFabien Thomas 	}
196*61af1d13SFabien Thomas 	sc->wd_bst = rman_get_bustag(sc->wd_res);
197*61af1d13SFabien Thomas 	sc->wd_bsh = rman_get_bushandle(sc->wd_res);
198*61af1d13SFabien Thomas 
199*61af1d13SFabien Thomas 	/* Check if watchdog fired last boot. */
200*61af1d13SFabien Thomas 	reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL);
201*61af1d13SFabien Thomas 	if (reg & VIAWD_MEM_CTRL_FIRED) {
202*61af1d13SFabien Thomas 		device_printf(dev,
203*61af1d13SFabien Thomas 		    "ERROR: watchdog rebooted the system\n");
204*61af1d13SFabien Thomas 		/* Reset bit state. */
205*61af1d13SFabien Thomas 		viawd_write_wd_4(sc, VIAWD_MEM_CTRL, reg);
206*61af1d13SFabien Thomas 	}
207*61af1d13SFabien Thomas 
208*61af1d13SFabien Thomas 	/* Register the watchdog event handler. */
209*61af1d13SFabien Thomas 	sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, viawd_event, sc, 0);
210*61af1d13SFabien Thomas 
211*61af1d13SFabien Thomas 	return (0);
212*61af1d13SFabien Thomas fail:
213*61af1d13SFabien Thomas 	if (sc->wd_res != NULL)
214*61af1d13SFabien Thomas 		bus_release_resource(dev, SYS_RES_MEMORY,
215*61af1d13SFabien Thomas 		    sc->wd_rid, sc->wd_res);
216*61af1d13SFabien Thomas 	return (ENXIO);
217*61af1d13SFabien Thomas }
218*61af1d13SFabien Thomas 
219*61af1d13SFabien Thomas static int
220*61af1d13SFabien Thomas viawd_detach(device_t dev)
221*61af1d13SFabien Thomas {
222*61af1d13SFabien Thomas 	struct viawd_softc *sc;
223*61af1d13SFabien Thomas 	uint32_t reg;
224*61af1d13SFabien Thomas 
225*61af1d13SFabien Thomas 	sc = device_get_softc(dev);
226*61af1d13SFabien Thomas 
227*61af1d13SFabien Thomas 	/* Deregister event handler. */
228*61af1d13SFabien Thomas 	if (sc->ev_tag != NULL)
229*61af1d13SFabien Thomas 		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
230*61af1d13SFabien Thomas 	sc->ev_tag = NULL;
231*61af1d13SFabien Thomas 
232*61af1d13SFabien Thomas 	/*
233*61af1d13SFabien Thomas 	 * Do not stop the watchdog on shutdown if active but bump the
234*61af1d13SFabien Thomas 	 * timer to avoid spurious reset.
235*61af1d13SFabien Thomas 	 */
236*61af1d13SFabien Thomas 	reg = viawd_read_wd_4(sc, VIAWD_MEM_CTRL);
237*61af1d13SFabien Thomas 	if (reg & VIAWD_MEM_CTRL_ENABLE) {
238*61af1d13SFabien Thomas 		viawd_tmr_set(sc, VIAWD_TIMEOUT_SHUTDOWN);
239*61af1d13SFabien Thomas 		viawd_tmr_state(sc, 1);
240*61af1d13SFabien Thomas 		device_printf(dev,
241*61af1d13SFabien Thomas 		    "Keeping watchog alive during shutdown for %d seconds\n",
242*61af1d13SFabien Thomas 		    VIAWD_TIMEOUT_SHUTDOWN);
243*61af1d13SFabien Thomas 	}
244*61af1d13SFabien Thomas 
245*61af1d13SFabien Thomas 	if (sc->wd_res != NULL)
246*61af1d13SFabien Thomas 		bus_release_resource(sc->dev, SYS_RES_MEMORY,
247*61af1d13SFabien Thomas 		    sc->wd_rid, sc->wd_res);
248*61af1d13SFabien Thomas 
249*61af1d13SFabien Thomas 	return (0);
250*61af1d13SFabien Thomas }
251*61af1d13SFabien Thomas 
252*61af1d13SFabien Thomas static device_method_t viawd_methods[] = {
253*61af1d13SFabien Thomas 	DEVMETHOD(device_identify, viawd_identify),
254*61af1d13SFabien Thomas 	DEVMETHOD(device_probe,	viawd_probe),
255*61af1d13SFabien Thomas 	DEVMETHOD(device_attach, viawd_attach),
256*61af1d13SFabien Thomas 	DEVMETHOD(device_detach, viawd_detach),
257*61af1d13SFabien Thomas 	DEVMETHOD(device_shutdown, viawd_detach),
258*61af1d13SFabien Thomas 	{0,0}
259*61af1d13SFabien Thomas };
260*61af1d13SFabien Thomas 
261*61af1d13SFabien Thomas static driver_t viawd_driver = {
262*61af1d13SFabien Thomas 	"viawd",
263*61af1d13SFabien Thomas 	viawd_methods,
264*61af1d13SFabien Thomas 	sizeof(struct viawd_softc),
265*61af1d13SFabien Thomas };
266*61af1d13SFabien Thomas 
267*61af1d13SFabien Thomas static int
268*61af1d13SFabien Thomas viawd_modevent(module_t mode, int type, void *data)
269*61af1d13SFabien Thomas {
270*61af1d13SFabien Thomas 	int error = 0;
271*61af1d13SFabien Thomas 
272*61af1d13SFabien Thomas 	switch (type) {
273*61af1d13SFabien Thomas 	case MOD_LOAD:
274*61af1d13SFabien Thomas 		printf("viawd module loaded\n");
275*61af1d13SFabien Thomas 		break;
276*61af1d13SFabien Thomas 	case MOD_UNLOAD:
277*61af1d13SFabien Thomas 		printf("viawd module unloaded\n");
278*61af1d13SFabien Thomas 		break;
279*61af1d13SFabien Thomas 	case MOD_SHUTDOWN:
280*61af1d13SFabien Thomas 		printf("viawd module shutting down\n");
281*61af1d13SFabien Thomas 		break;
282*61af1d13SFabien Thomas 	}
283*61af1d13SFabien Thomas 	return (error);
284*61af1d13SFabien Thomas }
285*61af1d13SFabien Thomas 
286*61af1d13SFabien Thomas DRIVER_MODULE(viawd, isa, viawd_driver, viawd_devclass, viawd_modevent, NULL);
287