1 /*- 2 * Copyright (c) 2014 Rui Paulo <rpaulo@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/conf.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/malloc.h> 36 #include <sys/rman.h> 37 #include <sys/event.h> 38 #include <sys/selinfo.h> 39 #include <sys/watchdog.h> 40 #include <machine/bus.h> 41 #include <machine/cpu.h> 42 #include <machine/frame.h> 43 #include <machine/intr.h> 44 45 #include <dev/fdt/fdt_common.h> 46 #include <dev/ofw/openfirm.h> 47 #include <dev/ofw/ofw_bus.h> 48 #include <dev/ofw/ofw_bus_subr.h> 49 50 #include <machine/bus.h> 51 #include <machine/fdt.h> 52 53 #include <arm/ti/ti_prcm.h> 54 #include <arm/ti/ti_wdt.h> 55 56 #ifdef DEBUG 57 #define DPRINTF(fmt, ...) do { \ 58 printf("%s: ", __func__); \ 59 printf(fmt, __VA_ARGS__); \ 60 } while (0) 61 #else 62 #define DPRINTF(fmt, ...) 63 #endif 64 65 static device_probe_t ti_wdt_probe; 66 static device_attach_t ti_wdt_attach; 67 static device_detach_t ti_wdt_detach; 68 static void ti_wdt_intr(void *); 69 static void ti_wdt_event(void *, unsigned int, int *); 70 71 struct ti_wdt_softc { 72 struct resource *sc_mem_res; 73 struct resource *sc_irq_res; 74 void *sc_intr; 75 bus_space_tag_t sc_bt; 76 bus_space_handle_t sc_bh; 77 eventhandler_tag sc_ev_tag; 78 }; 79 80 static device_method_t ti_wdt_methods[] = { 81 DEVMETHOD(device_probe, ti_wdt_probe), 82 DEVMETHOD(device_attach, ti_wdt_attach), 83 DEVMETHOD(device_detach, ti_wdt_detach), 84 85 DEVMETHOD_END 86 }; 87 88 static driver_t ti_wdt_driver = { 89 "ti_wdt", 90 ti_wdt_methods, 91 sizeof(struct ti_wdt_softc) 92 }; 93 94 static devclass_t ti_wdt_devclass; 95 96 DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0); 97 98 static volatile __inline uint32_t 99 ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg) 100 { 101 102 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 103 } 104 105 static __inline void 106 ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val) 107 { 108 109 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 110 } 111 112 /* 113 * Wait for the write to a specific synchronised register to complete. 114 */ 115 static __inline void 116 ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit) 117 { 118 119 while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit) 120 DELAY(10); 121 } 122 123 static __inline void 124 ti_wdt_disable(struct ti_wdt_softc *sc) 125 { 126 127 DPRINTF("disabling watchdog %p\n", sc); 128 ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA); 129 ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); 130 ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555); 131 ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); 132 } 133 134 static __inline void 135 ti_wdt_enable(struct ti_wdt_softc *sc) 136 { 137 138 DPRINTF("enabling watchdog %p\n", sc); 139 ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB); 140 ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); 141 ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444); 142 ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); 143 } 144 145 static int 146 ti_wdt_probe(device_t dev) 147 { 148 149 if (!ofw_bus_status_okay(dev)) 150 return (ENXIO); 151 if (ofw_bus_is_compatible(dev, "ti,omap3-wdt")) { 152 device_set_desc(dev, "TI Watchdog Timer"); 153 return (BUS_PROBE_DEFAULT); 154 } 155 156 return (ENXIO); 157 } 158 159 static int 160 ti_wdt_attach(device_t dev) 161 { 162 struct ti_wdt_softc *sc; 163 int rid; 164 165 sc = device_get_softc(dev); 166 rid = 0; 167 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 168 RF_ACTIVE); 169 if (sc->sc_mem_res == NULL) { 170 device_printf(dev, "could not allocate memory resource\n"); 171 return (ENXIO); 172 } 173 sc->sc_bt = rman_get_bustag(sc->sc_mem_res); 174 sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); 175 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 176 if (sc->sc_irq_res == NULL) { 177 device_printf(dev, "could not allocate interrupt resource\n"); 178 ti_wdt_detach(dev); 179 return (ENXIO); 180 } 181 if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 182 NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) { 183 device_printf(dev, 184 "unable to setup the interrupt handler\n"); 185 ti_wdt_detach(dev); 186 return (ENXIO); 187 } 188 /* Reset, enable interrupts and stop the watchdog. */ 189 ti_wdt_reg_write(sc, TI_WDT_WDSC, 190 ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR); 191 while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR) 192 DELAY(10); 193 ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY); 194 ti_wdt_disable(sc); 195 if (bootverbose) 196 device_printf(dev, "revision: 0x%x\n", 197 ti_wdt_reg_read(sc, TI_WDT_WIDR)); 198 sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc, 199 0); 200 201 return (0); 202 } 203 204 static int 205 ti_wdt_detach(device_t dev) 206 { 207 struct ti_wdt_softc *sc; 208 209 sc = device_get_softc(dev); 210 if (sc->sc_ev_tag) 211 EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag); 212 if (sc->sc_intr) 213 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); 214 if (sc->sc_irq_res) 215 bus_release_resource(dev, SYS_RES_IRQ, 216 rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); 217 if (sc->sc_mem_res) 218 bus_release_resource(dev, SYS_RES_MEMORY, 219 rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); 220 221 return (0); 222 } 223 224 static void 225 ti_wdt_intr(void *arg) 226 { 227 struct ti_wdt_softc *sc; 228 229 sc = arg; 230 DPRINTF("interrupt %p", sc); 231 ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY); 232 /* TODO: handle interrupt */ 233 } 234 235 static void 236 ti_wdt_event(void *arg, unsigned int cmd, int *error) 237 { 238 struct ti_wdt_softc *sc; 239 uint8_t s; 240 uint32_t wldr; 241 uint32_t ptv; 242 243 sc = arg; 244 ti_wdt_disable(sc); 245 if (cmd == WD_TO_NEVER) { 246 *error = 0; 247 return; 248 } 249 DPRINTF("cmd 0x%x\n", cmd); 250 cmd &= WD_INTERVAL; 251 if (cmd < WD_TO_1SEC) { 252 *error = EINVAL; 253 return; 254 } 255 s = 1 << (cmd - WD_TO_1SEC); 256 DPRINTF("seconds %u\n", s); 257 /* 258 * Leave the pre-scaler with its default values: 259 * PTV = 0 == 2**0 == 1 260 * PRE = 1 (enabled) 261 * 262 * Compute the load register value assuming a 32kHz clock. 263 * See OVF_Rate in the WDT section of the AM335x TRM. 264 */ 265 ptv = 0; 266 wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1; 267 DPRINTF("wldr 0x%x\n", wldr); 268 ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr); 269 /* 270 * Trigger a timer reload. 271 */ 272 ti_wdt_reg_write(sc, TI_WDT_WTGR, 273 ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1); 274 ti_wdt_reg_wait(sc, TI_W_PEND_WTGR); 275 ti_wdt_enable(sc); 276 *error = 0; 277 } 278