1258546Sganbold/*- 2266337Sian * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> 3258546Sganbold * All rights reserved. 4258546Sganbold * 5258546Sganbold * Redistribution and use in source and binary forms, with or without 6258546Sganbold * modification, are permitted provided that the following conditions 7258546Sganbold * are met: 8258546Sganbold * 1. Redistributions of source code must retain the above copyright 9258546Sganbold * notice, this list of conditions and the following disclaimer. 10258546Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11258546Sganbold * notice, this list of conditions and the following disclaimer in the 12258546Sganbold * documentation and/or other materials provided with the distribution. 13258546Sganbold * 14258546Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15258546Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16258546Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17258546Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18258546Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19258546Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20258546Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21258546Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22258546Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23258546Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24258546Sganbold * SUCH DAMAGE. 25258546Sganbold */ 26258546Sganbold#include <sys/cdefs.h> 27258546Sganbold__FBSDID("$FreeBSD$"); 28258546Sganbold 29258546Sganbold#include <sys/param.h> 30258546Sganbold#include <sys/systm.h> 31258546Sganbold#include <sys/watchdog.h> 32258546Sganbold#include <sys/bus.h> 33258546Sganbold#include <sys/kernel.h> 34258546Sganbold#include <sys/lock.h> 35258546Sganbold#include <sys/module.h> 36258546Sganbold#include <sys/mutex.h> 37258546Sganbold#include <sys/rman.h> 38258546Sganbold 39258546Sganbold#include <dev/fdt/fdt_common.h> 40258546Sganbold#include <dev/ofw/openfirm.h> 41258546Sganbold#include <dev/ofw/ofw_bus.h> 42258546Sganbold#include <dev/ofw/ofw_bus_subr.h> 43258546Sganbold 44258546Sganbold#include <machine/bus.h> 45258546Sganbold#include <machine/cpufunc.h> 46258546Sganbold#include <machine/machdep.h> 47258546Sganbold#include <machine/fdt.h> 48258546Sganbold 49258546Sganbold#include <arm/rockchip/rk30xx_wdog.h> 50258546Sganbold 51258546Sganbold#ifndef RK30_WDT_BASE 52258546Sganbold#define RK30_WDT_BASE 0x2004c000 53258546Sganbold#define RK30_WDT_PSIZE 0x100 54258546Sganbold#endif 55258546Sganbold 56258546Sganbold#define RK30_WDT_READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 57258546Sganbold#define RK30_WDT_WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 58258546Sganbold 59258546Sganbold#define WDOG_CTRL 0x00 60258546Sganbold#define WDOG_CTRL_EN (1 << 0) 61258546Sganbold#define WDOG_CTRL_RSP_MODE (1 << 1) 62258546Sganbold#define WDOG_CTRL_RST_PULSE (4 << 2) 63258546Sganbold#define WDOG_CTRL_RST 0xa 64258546Sganbold#define WDOG_TORR 0x04 65258546Sganbold#define WDOG_TORR_INTVL_SHIFT 0 66258546Sganbold#define WDOG_CCVR 0x08 67258546Sganbold#define WDOG_CRR 0x0c 68258546Sganbold#define WDOG_CRR_PWD 0x76 69258546Sganbold#define WDOG_STAT 0x10 70258546Sganbold#define WDOG_EOI 0x14 71258546Sganbold 72258546Sganboldstatic struct rk30_wd_softc *rk30_wd_sc = NULL; 73258546Sganbold 74258546Sganboldstruct rk30_wd_softc { 75258546Sganbold device_t dev; 76258546Sganbold struct resource *res; 77258546Sganbold struct mtx mtx; 78258546Sganbold int freq; 79258546Sganbold}; 80258546Sganbold 81258546Sganboldstatic void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error); 82258546Sganbold 83258546Sganboldstatic int 84258546Sganboldrk30_wd_probe(device_t dev) 85258546Sganbold{ 86258546Sganbold 87266152Sian if (!ofw_bus_status_okay(dev)) 88266152Sian return (ENXIO); 89266152Sian 90258546Sganbold if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) { 91258546Sganbold device_set_desc(dev, "Rockchip RK30XX Watchdog"); 92258546Sganbold return (BUS_PROBE_DEFAULT); 93258546Sganbold } 94258546Sganbold 95258546Sganbold return (ENXIO); 96258546Sganbold} 97258546Sganbold 98258546Sganboldstatic int 99258546Sganboldrk30_wd_attach(device_t dev) 100258546Sganbold{ 101258546Sganbold struct rk30_wd_softc *sc; 102258546Sganbold int rid; 103258546Sganbold phandle_t node; 104258546Sganbold pcell_t cell; 105258546Sganbold 106258546Sganbold if (rk30_wd_sc != NULL) 107258546Sganbold return (ENXIO); 108258546Sganbold 109258546Sganbold sc = device_get_softc(dev); 110258546Sganbold sc->dev = dev; 111258546Sganbold 112258546Sganbold node = ofw_bus_get_node(sc->dev); 113258546Sganbold if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) 114258546Sganbold sc->freq = cell / 1000000; 115258546Sganbold else 116258546Sganbold return (ENXIO); 117258546Sganbold 118258546Sganbold rid = 0; 119258546Sganbold sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 120258546Sganbold if (sc->res == NULL) { 121258546Sganbold device_printf(dev, "could not allocate memory resource\n"); 122258546Sganbold return (ENXIO); 123258546Sganbold } 124258546Sganbold 125258546Sganbold rk30_wd_sc = sc; 126258546Sganbold mtx_init(&sc->mtx, "RK30XX Watchdog", "rk30_wd", MTX_DEF); 127258546Sganbold EVENTHANDLER_REGISTER(watchdog_list, rk30_wd_watchdog_fn, sc, 0); 128258546Sganbold 129258546Sganbold return (0); 130258546Sganbold} 131258546Sganbold 132258546Sganboldstatic void 133258546Sganboldrk30_wd_watchdog_fn(void *private, u_int cmd, int *error) 134258546Sganbold{ 135258546Sganbold struct rk30_wd_softc *sc; 136258546Sganbold uint64_t ms, m, max; 137258546Sganbold int i; 138258546Sganbold 139258546Sganbold sc = private; 140258546Sganbold mtx_lock(&sc->mtx); 141258546Sganbold 142258546Sganbold cmd &= WD_INTERVAL; 143258546Sganbold 144258546Sganbold if (cmd > 0) { 145258546Sganbold ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; 146258546Sganbold m = 0xffff / sc->freq; 147258546Sganbold max = 0x7fffffff / sc->freq + 1; 148258546Sganbold i = 0; 149258546Sganbold while (m < max && m < ms) { 150258546Sganbold m <<= 1; 151258546Sganbold i++; 152258546Sganbold } 153258546Sganbold if (m < max) { 154258546Sganbold RK30_WDT_WRITE(sc, WDOG_TORR, 155258546Sganbold i << WDOG_TORR_INTVL_SHIFT); 156258546Sganbold RK30_WDT_WRITE(sc, WDOG_CTRL, 157258546Sganbold WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | 158258546Sganbold WDOG_CTRL_RST_PULSE); 159258546Sganbold RK30_WDT_WRITE(sc, WDOG_CRR, WDOG_CRR_PWD); 160258546Sganbold *error = 0; 161258546Sganbold } else { 162258546Sganbold device_printf(sc->dev, "Can not be disabled\n"); 163258546Sganbold mtx_unlock(&sc->mtx); 164258546Sganbold RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 165258546Sganbold return; 166258546Sganbold } 167258546Sganbold } 168258546Sganbold else 169258546Sganbold RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 170258546Sganbold 171258546Sganbold mtx_unlock(&sc->mtx); 172258546Sganbold} 173258546Sganbold 174258546Sganboldvoid 175258546Sganboldrk30_wd_watchdog_reset() 176258546Sganbold{ 177258546Sganbold bus_space_handle_t bsh; 178258546Sganbold 179258546Sganbold bus_space_map(fdtbus_bs_tag, RK30_WDT_BASE, RK30_WDT_PSIZE, 0, &bsh); 180258546Sganbold bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_TORR, 0); 181258546Sganbold bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_CTRL, 182258546Sganbold WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE); 183258546Sganbold 184258546Sganbold while (1); 185258546Sganbold} 186258546Sganbold 187258546Sganboldstatic device_method_t rk30_wd_methods[] = { 188258546Sganbold DEVMETHOD(device_probe, rk30_wd_probe), 189258546Sganbold DEVMETHOD(device_attach, rk30_wd_attach), 190258546Sganbold 191258546Sganbold DEVMETHOD_END 192258546Sganbold}; 193258546Sganbold 194258546Sganboldstatic driver_t rk30_wd_driver = { 195258546Sganbold "rk30_wd", 196258546Sganbold rk30_wd_methods, 197258546Sganbold sizeof(struct rk30_wd_softc), 198258546Sganbold}; 199258546Sganboldstatic devclass_t rk30_wd_devclass; 200258546Sganbold 201258546SganboldDRIVER_MODULE(rk30_wd, simplebus, rk30_wd_driver, rk30_wd_devclass, 0, 0); 202