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