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