]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/arm/at91/at91_wdt.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / arm / at91 / at91_wdt.c
1 /*-
2  * Copyright (c) 2010 Greg Ansley.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 /*
27  * The sam9 watchdog hardware can be programed only once. So we set the hardware
28  * watchdog to 16s in wdt_attach and only reset it in the wdt_tick
29  * handler. The watchdog is halted in processor debug mode.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/kdb.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/systm.h>
41 #include <sys/watchdog.h>
42
43 #include <machine/bus.h>
44
45 #include <arm/at91/at91var.h>
46 #include <arm/at91/at91_wdtreg.h>
47
48 struct wdt_softc {
49         struct mtx      sc_mtx;
50         device_t        sc_dev;
51         struct resource *mem_res;
52         struct callout  tick_ch;
53         eventhandler_tag sc_wet;
54         void            *intrhand;
55         u_int           cmd;
56         u_int           interval;
57 };
58
59 static inline uint32_t
60 RD4(struct wdt_softc *sc, bus_size_t off)
61 {
62         return (bus_read_4(sc->mem_res, off));
63 }
64
65 static inline void
66 WR4(struct wdt_softc *sc, bus_size_t off, uint32_t val)
67 {
68         bus_write_4(sc->mem_res, off, val);
69 }
70
71 static int
72 wdt_intr(void *argp)
73 {
74         struct wdt_softc *sc = argp;
75
76
77         if (RD4(sc, WDT_SR) & (WDT_WDUNF | WDT_WDERR)) {
78 #if defined(KDB) && !defined(KDB_UNATTENDED)
79                 kdb_backtrace();
80                 kdb_enter(KDB_WHY_WATCHDOG, "watchdog timeout");
81 #else
82                 panic("watchdog timeout");
83 #endif
84         }
85         return (FILTER_STRAY);
86 }
87
88 /* User interface, see watchdog(9) */
89 static void
90 wdt_watchdog(void *argp, u_int cmd, int *error)
91 {
92         struct wdt_softc *sc = argp;
93         u_int interval;
94
95         mtx_lock(&sc->sc_mtx);
96
97         *error = 0;
98         sc->cmd = 0;
99         interval = cmd & WD_INTERVAL;
100         if (interval > WD_TO_16SEC)
101                 *error = EOPNOTSUPP;
102         else if (interval > 0)
103                 sc->cmd = interval | WD_ACTIVE;
104
105         /* We cannot turn off our watchdog so if user
106          * fails to turn us on go to passive mode. */
107         if ((sc->cmd & WD_ACTIVE) == 0)
108                 sc->cmd = WD_PASSIVE;
109
110         mtx_unlock(&sc->sc_mtx);
111 }
112
113 /* This routine is called no matter what state the user sets the
114  * watchdog mode to. Called at a rate that is slightly less than
115  * half the hardware timeout. */
116 static void
117 wdt_tick(void *argp)
118 {
119         struct wdt_softc *sc = argp;
120
121         mtx_assert(&sc->sc_mtx, MA_OWNED);
122         if (sc->cmd & (WD_ACTIVE | WD_PASSIVE))
123                 WR4(sc, WDT_CR, WDT_KEY|WDT_WDRSTT);
124
125         sc->cmd &= WD_PASSIVE;
126         callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc);
127 }
128
129 static int
130 wdt_probe(device_t dev)
131 {
132
133         if (at91_is_sam9()) {
134                 device_set_desc(dev, "WDT");
135                 return (0);
136         }
137         return (ENXIO);
138 }
139
140 static int
141 wdt_attach(device_t dev)
142 {
143         static struct wdt_softc *sc;
144         struct resource *irq;
145         uint32_t wdt_mr;
146         int rid, err;
147
148         sc = device_get_softc(dev);
149         sc->cmd = WD_PASSIVE;
150         sc->sc_dev = dev;
151
152         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "at91_wdt", MTX_DEF);
153         callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0);
154
155         rid = 0;
156         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
157             RF_ACTIVE);
158
159         if (sc->mem_res == NULL)
160                panic("couldn't allocate wdt register resources");
161
162         wdt_mr = RD4(sc, WDT_MR);
163         if ((wdt_mr & WDT_WDRSTEN) == 0)
164                 device_printf(dev, "Watchdog disabled! (Boot ROM?)\n");
165         else {
166 #ifdef WDT_RESET
167                 /* Rude, full reset of whole system on watch dog timeout */
168                 WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)|
169                     WDT_WDRSTEN| WDT_WDV(0xFFF));
170 #else
171                 /* Generate stack trace and panic on watchdog timeout*/
172                 WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)|
173                     WDT_WDFIEN| WDT_WDV(0xFFF));
174 #endif
175                 /* This may have been set by Boot ROM so register value
176                  * may not be  what we just requested since this is a
177                  * write once register. */
178                 wdt_mr = RD4(sc, WDT_MR);
179                 if (wdt_mr & WDT_WDFIEN) {
180                         rid = 0;
181                         irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
182                             RF_ACTIVE | RF_SHAREABLE);
183                         if (!irq)
184                                 panic("could not allocate interrupt.\n");
185
186                         err = bus_setup_intr(dev, irq, INTR_TYPE_CLK, wdt_intr,
187                                 NULL, sc, &sc->intrhand);
188                 }
189
190                 /* interval * hz */
191                 sc->interval = (((wdt_mr & WDT_WDV(~0)) + 1) * WDT_DIV) /
192                         (WDT_CLOCK/hz);
193
194                 device_printf(dev, "watchdog timeout: %d seconds\n",
195                      sc->interval/hz);
196
197                 /* Slightly less than 1/2 of watchdog hardware timeout */
198                 sc->interval = (sc->interval/2) - (sc->interval/20);
199                 callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc);
200
201                 /* Register us as a watchdog */
202                 sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list,
203                     wdt_watchdog, sc, 0);
204         }
205         return (0);
206 }
207
208 static device_method_t wdt_methods[] = {
209         DEVMETHOD(device_probe, wdt_probe),
210         DEVMETHOD(device_attach, wdt_attach),
211         {0,0},
212 };
213
214 static driver_t wdt_driver = {
215         "at91_wdt",
216         wdt_methods,
217         sizeof(struct wdt_softc),
218 };
219
220 static devclass_t wdt_devclass;
221
222 DRIVER_MODULE(at91_wdt, atmelarm, wdt_driver, wdt_devclass, 0, 0);