1/*- 2 * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * 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/watchdog.h> 32#include <sys/bus.h> 33#include <sys/kernel.h> 34#include <sys/lock.h> 35#include <sys/module.h> 36#include <sys/mutex.h> 37#include <sys/rman.h> 38 39#include <dev/fdt/fdt_common.h> 40#include <dev/ofw/openfirm.h> 41#include <dev/ofw/ofw_bus.h> 42#include <dev/ofw/ofw_bus_subr.h> 43 44#include <machine/bus.h> 45#include <machine/cpufunc.h> 46#include <machine/machdep.h> 47#include <machine/fdt.h> 48 49#include <arm/allwinner/a10_wdog.h> 50 51#define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 52#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 53 54#define WDOG_CTRL 0x00 55#define WDOG_CTRL_RESTART (1 << 0) 56#define WDOG_MODE 0x04 57#define WDOG_MODE_INTVL_SHIFT 3 58#define WDOG_MODE_RST_EN (1 << 1) 59#define WDOG_MODE_EN (1 << 0) 60 61struct a10wd_interval { 62 uint64_t milliseconds; 63 unsigned int value; 64}; 65 66struct a10wd_interval wd_intervals[] = { 67 { 500, 0 }, 68 { 1000, 1 }, 69 { 2000, 2 }, 70 { 3000, 3 }, 71 { 4000, 4 }, 72 { 5000, 5 }, 73 { 6000, 6 }, 74 { 8000, 7 }, 75 { 10000, 8 }, 76 { 12000, 9 }, 77 { 14000, 10 }, 78 { 16000, 11 }, 79 { 0, 0 } /* sentinel */ 80}; 81 82static struct a10wd_softc *a10wd_sc = NULL; 83 84struct a10wd_softc { 85 device_t dev; 86 struct resource * res; 87 struct mtx mtx; 88}; 89 90static void a10wd_watchdog_fn(void *private, u_int cmd, int *error); 91 92static int 93a10wd_probe(device_t dev) 94{ 95 96 if (!ofw_bus_status_okay(dev)) 97 return (ENXIO); 98 99 if (ofw_bus_is_compatible(dev, "allwinner,sun4i-wdt")) { 100 device_set_desc(dev, "Allwinner A10 Watchdog"); 101 return (BUS_PROBE_DEFAULT); 102 } 103 104 return (ENXIO); 105} 106 107static int 108a10wd_attach(device_t dev) 109{ 110 struct a10wd_softc *sc; 111 int rid; 112 113 if (a10wd_sc != NULL) 114 return (ENXIO); 115 116 sc = device_get_softc(dev); 117 sc->dev = dev; 118 119 rid = 0; 120 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 121 if (sc->res == NULL) { 122 device_printf(dev, "could not allocate memory resource\n"); 123 return (ENXIO); 124 } 125 126 a10wd_sc = sc; 127 mtx_init(&sc->mtx, "A10 Watchdog", "a10wd", MTX_DEF); 128 EVENTHANDLER_REGISTER(watchdog_list, a10wd_watchdog_fn, sc, 0); 129 130 return (0); 131} 132 133static void 134a10wd_watchdog_fn(void *private, u_int cmd, int *error) 135{ 136 struct a10wd_softc *sc; 137 uint64_t ms; 138 int i; 139 140 sc = private; 141 mtx_lock(&sc->mtx); 142 143 cmd &= WD_INTERVAL; 144 145 if (cmd > 0) { 146 ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; 147 i = 0; 148 while (wd_intervals[i].milliseconds && 149 (ms > wd_intervals[i].milliseconds)) 150 i++; 151 if (wd_intervals[i].milliseconds) { 152 WRITE(sc, WDOG_MODE, 153 (wd_intervals[i].value << WDOG_MODE_INTVL_SHIFT) | 154 WDOG_MODE_EN | WDOG_MODE_RST_EN); 155 WRITE(sc, WDOG_CTRL, WDOG_CTRL_RESTART); 156 *error = 0; 157 } 158 else { 159 /* 160 * Can't arm 161 * disable watchdog as watchdog(9) requires 162 */ 163 device_printf(sc->dev, 164 "Can't arm, timeout is more than 16 sec\n"); 165 mtx_unlock(&sc->mtx); 166 WRITE(sc, WDOG_MODE, 0); 167 return; 168 } 169 } 170 else 171 WRITE(sc, WDOG_MODE, 0); 172 173 mtx_unlock(&sc->mtx); 174} 175 176void 177a10wd_watchdog_reset() 178{ 179 180 if (a10wd_sc == NULL) { 181 printf("Reset: watchdog device has not been initialized\n"); 182 return; 183 } 184 185 WRITE(a10wd_sc, WDOG_MODE, 186 (wd_intervals[0].value << WDOG_MODE_INTVL_SHIFT) | 187 WDOG_MODE_EN | WDOG_MODE_RST_EN); 188 189 while(1) 190 ; 191 192} 193 194static device_method_t a10wd_methods[] = { 195 DEVMETHOD(device_probe, a10wd_probe), 196 DEVMETHOD(device_attach, a10wd_attach), 197 198 DEVMETHOD_END 199}; 200 201static driver_t a10wd_driver = { 202 "a10wd", 203 a10wd_methods, 204 sizeof(struct a10wd_softc), 205}; 206static devclass_t a10wd_devclass; 207 208DRIVER_MODULE(a10wd, simplebus, a10wd_driver, a10wd_devclass, 0, 0); 209