1246660Sgonzo/*- 2246660Sgonzo * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org> 3246660Sgonzo * All rights reserved. 4246660Sgonzo * 5246660Sgonzo * Redistribution and use in source and binary forms, with or without 6246660Sgonzo * modification, are permitted provided that the following conditions 7246660Sgonzo * are met: 8246660Sgonzo * 1. Redistributions of source code must retain the above copyright 9246660Sgonzo * notice, this list of conditions and the following disclaimer. 10246660Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11246660Sgonzo * notice, this list of conditions and the following disclaimer in the 12246660Sgonzo * documentation and/or other materials provided with the distribution. 13246660Sgonzo * 14246660Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15246660Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16246660Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17246660Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18246660Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19246660Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20246660Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21246660Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22246660Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23246660Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24246660Sgonzo * SUCH DAMAGE. 25246660Sgonzo */ 26246660Sgonzo#include <sys/cdefs.h> 27246660Sgonzo__FBSDID("$FreeBSD$"); 28246660Sgonzo 29246660Sgonzo#include <sys/param.h> 30246660Sgonzo#include <sys/systm.h> 31246660Sgonzo#include <sys/watchdog.h> 32246660Sgonzo#include <sys/bus.h> 33246660Sgonzo#include <sys/kernel.h> 34246660Sgonzo#include <sys/lock.h> 35246660Sgonzo#include <sys/module.h> 36246660Sgonzo#include <sys/mutex.h> 37246660Sgonzo#include <sys/rman.h> 38246660Sgonzo 39246660Sgonzo#include <dev/fdt/fdt_common.h> 40246660Sgonzo#include <dev/ofw/openfirm.h> 41246660Sgonzo#include <dev/ofw/ofw_bus.h> 42246660Sgonzo#include <dev/ofw/ofw_bus_subr.h> 43246660Sgonzo 44246660Sgonzo#include <machine/bus.h> 45246660Sgonzo#include <machine/cpufunc.h> 46246660Sgonzo#include <machine/machdep.h> 47246660Sgonzo#include <machine/fdt.h> 48246660Sgonzo 49246660Sgonzo#include <arm/allwinner/a10_wdog.h> 50246660Sgonzo 51246660Sgonzo#define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 52246660Sgonzo#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 53246660Sgonzo 54246660Sgonzo#define WDOG_CTRL 0x00 55246660Sgonzo#define WDOG_CTRL_RESTART (1 << 0) 56246660Sgonzo#define WDOG_MODE 0x04 57246660Sgonzo#define WDOG_MODE_INTVL_SHIFT 3 58246660Sgonzo#define WDOG_MODE_RST_EN (1 << 1) 59246660Sgonzo#define WDOG_MODE_EN (1 << 0) 60246660Sgonzo 61246660Sgonzostruct a10wd_interval { 62246660Sgonzo uint64_t milliseconds; 63246660Sgonzo unsigned int value; 64246660Sgonzo}; 65246660Sgonzo 66246660Sgonzostruct a10wd_interval wd_intervals[] = { 67246660Sgonzo { 500, 0 }, 68246660Sgonzo { 1000, 1 }, 69246660Sgonzo { 2000, 2 }, 70246660Sgonzo { 3000, 3 }, 71246660Sgonzo { 4000, 4 }, 72246660Sgonzo { 5000, 5 }, 73246660Sgonzo { 6000, 6 }, 74246660Sgonzo { 8000, 7 }, 75246660Sgonzo { 10000, 8 }, 76246660Sgonzo { 12000, 9 }, 77246660Sgonzo { 14000, 10 }, 78246660Sgonzo { 16000, 11 }, 79246660Sgonzo { 0, 0 } /* sentinel */ 80246660Sgonzo}; 81246660Sgonzo 82246660Sgonzostatic struct a10wd_softc *a10wd_sc = NULL; 83246660Sgonzo 84246660Sgonzostruct a10wd_softc { 85246660Sgonzo device_t dev; 86246660Sgonzo struct resource * res; 87246660Sgonzo struct mtx mtx; 88246660Sgonzo}; 89246660Sgonzo 90246660Sgonzostatic void a10wd_watchdog_fn(void *private, u_int cmd, int *error); 91246660Sgonzo 92246660Sgonzostatic int 93246660Sgonzoa10wd_probe(device_t dev) 94246660Sgonzo{ 95246660Sgonzo 96266152Sian if (!ofw_bus_status_okay(dev)) 97266152Sian return (ENXIO); 98266152Sian 99246660Sgonzo if (ofw_bus_is_compatible(dev, "allwinner,sun4i-wdt")) { 100246660Sgonzo device_set_desc(dev, "Allwinner A10 Watchdog"); 101246660Sgonzo return (BUS_PROBE_DEFAULT); 102246660Sgonzo } 103246660Sgonzo 104246660Sgonzo return (ENXIO); 105246660Sgonzo} 106246660Sgonzo 107246660Sgonzostatic int 108246660Sgonzoa10wd_attach(device_t dev) 109246660Sgonzo{ 110246660Sgonzo struct a10wd_softc *sc; 111246660Sgonzo int rid; 112246660Sgonzo 113246660Sgonzo if (a10wd_sc != NULL) 114246660Sgonzo return (ENXIO); 115246660Sgonzo 116246660Sgonzo sc = device_get_softc(dev); 117246660Sgonzo sc->dev = dev; 118246660Sgonzo 119246660Sgonzo rid = 0; 120246660Sgonzo sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 121246660Sgonzo if (sc->res == NULL) { 122246660Sgonzo device_printf(dev, "could not allocate memory resource\n"); 123246660Sgonzo return (ENXIO); 124246660Sgonzo } 125246660Sgonzo 126246660Sgonzo a10wd_sc = sc; 127246660Sgonzo mtx_init(&sc->mtx, "A10 Watchdog", "a10wd", MTX_DEF); 128246660Sgonzo EVENTHANDLER_REGISTER(watchdog_list, a10wd_watchdog_fn, sc, 0); 129246660Sgonzo 130246660Sgonzo return (0); 131246660Sgonzo} 132246660Sgonzo 133246660Sgonzostatic void 134246660Sgonzoa10wd_watchdog_fn(void *private, u_int cmd, int *error) 135246660Sgonzo{ 136246660Sgonzo struct a10wd_softc *sc; 137246660Sgonzo uint64_t ms; 138246660Sgonzo int i; 139246660Sgonzo 140246660Sgonzo sc = private; 141246660Sgonzo mtx_lock(&sc->mtx); 142246660Sgonzo 143246660Sgonzo cmd &= WD_INTERVAL; 144246660Sgonzo 145246660Sgonzo if (cmd > 0) { 146246660Sgonzo ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; 147246660Sgonzo i = 0; 148246660Sgonzo while (wd_intervals[i].milliseconds && 149246660Sgonzo (ms > wd_intervals[i].milliseconds)) 150246660Sgonzo i++; 151246660Sgonzo if (wd_intervals[i].milliseconds) { 152246660Sgonzo WRITE(sc, WDOG_MODE, 153246660Sgonzo (wd_intervals[i].value << WDOG_MODE_INTVL_SHIFT) | 154246660Sgonzo WDOG_MODE_EN | WDOG_MODE_RST_EN); 155246660Sgonzo WRITE(sc, WDOG_CTRL, WDOG_CTRL_RESTART); 156266405Sian *error = 0; 157246660Sgonzo } 158266405Sian else { 159266405Sian /* 160266405Sian * Can't arm 161266405Sian * disable watchdog as watchdog(9) requires 162266405Sian */ 163266405Sian device_printf(sc->dev, 164266405Sian "Can't arm, timeout is more than 16 sec\n"); 165266405Sian mtx_unlock(&sc->mtx); 166266405Sian WRITE(sc, WDOG_MODE, 0); 167266405Sian return; 168266405Sian } 169246660Sgonzo } 170246660Sgonzo else 171246660Sgonzo WRITE(sc, WDOG_MODE, 0); 172246660Sgonzo 173246660Sgonzo mtx_unlock(&sc->mtx); 174246660Sgonzo} 175246660Sgonzo 176246660Sgonzovoid 177246660Sgonzoa10wd_watchdog_reset() 178246660Sgonzo{ 179246660Sgonzo 180246660Sgonzo if (a10wd_sc == NULL) { 181246660Sgonzo printf("Reset: watchdog device has not been initialized\n"); 182246660Sgonzo return; 183246660Sgonzo } 184246660Sgonzo 185246660Sgonzo WRITE(a10wd_sc, WDOG_MODE, 186246660Sgonzo (wd_intervals[0].value << WDOG_MODE_INTVL_SHIFT) | 187246660Sgonzo WDOG_MODE_EN | WDOG_MODE_RST_EN); 188246660Sgonzo 189246660Sgonzo while(1) 190246660Sgonzo ; 191246660Sgonzo 192246660Sgonzo} 193246660Sgonzo 194246660Sgonzostatic device_method_t a10wd_methods[] = { 195246660Sgonzo DEVMETHOD(device_probe, a10wd_probe), 196246660Sgonzo DEVMETHOD(device_attach, a10wd_attach), 197246660Sgonzo 198246660Sgonzo DEVMETHOD_END 199246660Sgonzo}; 200246660Sgonzo 201246660Sgonzostatic driver_t a10wd_driver = { 202246660Sgonzo "a10wd", 203246660Sgonzo a10wd_methods, 204246660Sgonzo sizeof(struct a10wd_softc), 205246660Sgonzo}; 206246660Sgonzostatic devclass_t a10wd_devclass; 207246660Sgonzo 208246660SgonzoDRIVER_MODULE(a10wd, simplebus, a10wd_driver, a10wd_devclass, 0, 0); 209