xref: /netbsd-src/sys/arch/mips/ralink/ralink_wdog.c (revision 0062f2f2910fc21c29ed363cb5efbc0fde33f59e)
1*0062f2f2Smatt /*	$NetBSD: ralink_wdog.c,v 1.2 2011/07/28 15:38:49 matt Exp $	*/
2*0062f2f2Smatt /*-
3*0062f2f2Smatt  * Copyright (c) 2011 CradlePoint Technology, Inc.
4*0062f2f2Smatt  * All rights reserved.
5*0062f2f2Smatt  *
6*0062f2f2Smatt  *
7*0062f2f2Smatt  * Redistribution and use in source and binary forms, with or without
8*0062f2f2Smatt  * modification, are permitted provided that the following conditions
9*0062f2f2Smatt  * are met:
10*0062f2f2Smatt  * 1. Redistributions of source code must retain the above copyright
11*0062f2f2Smatt  *    notice, this list of conditions and the following disclaimer.
12*0062f2f2Smatt  * 2. Redistributions in binary form must reproduce the above copyright
13*0062f2f2Smatt  *    notice, this list of conditions and the following disclaimer in the
14*0062f2f2Smatt  *    documentation and/or other materials provided with the distribution.
15*0062f2f2Smatt  *
16*0062f2f2Smatt  * THIS SOFTWARE IS PROVIDED BY CRADLEPOINT TECHNOLOGY, INC. AND CONTRIBUTORS
17*0062f2f2Smatt  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18*0062f2f2Smatt  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19*0062f2f2Smatt  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
20*0062f2f2Smatt  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*0062f2f2Smatt  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*0062f2f2Smatt  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*0062f2f2Smatt  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*0062f2f2Smatt  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*0062f2f2Smatt  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*0062f2f2Smatt  * POSSIBILITY OF SUCH DAMAGE.
27*0062f2f2Smatt  */
28*0062f2f2Smatt 
29*0062f2f2Smatt /*
30*0062f2f2Smatt  * ra_wdog.c -- Ralink 305x Watchdog Timer driver
31*0062f2f2Smatt  *
32*0062f2f2Smatt  * Timer 1 is used as a system reset watchdog timer
33*0062f2f2Smatt  * Timer 0 is (optionally) used as a periodic watchdog service interrupt
34*0062f2f2Smatt  *
35*0062f2f2Smatt  * NetBSD sysmon watchdog is used in mode defined by RA_WDOG_DEFAULT_MODE
36*0062f2f2Smatt  * (which can be set via kernel config), or by mode passed to
37*0062f2f2Smatt  * our 'smw_setmode' function.  The mode used determines what
38*0062f2f2Smatt  * mechanism is used to periodically service the watchdog.
39*0062f2f2Smatt  *
40*0062f2f2Smatt  * KTICKLE mode is default and supports 2 variants, allowing some control
41*0062f2f2Smatt  * over the priority of the service routine:
42*0062f2f2Smatt  *
43*0062f2f2Smatt  * 1. the specified reset period is a positive integer:
44*0062f2f2Smatt  *    A callout runs the 'smw_tickle' function at IPL_SOFTCLOCK for service.
45*0062f2f2Smatt  *    If your system cannot make "forward progress" without softints running,
46*0062f2f2Smatt  *    you should use this variant.
47*0062f2f2Smatt  *
48*0062f2f2Smatt  * 2. the specified reset period is a negative integer:
49*0062f2f2Smatt  *    Timer 0 interrupt runs ra_wdog_timer0() at IPL_VM for service.
50*0062f2f2Smatt  *    If your system can make "forward progress" while spelding long times
51*0062f2f2Smatt  *    at IPL_VM, you should use this variant.
52*0062f2f2Smatt  *    The numbner is rectified
53*0062f2f2Smatt  *
54*0062f2f2Smatt  * The reset period is defined by RA_WDOG_DEFAULT_PERIOD
55*0062f2f2Smatt  * (which can be set via kernel config), or by period passed to
56*0062f2f2Smatt  * our 'smw_setmode' function.  The interrupt service interval
57*0062f2f2Smatt  * is half the reset interval.
58*0062f2f2Smatt  *
59*0062f2f2Smatt  */
60*0062f2f2Smatt 
61*0062f2f2Smatt #include "rwdog.h"
62*0062f2f2Smatt 
63*0062f2f2Smatt #include <sys/cdefs.h>
64*0062f2f2Smatt __KERNEL_RCSID(0, "$NetBSD: ralink_wdog.c,v 1.2 2011/07/28 15:38:49 matt Exp $");
65*0062f2f2Smatt 
66*0062f2f2Smatt #include <sys/param.h>
67*0062f2f2Smatt #include <sys/bus.h>
68*0062f2f2Smatt #include <sys/device.h>
69*0062f2f2Smatt #include <sys/systm.h>
70*0062f2f2Smatt #include <sys/wdog.h>
71*0062f2f2Smatt 
72*0062f2f2Smatt #include <mips/ralink/ralink_var.h>
73*0062f2f2Smatt #include <mips/ralink/ralink_reg.h>
74*0062f2f2Smatt 
75*0062f2f2Smatt #include <dev/sysmon/sysmonvar.h>
76*0062f2f2Smatt 
77*0062f2f2Smatt #if 0
78*0062f2f2Smatt # define DISABLE_WATCHDOG
79*0062f2f2Smatt #endif
80*0062f2f2Smatt 
81*0062f2f2Smatt #ifndef RA_WDOG_DEFAULT_MODE
82*0062f2f2Smatt # define RA_WDOG_DEFAULT_MODE	WDOG_MODE_KTICKLE
83*0062f2f2Smatt #endif
84*0062f2f2Smatt 
85*0062f2f2Smatt /*
86*0062f2f2Smatt  * PERIODs are in in seconds;
87*0062f2f2Smatt  * the counter is 16-bits;
88*0062f2f2Smatt  * maximum period depends on bus freq
89*0062f2f2Smatt  */
90*0062f2f2Smatt #ifndef RA_WDOG_DEFAULT_PERIOD
91*0062f2f2Smatt # define RA_WDOG_DEFAULT_PERIOD	10
92*0062f2f2Smatt #endif
93*0062f2f2Smatt #define WDOG_COUNT_MASK		0xffff
94*0062f2f2Smatt #define WDOG_MAX_COUNT		WDOG_COUNT_MASK
95*0062f2f2Smatt #define WDOG_MAX_PERIOD	\
96*0062f2f2Smatt 		(WDOG_MAX_COUNT / (RA_BUS_FREQ / WDOG_MAX_COUNT))
97*0062f2f2Smatt 
98*0062f2f2Smatt static int  ra_wdog_match(device_t, cfdata_t, void *);
99*0062f2f2Smatt static void ra_wdog_attach(device_t, device_t, void *);
100*0062f2f2Smatt static int  ra_wdog_tickle(struct sysmon_wdog *);
101*0062f2f2Smatt static int  ra_wdog_timer0(void *);
102*0062f2f2Smatt static int  ra_wdog_setmode(struct sysmon_wdog *);
103*0062f2f2Smatt 
104*0062f2f2Smatt extern int sysmon_wdog_setmode(struct sysmon_wdog *, int, u_int);
105*0062f2f2Smatt 
106*0062f2f2Smatt typedef struct ra_wdog_softc {
107*0062f2f2Smatt 	device_t		sc_dev;
108*0062f2f2Smatt 	struct sysmon_wdog 	sc_smw;
109*0062f2f2Smatt 	bus_space_tag_t		sc_memt;
110*0062f2f2Smatt 	bus_space_handle_t	sc_memh;
111*0062f2f2Smatt 	void			*sc_ih;
112*0062f2f2Smatt } ra_wdog_softc_t;
113*0062f2f2Smatt 
114*0062f2f2Smatt 
115*0062f2f2Smatt CFATTACH_DECL_NEW(rwdog, sizeof(struct ra_wdog_softc),
116*0062f2f2Smatt 	ra_wdog_match, ra_wdog_attach, NULL, NULL);
117*0062f2f2Smatt 
118*0062f2f2Smatt static const char *wdog_modestr[WDOG_MODE_MASK+1] = {
119*0062f2f2Smatt 	[ WDOG_MODE_DISARMED ] = "DISARMED",
120*0062f2f2Smatt 	[ WDOG_MODE_KTICKLE  ] = "KTICKLE",
121*0062f2f2Smatt 	[ WDOG_MODE_UTICKLE  ] = "UTICKLE",
122*0062f2f2Smatt 	[ WDOG_MODE_ETICKLE  ] = "ETICKLE"
123*0062f2f2Smatt };
124*0062f2f2Smatt 
125*0062f2f2Smatt static inline void
ra_wdog_reset(const ra_wdog_softc_t * sc)126*0062f2f2Smatt ra_wdog_reset(const ra_wdog_softc_t *sc)
127*0062f2f2Smatt {
128*0062f2f2Smatt 	uint32_t r;
129*0062f2f2Smatt 
130*0062f2f2Smatt 	r = bus_space_read_4(sc->sc_memt, sc->sc_memh, RA_TIMER_STAT);
131*0062f2f2Smatt 	r |= TIMER_1_RESET;
132*0062f2f2Smatt 	bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_STAT, r);
133*0062f2f2Smatt }
134*0062f2f2Smatt 
135*0062f2f2Smatt static inline u_int32_t
ra_wdog_sec_to_count(u_int nsec)136*0062f2f2Smatt ra_wdog_sec_to_count(u_int nsec)
137*0062f2f2Smatt {
138*0062f2f2Smatt 	KASSERT(nsec <= WDOG_MAX_PERIOD);
139*0062f2f2Smatt 	const u_int32_t count = (RA_BUS_FREQ / WDOG_MAX_COUNT) * nsec;
140*0062f2f2Smatt 	KASSERT(count <= WDOG_MAX_COUNT);
141*0062f2f2Smatt 	return count;
142*0062f2f2Smatt }
143*0062f2f2Smatt 
144*0062f2f2Smatt static int
ra_wdog_match(device_t parent,cfdata_t cf,void * aux)145*0062f2f2Smatt ra_wdog_match(device_t parent, cfdata_t cf, void *aux)
146*0062f2f2Smatt {
147*0062f2f2Smatt 	return 1;
148*0062f2f2Smatt }
149*0062f2f2Smatt 
150*0062f2f2Smatt static void
ra_wdog_attach(device_t parent,device_t self,void * aux)151*0062f2f2Smatt ra_wdog_attach(device_t parent, device_t self, void *aux)
152*0062f2f2Smatt {
153*0062f2f2Smatt 	ra_wdog_softc_t * const sc = device_private(self);
154*0062f2f2Smatt 	const struct mainbus_attach_args *ma = aux;
155*0062f2f2Smatt 	bus_space_handle_t memh;
156*0062f2f2Smatt 	int error;
157*0062f2f2Smatt 
158*0062f2f2Smatt 	aprint_naive(": Ralink watchdog controller\n");
159*0062f2f2Smatt 	aprint_normal(": Ralink watchdog controller\n");
160*0062f2f2Smatt 	aprint_normal_dev(self, "max period %d sec.\n", WDOG_MAX_PERIOD);
161*0062f2f2Smatt 
162*0062f2f2Smatt 	error = bus_space_map(ma->ma_memt, RA_TIMER_BASE, 0x100, 0, &memh);
163*0062f2f2Smatt 	if (error != 0) {
164*0062f2f2Smatt 		aprint_error_dev(self, "unable to map registers, "
165*0062f2f2Smatt 			"error=%d\n", error);
166*0062f2f2Smatt                 return;
167*0062f2f2Smatt 	}
168*0062f2f2Smatt 
169*0062f2f2Smatt 	sc->sc_memt = ma->ma_memt;
170*0062f2f2Smatt 	sc->sc_memh = memh;
171*0062f2f2Smatt 
172*0062f2f2Smatt 	sc->sc_smw.smw_name = device_xname(self);
173*0062f2f2Smatt 	sc->sc_smw.smw_cookie = sc;
174*0062f2f2Smatt 	sc->sc_smw.smw_setmode = ra_wdog_setmode;
175*0062f2f2Smatt 	sc->sc_smw.smw_tickle = ra_wdog_tickle;
176*0062f2f2Smatt 	sc->sc_smw.smw_period = RA_WDOG_DEFAULT_PERIOD;
177*0062f2f2Smatt 
178*0062f2f2Smatt 	error = sysmon_wdog_register(&sc->sc_smw);
179*0062f2f2Smatt 	if (error != 0)
180*0062f2f2Smatt 		aprint_error_dev(self, "unable to register with sysmon, "
181*0062f2f2Smatt 			"error %d\n", error);
182*0062f2f2Smatt 
183*0062f2f2Smatt 	sc->sc_ih = ra_intr_establish(RA_IRQ_TIMER0, ra_wdog_timer0, sc, 0);
184*0062f2f2Smatt 	if (sc->sc_ih == NULL)
185*0062f2f2Smatt 		aprint_error_dev(self, "unable to establish interrupt\n");
186*0062f2f2Smatt 			/* expect watchdog reset shortly */
187*0062f2f2Smatt 
188*0062f2f2Smatt 	if (RA_WDOG_DEFAULT_MODE == WDOG_MODE_DISARMED) {
189*0062f2f2Smatt 		/*
190*0062f2f2Smatt 		 * disarm the watchdog
191*0062f2f2Smatt 		 */
192*0062f2f2Smatt 		bus_space_write_4(sc->sc_memt, memh, RA_TIMER_0_CNTRL, 0);
193*0062f2f2Smatt 		bus_space_write_4(sc->sc_memt, memh, RA_TIMER_1_CNTRL, 0);
194*0062f2f2Smatt 		aprint_normal_dev(self, "%s mode\n",
195*0062f2f2Smatt 			wdog_modestr[sc->sc_smw.smw_mode]);
196*0062f2f2Smatt 	} else {
197*0062f2f2Smatt 		/*
198*0062f2f2Smatt 		 * initialize and arm the watchdog now.
199*0062f2f2Smatt 		 * if boot loader already initialized the watchdog
200*0062f2f2Smatt 		 * then we are re-initializing; this will buy some time
201*0062f2f2Smatt 		 * until interrupts are enabled, and will establish our
202*0062f2f2Smatt 		 * (default) mode and smw_period indedpendent of the
203*0062f2f2Smatt 		 * boot loader.
204*0062f2f2Smatt 		 */
205*0062f2f2Smatt 		error = sysmon_wdog_setmode(&sc->sc_smw, RA_WDOG_DEFAULT_MODE,
206*0062f2f2Smatt 			RA_WDOG_DEFAULT_PERIOD);
207*0062f2f2Smatt 		if (error != 0) {
208*0062f2f2Smatt 			aprint_error_dev(self, "unable to set sysmon wdog, "
209*0062f2f2Smatt 				"mode %d, error %d\n",
210*0062f2f2Smatt 				RA_WDOG_DEFAULT_MODE, error);
211*0062f2f2Smatt 		} else {
212*0062f2f2Smatt 			aprint_normal_dev(self, "%s mode, period %d sec.\n",
213*0062f2f2Smatt 				wdog_modestr[sc->sc_smw.smw_mode],
214*0062f2f2Smatt 				sc->sc_smw.smw_period);
215*0062f2f2Smatt 		}
216*0062f2f2Smatt 	}
217*0062f2f2Smatt }
218*0062f2f2Smatt 
219*0062f2f2Smatt /*
220*0062f2f2Smatt  * ra_wdog_tickle - smw watchdog service function
221*0062f2f2Smatt  */
222*0062f2f2Smatt static int
ra_wdog_tickle(struct sysmon_wdog * smw)223*0062f2f2Smatt ra_wdog_tickle(struct sysmon_wdog *smw)
224*0062f2f2Smatt {
225*0062f2f2Smatt 	const ra_wdog_softc_t * const sc = smw->smw_cookie;
226*0062f2f2Smatt 	ra_wdog_reset(sc);
227*0062f2f2Smatt 	return 0;
228*0062f2f2Smatt }
229*0062f2f2Smatt 
230*0062f2f2Smatt /*
231*0062f2f2Smatt  * ra_wdog_timer0 - periodic watchdog service ISR
232*0062f2f2Smatt  */
233*0062f2f2Smatt static int
ra_wdog_timer0(void * arg)234*0062f2f2Smatt ra_wdog_timer0(void *arg)
235*0062f2f2Smatt {
236*0062f2f2Smatt 	const ra_wdog_softc_t * const sc = arg;
237*0062f2f2Smatt 	ra_wdog_reset(sc);
238*0062f2f2Smatt 	return 0;
239*0062f2f2Smatt }
240*0062f2f2Smatt 
241*0062f2f2Smatt static int
ra_wdog_setmode(struct sysmon_wdog * smw)242*0062f2f2Smatt ra_wdog_setmode(struct sysmon_wdog *smw)
243*0062f2f2Smatt {
244*0062f2f2Smatt 	const ra_wdog_softc_t * const sc = smw->smw_cookie;
245*0062f2f2Smatt 	u_int period = smw->smw_period;
246*0062f2f2Smatt 	bool itickle = false;
247*0062f2f2Smatt 	uint32_t r;
248*0062f2f2Smatt 
249*0062f2f2Smatt 	if (((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE) &&
250*0062f2f2Smatt 	    ((int)period < 0)) {
251*0062f2f2Smatt 		itickle = true;		/* use Timer 0 */
252*0062f2f2Smatt 		period = -period;
253*0062f2f2Smatt 	}
254*0062f2f2Smatt 
255*0062f2f2Smatt 	/* all configuration has to be done with the timer disabled */
256*0062f2f2Smatt 	bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_0_CNTRL, 0);
257*0062f2f2Smatt 	bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_1_CNTRL, 0);
258*0062f2f2Smatt 
259*0062f2f2Smatt 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED)
260*0062f2f2Smatt 		return 0;
261*0062f2f2Smatt 
262*0062f2f2Smatt 	if (period > WDOG_MAX_PERIOD)
263*0062f2f2Smatt 		return EOPNOTSUPP;
264*0062f2f2Smatt 
265*0062f2f2Smatt 	/* Set the new watchdog reset period in Timer 1 */
266*0062f2f2Smatt 	r = ra_wdog_sec_to_count(period);
267*0062f2f2Smatt 	bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_1_LOAD, r);
268*0062f2f2Smatt 	bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_1_CNTRL,
269*0062f2f2Smatt 		TIMER_EN | TIMER_MODE(TIMER_MODE_WDOG) |
270*0062f2f2Smatt 			TIMER_PRESCALE(TIMER_PRESCALE_DIV_65536));
271*0062f2f2Smatt 
272*0062f2f2Smatt 	if (itickle) {
273*0062f2f2Smatt 		/* Set the new watchdog service period in Timer 0 */
274*0062f2f2Smatt 		r = ra_wdog_sec_to_count(period) / 2;
275*0062f2f2Smatt 		bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_0_LOAD, r);
276*0062f2f2Smatt 		bus_space_write_4(sc->sc_memt, sc->sc_memh, RA_TIMER_0_CNTRL,
277*0062f2f2Smatt 			TIMER_EN | TIMER_MODE(TIMER_MODE_PERIODIC) |
278*0062f2f2Smatt 				TIMER_PRESCALE(TIMER_PRESCALE_DIV_65536));
279*0062f2f2Smatt 	}
280*0062f2f2Smatt 
281*0062f2f2Smatt 	return 0;
282*0062f2f2Smatt }
283