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: stable/11/sys/arm/rockchip/rk30xx_wdog.c 314506 2017-03-01 19:55:04Z ian $"); 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/machdep.h> 46#include <machine/fdt.h> 47 48#include <arm/rockchip/rk30xx_wdog.h> 49 50#ifndef RK30_WDT_BASE 51#define RK30_WDT_BASE 0x2004c000 52#define RK30_WDT_PSIZE 0x100 53#endif 54 55#define RK30_WDT_READ(_sc, _r) bus_read_4((_sc)->res, (_r)) 56#define RK30_WDT_WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) 57 58#define WDOG_CTRL 0x00 59#define WDOG_CTRL_EN (1 << 0) 60#define WDOG_CTRL_RSP_MODE (1 << 1) 61#define WDOG_CTRL_RST_PULSE (4 << 2) 62#define WDOG_CTRL_RST 0xa 63#define WDOG_TORR 0x04 64#define WDOG_TORR_INTVL_SHIFT 0 65#define WDOG_CCVR 0x08 66#define WDOG_CRR 0x0c 67#define WDOG_CRR_PWD 0x76 68#define WDOG_STAT 0x10 69#define WDOG_EOI 0x14 70 71static struct rk30_wd_softc *rk30_wd_sc = NULL; 72 73struct rk30_wd_softc { 74 device_t dev; 75 struct resource *res; 76 struct mtx mtx; 77 int freq; 78}; 79 80static void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error); 81 82static int 83rk30_wd_probe(device_t dev) 84{ 85 86 if (!ofw_bus_status_okay(dev)) 87 return (ENXIO); 88 89 if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) { 90 device_set_desc(dev, "Rockchip RK30XX Watchdog"); 91 return (BUS_PROBE_DEFAULT); 92 } 93 94 return (ENXIO); 95} 96 97static int 98rk30_wd_attach(device_t dev) 99{ 100 struct rk30_wd_softc *sc; 101 int rid; 102 phandle_t node; 103 pcell_t cell; 104 105 if (rk30_wd_sc != NULL) 106 return (ENXIO); 107 108 sc = device_get_softc(dev); 109 sc->dev = dev; 110 111 node = ofw_bus_get_node(sc->dev); 112 if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) 113 sc->freq = cell / 1000000; 114 else 115 return (ENXIO); 116 117 rid = 0; 118 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 119 if (sc->res == NULL) { 120 device_printf(dev, "could not allocate memory resource\n"); 121 return (ENXIO); 122 } 123 124 rk30_wd_sc = sc; 125 mtx_init(&sc->mtx, "RK30XX Watchdog", "rk30_wd", MTX_DEF); 126 EVENTHANDLER_REGISTER(watchdog_list, rk30_wd_watchdog_fn, sc, 0); 127 128 return (0); 129} 130 131static void 132rk30_wd_watchdog_fn(void *private, u_int cmd, int *error) 133{ 134 struct rk30_wd_softc *sc; 135 uint64_t ms, m, max; 136 int i; 137 138 sc = private; 139 mtx_lock(&sc->mtx); 140 141 cmd &= WD_INTERVAL; 142 143 if (cmd > 0) { 144 ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; 145 m = 0xffff / sc->freq; 146 max = 0x7fffffff / sc->freq + 1; 147 i = 0; 148 while (m < max && m < ms) { 149 m <<= 1; 150 i++; 151 } 152 if (m < max) { 153 RK30_WDT_WRITE(sc, WDOG_TORR, 154 i << WDOG_TORR_INTVL_SHIFT); 155 RK30_WDT_WRITE(sc, WDOG_CTRL, 156 WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | 157 WDOG_CTRL_RST_PULSE); 158 RK30_WDT_WRITE(sc, WDOG_CRR, WDOG_CRR_PWD); 159 *error = 0; 160 } else { 161 device_printf(sc->dev, "Can not be disabled\n"); 162 mtx_unlock(&sc->mtx); 163 RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 164 return; 165 } 166 } 167 else 168 RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); 169 170 mtx_unlock(&sc->mtx); 171} 172 173void 174rk30_wd_watchdog_reset(void) 175{ 176 bus_space_handle_t bsh; 177 178 bus_space_map(fdtbus_bs_tag, RK30_WDT_BASE, RK30_WDT_PSIZE, 0, &bsh); 179 bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_TORR, 0); 180 bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_CTRL, 181 WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE); 182 183 while (1); 184} 185 186static device_method_t rk30_wd_methods[] = { 187 DEVMETHOD(device_probe, rk30_wd_probe), 188 DEVMETHOD(device_attach, rk30_wd_attach), 189 190 DEVMETHOD_END 191}; 192 193static driver_t rk30_wd_driver = { 194 "rk30_wd", 195 rk30_wd_methods, 196 sizeof(struct rk30_wd_softc), 197}; 198static devclass_t rk30_wd_devclass; 199 200DRIVER_MODULE(rk30_wd, simplebus, rk30_wd_driver, rk30_wd_devclass, 0, 0); 201