174462Salfred/*- 274462Salfred * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> 374462Salfred * All rights reserved. 474462Salfred * 574462Salfred * Redistribution and use in source and binary forms, with or without 674462Salfred * modification, are permitted provided that the following conditions 774462Salfred * are met: 874462Salfred * 1. Redistributions of source code must retain the above copyright 974462Salfred * notice, this list of conditions and the following disclaimer. 1074462Salfred * 2. Redistributions in binary form must reproduce the above copyright 1174462Salfred * notice, this list of conditions and the following disclaimer in the 1274462Salfred * documentation and/or other materials provided with the distribution. 1374462Salfred * 1474462Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1574462Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1674462Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1774462Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1874462Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1974462Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2074462Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2174462Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2274462Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2374462Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2474462Salfred * SUCH DAMAGE. 2574462Salfred */ 2674462Salfred#include <sys/cdefs.h> 2774462Salfred__FBSDID("$FreeBSD$"); 2874462Salfred 2974462Salfred#include <sys/param.h> 3074462Salfred#include <sys/systm.h> 3174462Salfred#include <sys/watchdog.h> 3274462Salfred#include <sys/bus.h> 3374462Salfred#include <sys/kernel.h> 3474462Salfred#include <sys/lock.h> 3574462Salfred#include <sys/module.h> 3674462Salfred#include <sys/mutex.h> 3774462Salfred#include <sys/rman.h> 3874462Salfred 3974462Salfred#include <dev/fdt/fdt_common.h> 4074462Salfred#include <dev/ofw/openfirm.h> 4174462Salfred#include <dev/ofw/ofw_bus.h> 4274462Salfred#include <dev/ofw/ofw_bus_subr.h> 4374462Salfred 4474462Salfred#include <machine/bus.h> 4574462Salfred#include <machine/cpufunc.h> 4674462Salfred#include <machine/machdep.h> 4774462Salfred#include <machine/fdt.h> 4874462Salfred 4974462Salfred#include <arm/rockchip/rk30xx_wdog.h> 5074462Salfred 5174462Salfred#ifndef RK30_WDT_BASE 5274462Salfred#define RK30_WDT_BASE 0x2004c000 5374462Salfred#define RK30_WDT_PSIZE 0x100 5474462Salfred#endif 5574462Salfred 5674462Salfred#define RK30_WDT_READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 5774462Salfred#define RK30_WDT_WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 5874462Salfred 5974462Salfred#define WDOG_CTRL 0x00 6074462Salfred#define WDOG_CTRL_EN (1 << 0) 6174462Salfred#define WDOG_CTRL_RSP_MODE (1 << 1) 6274462Salfred#define WDOG_CTRL_RST_PULSE (4 << 2) 6374462Salfred#define WDOG_CTRL_RST 0xa 6474462Salfred#define WDOG_TORR 0x04 6574462Salfred#define WDOG_TORR_INTVL_SHIFT 0 6674462Salfred#define WDOG_CCVR 0x08 6774462Salfred#define WDOG_CRR 0x0c 6874462Salfred#define WDOG_CRR_PWD 0x76 6974462Salfred#define WDOG_STAT 0x10 7074462Salfred#define WDOG_EOI 0x14 7174462Salfred 7274462Salfredstatic struct rk30_wd_softc *rk30_wd_sc = NULL; 7374462Salfred 7474462Salfredstruct rk30_wd_softc { 7574462Salfred device_t dev; 7674462Salfred struct resource *res; 7774462Salfred struct mtx mtx; 7874462Salfred int freq; 7974462Salfred}; 8074462Salfred 8174462Salfredstatic void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error); 8274462Salfred 8374462Salfredstatic int 8474462Salfredrk30_wd_probe(device_t dev) 8574462Salfred{ 8674462Salfred 8774462Salfred if (!ofw_bus_status_okay(dev)) 8874462Salfred return (ENXIO); 8974462Salfred 9074462Salfred if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) { 9174462Salfred device_set_desc(dev, "Rockchip RK30XX Watchdog"); 9274462Salfred return (BUS_PROBE_DEFAULT); 9374462Salfred } 9474462Salfred 9574462Salfred return (ENXIO); 9674462Salfred} 97 98static int 99rk30_wd_attach(device_t dev) 100{ 101 struct rk30_wd_softc *sc; 102 int rid; 103 phandle_t node; 104 pcell_t cell; 105 106 if (rk30_wd_sc != NULL) 107 return (ENXIO); 108 109 sc = device_get_softc(dev); 110 sc->dev = dev; 111 112 node = ofw_bus_get_node(sc->dev); 113 if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) 114 sc->freq = cell / 1000000; 115 else 116 return (ENXIO); 117 118 rid = 0; 119 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 120 if (sc->res == NULL) { 121 device_printf(dev, "could not allocate memory resource\n"); 122 return (ENXIO); 123 } 124 125 rk30_wd_sc = sc; 126 mtx_init(&sc->mtx, "RK30XX Watchdog", "rk30_wd", MTX_DEF); 127 EVENTHANDLER_REGISTER(watchdog_list, rk30_wd_watchdog_fn, sc, 0); 128 129 return (0); 130} 131 132static void 133rk30_wd_watchdog_fn(void *private, u_int cmd, int *error) 134{ 135 struct rk30_wd_softc *sc; 136 uint64_t ms, m, max; 137 int i; 138 139 sc = private; 140 mtx_lock(&sc->mtx); 141 142 cmd &= WD_INTERVAL; 143 144 if (cmd > 0) { 145 ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; 146 m = 0xffff / sc->freq; 147 max = 0x7fffffff / sc->freq + 1; 148 i = 0; 149 while (m < max && m < ms) { 150 m <<= 1; 151 i++; 152 } 153 if (m < max) { 154 RK30_WDT_WRITE(sc, WDOG_TORR, 155 i << WDOG_TORR_INTVL_SHIFT); 156 RK30_WDT_WRITE(sc, WDOG_CTRL, 157 WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | 158 WDOG_CTRL_RST_PULSE); 159 RK30_WDT_WRITE(sc, WDOG_CRR, WDOG_CRR_PWD); 160 *error = 0; 161 } else { 162 device_printf(sc->dev, "Can not be disabled\n"); 163 mtx_unlock(&sc->mtx); 164 RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 165 return; 166 } 167 } 168 else 169 RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 170 171 mtx_unlock(&sc->mtx); 172} 173 174void 175rk30_wd_watchdog_reset() 176{ 177 bus_space_handle_t bsh; 178 179 bus_space_map(fdtbus_bs_tag, RK30_WDT_BASE, RK30_WDT_PSIZE, 0, &bsh); 180 bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_TORR, 0); 181 bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_CTRL, 182 WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE); 183 184 while (1); 185} 186 187static device_method_t rk30_wd_methods[] = { 188 DEVMETHOD(device_probe, rk30_wd_probe), 189 DEVMETHOD(device_attach, rk30_wd_attach), 190 191 DEVMETHOD_END 192}; 193 194static driver_t rk30_wd_driver = { 195 "rk30_wd", 196 rk30_wd_methods, 197 sizeof(struct rk30_wd_softc), 198}; 199static devclass_t rk30_wd_devclass; 200 201DRIVER_MODULE(rk30_wd, simplebus, rk30_wd_driver, rk30_wd_devclass, 0, 0); 202