1 /* $NetBSD: geodewdg.c,v 1.9 2008/05/05 11:49:40 xtraeme 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.9 2008/05/05 11:49:40 xtraeme 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 geode_gcb_softc *sc_gcb_dev; 94 95 uint16_t sc_countdown; 96 uint8_t sc_prescale; 97 struct sysmon_wdog sc_smw; 98 }; 99 100 static int attached = 0; 101 102 static void 103 geode_wdog_disable(struct geode_wdog_softc *sc) 104 { 105 uint16_t wdcnfg; 106 107 /* cancel any pending countdown */ 108 sc->sc_countdown = 0; 109 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 110 SC1100_GCB_WDTO, 0); 111 /* power-down clock */ 112 wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot, 113 sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG); 114 115 GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg)); 116 117 wdcnfg |= SC1100_WDCNFG_WD32KPD; 118 wdcnfg &= ~(SC1100_WDCNFG_WDTYPE2_MASK | SC1100_WDCNFG_WDTYPE1_MASK); 119 /* This no-op is for the reader's benefit. */ 120 wdcnfg |= SC1100_WDCNFG_WDTYPE1_NOACTION | 121 SC1100_WDCNFG_WDTYPE2_NOACTION; 122 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 123 SC1100_GCB_WDCNFG, wdcnfg); 124 125 GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg)); 126 } 127 128 static void 129 geode_wdog_enable(struct geode_wdog_softc *sc) 130 { 131 uint16_t wdcnfg; 132 133 /* power-up clock and set prescale */ 134 wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot, 135 sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG); 136 137 GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg)); 138 139 wdcnfg &= ~(SC1100_WDCNFG_WD32KPD | SC1100_WDCNFG_WDPRES_MASK | 140 SC1100_WDCNFG_WDTYPE1_MASK | SC1100_WDCNFG_WDTYPE2_MASK); 141 wdcnfg |= __SHIFTIN(sc->sc_prescale, SC1100_WDCNFG_WDPRES_MASK); 142 wdcnfg |= SC1100_WDCNFG_WDTYPE1_RESET | SC1100_WDCNFG_WDTYPE2_NOACTION; 143 144 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 145 SC1100_GCB_WDCNFG, wdcnfg); 146 147 GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg)); 148 } 149 150 static void 151 geode_wdog_reset(struct geode_wdog_softc *sc) 152 { 153 /* set countdown */ 154 bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 155 SC1100_GCB_WDTO, sc->sc_countdown); 156 } 157 158 static int 159 geode_wdog_tickle(struct sysmon_wdog *smw) 160 { 161 int s; 162 struct geode_wdog_softc *sc = smw->smw_cookie; 163 164 s = splhigh(); 165 geode_wdog_reset(sc); 166 splx(s); 167 return 0; 168 } 169 170 static int 171 geode_wdog_setmode(struct sysmon_wdog *smw) 172 { 173 struct geode_wdog_softc *sc = smw->smw_cookie; 174 uint32_t ticks; 175 int prescale, s; 176 177 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 178 s = splhigh(); 179 geode_wdog_disable(sc); 180 splx(s); 181 return 0; 182 } 183 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 184 smw->smw_period = 32; 185 else if (smw->smw_period > SC1100_WDIVL_MAX) /* too big? */ 186 return EINVAL; 187 188 GEODE_DPRINTF(("%s: period %u\n", __func__, smw->smw_period)); 189 190 ticks = smw->smw_period * SC1100_WDCLK_HZ; 191 192 GEODE_DPRINTF(("%s: ticks0 %" PRIu32 "\n", __func__, ticks)); 193 194 for (prescale = 0; ticks > UINT16_MAX; prescale++) 195 ticks /= 2; 196 197 GEODE_DPRINTF(("%s: ticks %" PRIu32 "\n", __func__, ticks)); 198 GEODE_DPRINTF(("%s: prescale %d\n", __func__, prescale)); 199 200 KASSERT(prescale <= SC1100_WDCNFG_WDPRES_MAX); 201 KASSERT(ticks <= UINT16_MAX); 202 203 s = splhigh(); 204 205 sc->sc_prescale = (uint8_t)prescale; 206 sc->sc_countdown = (uint16_t)ticks; 207 208 geode_wdog_enable(sc); 209 210 geode_wdog_reset(sc); 211 212 splx(s); 213 return 0; 214 } 215 216 static int 217 geode_wdog_match(device_t parent, cfdata_t match, void *aux) 218 { 219 return !attached; 220 } 221 222 static void 223 geode_wdog_attach(device_t parent, device_t self, void *aux) 224 { 225 struct geode_wdog_softc *sc = device_private(self); 226 uint8_t wdsts; 227 228 aprint_naive(": Watchdog Timer\n"); 229 aprint_normal(": AMD Geode SC1100 Watchdog Timer\n"); 230 231 232 /* 233 * Hook up the watchdog timer. 234 */ 235 sc->sc_gcb_dev = device_private(parent); 236 sc->sc_smw.smw_name = device_xname(self); 237 sc->sc_smw.smw_cookie = sc; 238 sc->sc_smw.smw_setmode = geode_wdog_setmode; 239 sc->sc_smw.smw_tickle = geode_wdog_tickle; 240 sc->sc_smw.smw_period = 32; 241 242 /* 243 * Determine cause of the last reset, and issue a warning if it 244 * was due to watchdog expiry. 245 */ 246 wdsts = bus_space_read_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 247 SC1100_GCB_WDSTS); 248 249 GEODE_DPRINTF(("%s: status %#02" PRIx8 "\n", device_xname(self), 250 wdsts)); 251 252 if (wdsts & SC1100_WDSTS_WDRST) 253 aprint_error( 254 "%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n", 255 device_xname(self)); 256 257 /* reset WDOVF by writing 1 to it */ 258 bus_space_write_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh, 259 SC1100_GCB_WDSTS, wdsts & SC1100_WDSTS_WDOVF); 260 261 if (sysmon_wdog_register(&sc->sc_smw) != 0) 262 aprint_error("%s: unable to register watchdog with sysmon\n", 263 device_xname(self)); 264 265 /* cancel any pending countdown */ 266 geode_wdog_disable(sc); 267 268 attached = 1; 269 } 270 271 static int 272 geode_wdog_detach(device_t self, int flags) 273 { 274 int rc; 275 struct geode_wdog_softc *sc = device_private(self); 276 277 if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) { 278 if (rc == ERESTART) 279 rc = EINTR; 280 return rc; 281 } 282 283 /* cancel any pending countdown */ 284 geode_wdog_disable(sc); 285 286 attached = 0; 287 288 return 0; 289 } 290 291 CFATTACH_DECL_NEW(geodewdog, sizeof(struct geode_wdog_softc), 292 geode_wdog_match, geode_wdog_attach, geode_wdog_detach, NULL); 293