octeon_wdog.c revision 215989
1215989Sgonzo/*-
2215989Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3215989Sgonzo * All rights reserved.
4215989Sgonzo *
5215989Sgonzo * Redistribution and use in source and binary forms, with or without
6215989Sgonzo * modification, are permitted provided that the following conditions
7215989Sgonzo * are met:
8215989Sgonzo * 1. Redistributions of source code must retain the above copyright
9215989Sgonzo *    notice unmodified, this list of conditions, and the following
10215989Sgonzo *    disclaimer.
11215989Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12215989Sgonzo *    notice, this list of conditions and the following disclaimer in the
13215989Sgonzo *    documentation and/or other materials provided with the distribution.
14215989Sgonzo *
15215989Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16215989Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17215989Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18215989Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19215989Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20215989Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21215989Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22215989Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23215989Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24215989Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25215989Sgonzo * SUCH DAMAGE.
26215989Sgonzo */
27215989Sgonzo
28215989Sgonzo/*
29215989Sgonzo * Watchdog driver for Cavium Octeon
30215989Sgonzo */
31215989Sgonzo
32215989Sgonzo#include <sys/cdefs.h>
33215989Sgonzo__FBSDID("$FreeBSD: head/sys/mips/cavium/octeon_wdog.c 215989 2010-11-28 08:11:05Z gonzo $");
34215989Sgonzo
35215989Sgonzo#include <sys/param.h>
36215989Sgonzo#include <sys/systm.h>
37215989Sgonzo#include <sys/watchdog.h>
38215989Sgonzo#include <sys/bus.h>
39215989Sgonzo#include <sys/kernel.h>
40215989Sgonzo#include <sys/module.h>
41215989Sgonzo#include <sys/sysctl.h>
42215989Sgonzo#include <sys/rman.h>
43215989Sgonzo#include <sys/smp.h>
44215989Sgonzo
45215989Sgonzo#include <contrib/octeon-sdk/cvmx.h>
46215989Sgonzo#include <contrib/octeon-sdk/cvmx-interrupt.h>
47215989Sgonzo
48215989Sgonzo#define	DEFAULT_TIMER_VAL	65535
49215989Sgonzo
50215989Sgonzostruct octeon_wdog_softc {
51215989Sgonzo	device_t dev;
52215989Sgonzo	/* XXX: replace with repscive CVMX_ constant */
53215989Sgonzo	struct resource *irq_res[16];
54215989Sgonzo	void *intr_hdl[16];
55215989Sgonzo	int armed;
56215989Sgonzo	int debug;
57215989Sgonzo};
58215989Sgonzo
59215989Sgonzoextern void octeon_wdog_nmi_handler(void);
60215989Sgonzovoid octeon_wdog_nmi(void);
61215989Sgonzo
62215989Sgonzostatic void octeon_watchdog_arm_core(int core, unsigned long timer_val);
63215989Sgonzostatic void octeon_watchdog_disarm_core(int core);
64215989Sgonzostatic int octeon_wdog_attach(device_t dev);
65215989Sgonzostatic void octeon_wdog_identify(driver_t *drv, device_t parent);
66215989Sgonzostatic int octeon_wdog_intr(void *);;
67215989Sgonzostatic int octeon_wdog_probe(device_t dev);
68215989Sgonzostatic void octeon_wdog_setup(struct octeon_wdog_softc *sc, int cpu);
69215989Sgonzostatic void octeon_wdog_sysctl(device_t dev);
70215989Sgonzostatic void octeon_wdog_watchdog_fn(void *private, u_int cmd, int *error);
71215989Sgonzo
72215989Sgonzovoid
73215989Sgonzoocteon_wdog_nmi()
74215989Sgonzo{
75215989Sgonzo
76215989Sgonzo	/* XXX: Add something useful here */
77215989Sgonzo	printf("NMI detected\n");
78215989Sgonzo
79215989Sgonzo	/*
80215989Sgonzo	 * This is the end
81215989Sgonzo	 * Beautiful friend
82215989Sgonzo	 *
83215989Sgonzo	 * Just wait for Soft Reset to come and take us
84215989Sgonzo	 */
85215989Sgonzo	for (;;)
86215989Sgonzo		;
87215989Sgonzo}
88215989Sgonzo
89215989Sgonzostatic void
90215989Sgonzoocteon_watchdog_arm_core(int core, unsigned long timer_val)
91215989Sgonzo{
92215989Sgonzo	cvmx_ciu_wdogx_t ciu_wdog;
93215989Sgonzo
94215989Sgonzo	/* Poke it! */
95215989Sgonzo	cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1);
96215989Sgonzo
97215989Sgonzo	ciu_wdog.u64 = 0;
98215989Sgonzo	ciu_wdog.s.len = timer_val;
99215989Sgonzo	ciu_wdog.s.mode = 3;
100215989Sgonzo	cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64);
101215989Sgonzo}
102215989Sgonzo
103215989Sgonzostatic void
104215989Sgonzoocteon_watchdog_disarm_core(int core)
105215989Sgonzo{
106215989Sgonzo
107215989Sgonzo	cvmx_write_csr(CVMX_CIU_WDOGX(core), 0);
108215989Sgonzo}
109215989Sgonzo
110215989Sgonzo
111215989Sgonzo
112215989Sgonzostatic void
113215989Sgonzoocteon_wdog_watchdog_fn(void *private, u_int cmd, int *error)
114215989Sgonzo{
115215989Sgonzo	struct octeon_wdog_softc *sc = private;
116215989Sgonzo	uint64_t timer_val = 0;
117215989Sgonzo
118215989Sgonzo	cmd &= WD_INTERVAL;
119215989Sgonzo	if (sc->debug)
120215989Sgonzo		device_printf(sc->dev, "octeon_wdog_watchdog_fn: cmd: %x\n", cmd);
121215989Sgonzo	if (cmd > 0) {
122215989Sgonzo		if (sc->debug)
123215989Sgonzo			device_printf(sc->dev, "octeon_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val);
124215989Sgonzo		/*
125215989Sgonzo		 * XXX: This should be done for every core and with value
126215989Sgonzo		 * calculated based on CPU frquency
127215989Sgonzo		 */
128215989Sgonzo		octeon_watchdog_arm_core(cvmx_get_core_num(), DEFAULT_TIMER_VAL);
129215989Sgonzo		sc->armed = 1;
130215989Sgonzo		*error = 0;
131215989Sgonzo	} else {
132215989Sgonzo		if (sc->debug)
133215989Sgonzo			device_printf(sc->dev, "octeon_wdog_watchdog_fn: disarming\n");
134215989Sgonzo		if (sc->armed) {
135215989Sgonzo			sc->armed = 0;
136215989Sgonzo		 	/* XXX: This should be done for every core */
137215989Sgonzo			octeon_watchdog_disarm_core(cvmx_get_core_num());
138215989Sgonzo		}
139215989Sgonzo	}
140215989Sgonzo}
141215989Sgonzo
142215989Sgonzostatic void
143215989Sgonzoocteon_wdog_sysctl(device_t dev)
144215989Sgonzo{
145215989Sgonzo	struct octeon_wdog_softc *sc = device_get_softc(dev);
146215989Sgonzo
147215989Sgonzo        struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
148215989Sgonzo        struct sysctl_oid *tree = device_get_sysctl_tree(sc->dev);
149215989Sgonzo
150215989Sgonzo        SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
151215989Sgonzo                "debug", CTLFLAG_RW, &sc->debug, 0,
152215989Sgonzo                "enable watchdog debugging");
153215989Sgonzo        SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
154215989Sgonzo                "armed", CTLFLAG_RD, &sc->armed, 0,
155215989Sgonzo                "whether the watchdog is armed");
156215989Sgonzo}
157215989Sgonzo
158215989Sgonzostatic void
159215989Sgonzoocteon_wdog_setup(struct octeon_wdog_softc *sc, int cpu)
160215989Sgonzo{
161215989Sgonzo	int core, rid, err;
162215989Sgonzo
163215989Sgonzo	/* XXX: map cpu id to core here ? */
164215989Sgonzo	core = cvmx_get_core_num();
165215989Sgonzo
166215989Sgonzo	/* Interrupt part */
167215989Sgonzo	rid = 0;
168215989Sgonzo	sc->irq_res[core] =
169215989Sgonzo		bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid,
170215989Sgonzo			CVMX_IRQ_WDOG0+core,
171215989Sgonzo			CVMX_IRQ_WDOG0+core, 1, RF_ACTIVE);
172215989Sgonzo	if (!(sc->irq_res[core]))
173215989Sgonzo		goto error;
174215989Sgonzo
175215989Sgonzo	err = bus_setup_intr(sc->dev, sc->irq_res[core], INTR_TYPE_MISC,
176215989Sgonzo	    octeon_wdog_intr, NULL, sc, &sc->intr_hdl[core]);
177215989Sgonzo	if (err)
178215989Sgonzo		goto error;
179215989Sgonzo
180215989Sgonzo	/* XXX: pin interrupt handler to the respective core */
181215989Sgonzo
182215989Sgonzo	/* Disarm by default */
183215989Sgonzo	octeon_watchdog_disarm_core(core);
184215989Sgonzo
185215989Sgonzo	return;
186215989Sgonzo
187215989Sgonzoerror:
188215989Sgonzo	panic("failed to setup watchdog interrupt for core %d", core);
189215989Sgonzo}
190215989Sgonzo
191215989Sgonzo
192215989Sgonzostatic int
193215989Sgonzoocteon_wdog_intr(void *sc)
194215989Sgonzo{
195215989Sgonzo
196215989Sgonzo	/* Poke it! */
197215989Sgonzo	cvmx_write_csr(CVMX_CIU_PP_POKEX(cvmx_get_core_num()), 1);
198215989Sgonzo
199215989Sgonzo	return (FILTER_HANDLED);
200215989Sgonzo}
201215989Sgonzo
202215989Sgonzostatic int
203215989Sgonzoocteon_wdog_probe(device_t dev)
204215989Sgonzo{
205215989Sgonzo
206215989Sgonzo	device_set_desc(dev, "Cavium Octeon watchdog timer");
207215989Sgonzo	return (0);
208215989Sgonzo}
209215989Sgonzo
210215989Sgonzostatic int
211215989Sgonzoocteon_wdog_attach(device_t dev)
212215989Sgonzo{
213215989Sgonzo	struct octeon_wdog_softc *sc = device_get_softc(dev);
214215989Sgonzo	int i;
215215989Sgonzo	uint64_t *nmi_handler = (uint64_t*)octeon_wdog_nmi_handler;
216215989Sgonzo
217215989Sgonzo	/* Initialise */
218215989Sgonzo	sc->armed = 0;
219215989Sgonzo	sc->debug = 0;
220215989Sgonzo
221215989Sgonzo	sc->dev = dev;
222215989Sgonzo	EVENTHANDLER_REGISTER(watchdog_list, octeon_wdog_watchdog_fn, sc, 0);
223215989Sgonzo	octeon_wdog_sysctl(dev);
224215989Sgonzo
225215989Sgonzo	for (i = 0; i < 16; i++) {
226215989Sgonzo		cvmx_write_csr(CVMX_MIO_BOOT_LOC_ADR, i * 8);
227215989Sgonzo		cvmx_write_csr(CVMX_MIO_BOOT_LOC_DAT, nmi_handler[i]);
228215989Sgonzo        }
229215989Sgonzo
230215989Sgonzo	cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0x81fc0000);
231215989Sgonzo
232215989Sgonzo	/* XXX: This should be done for every core */
233215989Sgonzo	octeon_wdog_setup(sc, cvmx_get_core_num());
234215989Sgonzo	return (0);
235215989Sgonzo}
236215989Sgonzo
237215989Sgonzostatic void
238215989Sgonzoocteon_wdog_identify(driver_t *drv, device_t parent)
239215989Sgonzo{
240215989Sgonzo
241215989Sgonzo	BUS_ADD_CHILD(parent, 0, "octeon_wdog", 0);
242215989Sgonzo}
243215989Sgonzo
244215989Sgonzostatic device_method_t octeon_wdog_methods[] = {
245215989Sgonzo	DEVMETHOD(device_identify, octeon_wdog_identify),
246215989Sgonzo
247215989Sgonzo	DEVMETHOD(device_probe, octeon_wdog_probe),
248215989Sgonzo	DEVMETHOD(device_attach, octeon_wdog_attach),
249215989Sgonzo	{0, 0},
250215989Sgonzo};
251215989Sgonzo
252215989Sgonzostatic driver_t octeon_wdog_driver = {
253215989Sgonzo	"octeon_wdog",
254215989Sgonzo	octeon_wdog_methods,
255215989Sgonzo	sizeof(struct octeon_wdog_softc),
256215989Sgonzo};
257215989Sgonzostatic devclass_t octeon_wdog_devclass;
258215989Sgonzo
259215989SgonzoDRIVER_MODULE(octeon_wdog, ciu, octeon_wdog_driver, octeon_wdog_devclass, 0, 0);
260