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