geodewdg.c revision 1.8 1 /* $NetBSD: geodewdg.c,v 1.8 2008/04/28 20:23:25 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2005 David Young. All rights reserved.
5 *
6 * This code was written by David Young.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by David Young.
19 * 4. The name of David Young may not be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
27 * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34 * OF SUCH DAMAGE.
35 */
36 /*-
37 * Copyright (c) 2002 The NetBSD Foundation, Inc.
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to The NetBSD Foundation
41 * by Jason R. Thorpe.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
53 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
56 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62 * POSSIBILITY OF SUCH DAMAGE.
63 */
64
65 /*
66 * Device driver for the watchdog timer built into the
67 * AMD Geode SC1100 processor.
68 */
69
70 #include <sys/cdefs.h>
71
72 __KERNEL_RCSID(0, "$NetBSD: geodewdg.c,v 1.8 2008/04/28 20:23:25 martin Exp $");
73
74 #include <sys/param.h>
75 #include <sys/systm.h>
76 #include <sys/device.h>
77 #include <sys/wdog.h>
78 #include <uvm/uvm_extern.h>
79 #include <machine/bus.h>
80 #include <dev/pci/pcivar.h>
81 #include <dev/pci/pcidevs.h>
82 #include <arch/i386/pci/geodevar.h>
83 #include <arch/i386/pci/geodereg.h>
84 #include <dev/sysmon/sysmonvar.h>
85
86 #ifdef GEODE_DEBUG
87 #define GEODE_DPRINTF(__x) printf __x
88 #else /* GEODE_DEBUG */
89 #define GEODE_DPRINTF(__x) /* nothing */
90 #endif
91
92 struct geode_wdog_softc {
93 struct device sc_dev;
94 struct geode_gcb_softc *sc_gcb_dev;
95
96 uint16_t sc_countdown;
97 uint8_t sc_prescale;
98 struct sysmon_wdog sc_smw;
99 };
100
101 static int attached = 0;
102
103 static void
104 geode_wdog_disable(struct geode_wdog_softc *sc)
105 {
106 uint16_t wdcnfg;
107
108 /* cancel any pending countdown */
109 sc->sc_countdown = 0;
110 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
111 SC1100_GCB_WDTO, 0);
112 /* power-down clock */
113 wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
114 sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
115
116 GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
117
118 wdcnfg |= SC1100_WDCNFG_WD32KPD;
119 wdcnfg &= ~(SC1100_WDCNFG_WDTYPE2_MASK | SC1100_WDCNFG_WDTYPE1_MASK);
120 /* This no-op is for the reader's benefit. */
121 wdcnfg |= SC1100_WDCNFG_WDTYPE1_NOACTION |
122 SC1100_WDCNFG_WDTYPE2_NOACTION;
123 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
124 SC1100_GCB_WDCNFG, wdcnfg);
125
126 GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
127 }
128
129 static void
130 geode_wdog_enable(struct geode_wdog_softc *sc)
131 {
132 uint16_t wdcnfg;
133
134 /* power-up clock and set prescale */
135 wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
136 sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
137
138 GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
139
140 wdcnfg &= ~(SC1100_WDCNFG_WD32KPD | SC1100_WDCNFG_WDPRES_MASK |
141 SC1100_WDCNFG_WDTYPE1_MASK | SC1100_WDCNFG_WDTYPE2_MASK);
142 wdcnfg |= __SHIFTIN(sc->sc_prescale, SC1100_WDCNFG_WDPRES_MASK);
143 wdcnfg |= SC1100_WDCNFG_WDTYPE1_RESET | SC1100_WDCNFG_WDTYPE2_NOACTION;
144
145 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
146 SC1100_GCB_WDCNFG, wdcnfg);
147
148 GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
149 }
150
151 static void
152 geode_wdog_reset(struct geode_wdog_softc *sc)
153 {
154 /* set countdown */
155 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
156 SC1100_GCB_WDTO, sc->sc_countdown);
157 }
158
159 static int
160 geode_wdog_tickle(struct sysmon_wdog *smw)
161 {
162 int s;
163 struct geode_wdog_softc *sc = smw->smw_cookie;
164
165 s = splhigh();
166 geode_wdog_reset(sc);
167 splx(s);
168 return 0;
169 }
170
171 static int
172 geode_wdog_setmode(struct sysmon_wdog *smw)
173 {
174 struct geode_wdog_softc *sc = smw->smw_cookie;
175 uint32_t ticks;
176 int prescale, s;
177
178 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
179 s = splhigh();
180 geode_wdog_disable(sc);
181 splx(s);
182 return 0;
183 }
184 if (smw->smw_period == WDOG_PERIOD_DEFAULT)
185 smw->smw_period = 32;
186 else if (smw->smw_period > SC1100_WDIVL_MAX) /* too big? */
187 return EINVAL;
188
189 GEODE_DPRINTF(("%s: period %u\n", __func__, smw->smw_period));
190
191 ticks = smw->smw_period * SC1100_WDCLK_HZ;
192
193 GEODE_DPRINTF(("%s: ticks0 %" PRIu32 "\n", __func__, ticks));
194
195 for (prescale = 0; ticks > UINT16_MAX; prescale++)
196 ticks /= 2;
197
198 GEODE_DPRINTF(("%s: ticks %" PRIu32 "\n", __func__, ticks));
199 GEODE_DPRINTF(("%s: prescale %d\n", __func__, prescale));
200
201 KASSERT(prescale <= SC1100_WDCNFG_WDPRES_MAX);
202 KASSERT(ticks <= UINT16_MAX);
203
204 s = splhigh();
205
206 sc->sc_prescale = (uint8_t)prescale;
207 sc->sc_countdown = (uint16_t)ticks;
208
209 geode_wdog_enable(sc);
210
211 geode_wdog_reset(sc);
212
213 splx(s);
214 return 0;
215 }
216
217 static int
218 geode_wdog_match(device_t parent, struct cfdata *match, void *aux)
219 {
220 return !attached;
221 }
222
223 static void
224 geode_wdog_attach(device_t parent, device_t self, void *aux)
225 {
226 struct geode_wdog_softc *sc = device_private(self);
227 uint8_t wdsts;
228
229 aprint_naive(": Watchdog Timer\n");
230 aprint_normal(": AMD Geode SC1100 Watchdog Timer\n");
231
232
233 /*
234 * Hook up the watchdog timer.
235 */
236 sc->sc_gcb_dev = device_private(parent);
237 sc->sc_smw.smw_name = device_xname(self);
238 sc->sc_smw.smw_cookie = sc;
239 sc->sc_smw.smw_setmode = geode_wdog_setmode;
240 sc->sc_smw.smw_tickle = geode_wdog_tickle;
241 sc->sc_smw.smw_period = 32;
242
243 /*
244 * Determine cause of the last reset, and issue a warning if it
245 * was due to watchdog expiry.
246 */
247 wdsts = bus_space_read_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
248 SC1100_GCB_WDSTS);
249
250 GEODE_DPRINTF(("%s: status %#02" PRIx8 "\n", device_xname(self),
251 wdsts));
252
253 if (wdsts & SC1100_WDSTS_WDRST)
254 aprint_error(
255 "%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
256 device_xname(self));
257
258 /* reset WDOVF by writing 1 to it */
259 bus_space_write_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
260 SC1100_GCB_WDSTS, wdsts & SC1100_WDSTS_WDOVF);
261
262 if (sysmon_wdog_register(&sc->sc_smw) != 0)
263 aprint_error("%s: unable to register watchdog with sysmon\n",
264 device_xname(self));
265
266 /* cancel any pending countdown */
267 geode_wdog_disable(sc);
268
269 attached = 1;
270 }
271
272 static int
273 geode_wdog_detach(device_t self, int flags)
274 {
275 int rc;
276 struct geode_wdog_softc *sc = device_private(self);
277
278 if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) {
279 if (rc == ERESTART)
280 rc = EINTR;
281 return rc;
282 }
283
284 /* cancel any pending countdown */
285 geode_wdog_disable(sc);
286
287 attached = 0;
288
289 return 0;
290 }
291
292 CFATTACH_DECL(geodewdog, sizeof(struct geode_wdog_softc),
293 geode_wdog_match, geode_wdog_attach, geode_wdog_detach, NULL);
294