1/*- 2 * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@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/rockchip/rk30xx_wdog.h> 50 51#ifndef RK30_WDT_BASE 52#define RK30_WDT_BASE 0x2004c000 53#define RK30_WDT_PSIZE 0x100 54#endif 55 56#define RK30_WDT_READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 57#define RK30_WDT_WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 58 59#define WDOG_CTRL 0x00 60#define WDOG_CTRL_EN (1 << 0) 61#define WDOG_CTRL_RSP_MODE (1 << 1) 62#define WDOG_CTRL_RST_PULSE (4 << 2) 63#define WDOG_CTRL_RST 0xa 64#define WDOG_TORR 0x04 65#define WDOG_TORR_INTVL_SHIFT 0 66#define WDOG_CCVR 0x08 67#define WDOG_CRR 0x0c 68#define WDOG_CRR_PWD 0x76 69#define WDOG_STAT 0x10 70#define WDOG_EOI 0x14 71 72static struct rk30_wd_softc *rk30_wd_sc = NULL; 73 74struct rk30_wd_softc { 75 device_t dev; 76 struct resource *res; 77 struct mtx mtx; 78 int freq; 79}; 80 81static void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error); 82 83static int 84rk30_wd_probe(device_t dev) 85{ 86 87 if (!ofw_bus_status_okay(dev)) 88 return (ENXIO); 89 90 if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) { 91 device_set_desc(dev, "Rockchip RK30XX Watchdog"); 92 return (BUS_PROBE_DEFAULT); 93 } 94 95 return (ENXIO); 96} 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