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