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