12966Swollman/*-
250477Speter * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org>
31590Srgrimes * All rights reserved.
44699Sjkh *
534706Sbde * Redistribution and use in source and binary forms, with or without
697128Sjmallett * modification, are permitted provided that the following conditions
71930Swollman * are met:
81930Swollman * 1. Redistributions of source code must retain the above copyright
9106717Smarcel *    notice, this list of conditions and the following disclaimer.
10100200Swollman * 2. Redistributions in binary form must reproduce the above copyright
11100200Swollman *    notice, this list of conditions and the following disclaimer in the
1296630Stjr *    documentation and/or other materials provided with the distribution.
1338653Sgpalmer *
14121666Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1591706Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638653Sgpalmer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738653Sgpalmer * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838653Sgpalmer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19124587Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2057013Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21148771Scperciva * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2278562Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23108667Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24119553Sphk * SUCH DAMAGE.
25119553Sphk */
26148677Sphk#include <sys/cdefs.h>
2738653Sgpalmer__FBSDID("$FreeBSD$");
28148741Sphk
2938653Sgpalmer#include <sys/param.h>
3038653Sgpalmer#include <sys/systm.h>
31124587Sru#include <sys/watchdog.h>
3238653Sgpalmer#include <sys/bus.h>
3338653Sgpalmer#include <sys/kernel.h>
3438653Sgpalmer#include <sys/lock.h>
3538653Sgpalmer#include <sys/module.h>
3638653Sgpalmer#include <sys/mutex.h>
3738653Sgpalmer#include <sys/rman.h>
3838653Sgpalmer
3938653Sgpalmer#include <dev/fdt/fdt_common.h>
4038653Sgpalmer#include <dev/ofw/openfirm.h>
4138653Sgpalmer#include <dev/ofw/ofw_bus.h>
4238653Sgpalmer#include <dev/ofw/ofw_bus_subr.h>
4395926Stjr
4438653Sgpalmer#include <machine/bus.h>
4538653Sgpalmer#include <machine/cpufunc.h>
46124587Sru#include <machine/machdep.h>
4738653Sgpalmer#include <machine/fdt.h>
4838653Sgpalmer
4938653Sgpalmer#include <arm/rockchip/rk30xx_wdog.h>
50108439Sobrien
51109314Sobrien#ifndef	RK30_WDT_BASE
5240826Sjoerg#define	RK30_WDT_BASE		0x2004c000
5338653Sgpalmer#define	RK30_WDT_PSIZE		0x100
5438653Sgpalmer#endif
5538653Sgpalmer
5638653Sgpalmer#define	RK30_WDT_READ(_sc, _r)		bus_read_4((_sc)->res, (_r))
5738653Sgpalmer#define	RK30_WDT_WRITE(_sc, _r, _v)	bus_write_4((_sc)->res, (_r), (_v))
5838653Sgpalmer
5938653Sgpalmer#define	WDOG_CTRL		0x00
6038653Sgpalmer#define	WDOG_CTRL_EN		(1 << 0)
6138653Sgpalmer#define	WDOG_CTRL_RSP_MODE	(1 << 1)
6238653Sgpalmer#define	WDOG_CTRL_RST_PULSE	(4 << 2)
6338653Sgpalmer#define	WDOG_CTRL_RST		0xa
6441036Sdima#define	WDOG_TORR		0x04
6563499Sps#define	WDOG_TORR_INTVL_SHIFT	0
6638653Sgpalmer#define	WDOG_CCVR		0x08
67103303Speter#define	WDOG_CRR		0x0c
6838653Sgpalmer#define	WDOG_CRR_PWD		0x76
69101629Sjake#define	WDOG_STAT		0x10
7038653Sgpalmer#define	WDOG_EOI		0x14
71111204Sobrien
7238653Sgpalmerstatic struct rk30_wd_softc *rk30_wd_sc = NULL;
73148780Sphk
7438653Sgpalmerstruct rk30_wd_softc {
75124587Sru	device_t		dev;
7638653Sgpalmer	struct resource		*res;
7738653Sgpalmer	struct mtx		mtx;
7838653Sgpalmer	int			freq;
7938653Sgpalmer};
8038653Sgpalmer
8138653Sgpalmerstatic void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error);
8238653Sgpalmer
8338653Sgpalmerstatic int
8438653Sgpalmerrk30_wd_probe(device_t dev)
8538653Sgpalmer{
8638653Sgpalmer
8793619Sjake	if (!ofw_bus_status_okay(dev))
8838653Sgpalmer		return (ENXIO);
8938653Sgpalmer
9038653Sgpalmer	if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) {
9139614Sbde		device_set_desc(dev, "Rockchip RK30XX Watchdog");
9238653Sgpalmer		return (BUS_PROBE_DEFAULT);
9360789Sps	}
9460789Sps
9560789Sps	return (ENXIO);
96119553Sphk}
9738653Sgpalmer
98116677Sphantomstatic int
9938653Sgpalmerrk30_wd_attach(device_t dev)
10038653Sgpalmer{
10138653Sgpalmer	struct rk30_wd_softc *sc;
10238653Sgpalmer	int rid;
10338653Sgpalmer	phandle_t node;
104126701Sdes	pcell_t cell;
10538653Sgpalmer
10638653Sgpalmer	if (rk30_wd_sc != NULL)
10738653Sgpalmer		return (ENXIO);
10838653Sgpalmer
10938653Sgpalmer	sc = device_get_softc(dev);
11038653Sgpalmer	sc->dev = dev;
11138653Sgpalmer
11296845Smarkm	node = ofw_bus_get_node(sc->dev);
11338653Sgpalmer	if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0)
11439614Sbde		sc->freq = cell / 1000000;
11538653Sgpalmer	else
11638653Sgpalmer		return (ENXIO);
11738653Sgpalmer
11838653Sgpalmer	rid = 0;
11938653Sgpalmer	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
120146107Sfjoe	if (sc->res == NULL) {
12138653Sgpalmer		device_printf(dev, "could not allocate memory resource\n");
12239914Sdfr		return (ENXIO);
123141800Sdelphij	}
12438653Sgpalmer
125124587Sru	rk30_wd_sc = sc;
126124587Sru	mtx_init(&sc->mtx, "RK30XX Watchdog", "rk30_wd", MTX_DEF);
12738653Sgpalmer	EVENTHANDLER_REGISTER(watchdog_list, rk30_wd_watchdog_fn, sc, 0);
12897365Stjr
129124587Sru	return (0);
13038653Sgpalmer}
13138653Sgpalmer
13276273Sbrianstatic void
13338653Sgpalmerrk30_wd_watchdog_fn(void *private, u_int cmd, int *error)
134135549Sdes{
135135549Sdes	struct rk30_wd_softc *sc;
13638653Sgpalmer	uint64_t ms, m, max;
13738653Sgpalmer	int i;
13838653Sgpalmer
13938653Sgpalmer	sc = private;
14038653Sgpalmer	mtx_lock(&sc->mtx);
14138653Sgpalmer
14238653Sgpalmer	cmd &= WD_INTERVAL;
14397096Stjr
144127471Sgad	if (cmd > 0) {
14538653Sgpalmer		ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000;
14638653Sgpalmer		m = 0xffff / sc->freq;
14738653Sgpalmer		max = 0x7fffffff / sc->freq + 1;
14838653Sgpalmer		i = 0;
14938653Sgpalmer		while (m < max && m < ms) {
15038653Sgpalmer			m <<= 1;
151143026Strhodes			i++;
15238653Sgpalmer		}
15338653Sgpalmer		if (m < max) {
15438653Sgpalmer			RK30_WDT_WRITE(sc, WDOG_TORR,
155143026Strhodes			    i << WDOG_TORR_INTVL_SHIFT);
15638653Sgpalmer			RK30_WDT_WRITE(sc, WDOG_CTRL,
15738653Sgpalmer			    WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE |
15838653Sgpalmer			    WDOG_CTRL_RST_PULSE);
15938653Sgpalmer			RK30_WDT_WRITE(sc, WDOG_CRR, WDOG_CRR_PWD);
16038653Sgpalmer			*error = 0;
16138653Sgpalmer		} else {
16238653Sgpalmer			device_printf(sc->dev, "Can not be disabled\n");
16338653Sgpalmer			mtx_unlock(&sc->mtx);
16438653Sgpalmer			RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST);
165124587Sru			return;
16645701Sdes		}
16738653Sgpalmer	}
16897955Sdougb	else
16938653Sgpalmer		RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST);
17041035Sdima
17197034Stjr	mtx_unlock(&sc->mtx);
17238653Sgpalmer}
17338653Sgpalmer
174127947Skientzlevoid
17538653Sgpalmerrk30_wd_watchdog_reset()
17638653Sgpalmer{
177117675Smarkm	bus_space_handle_t bsh;
17838653Sgpalmer
17938653Sgpalmer	bus_space_map(fdtbus_bs_tag, RK30_WDT_BASE, RK30_WDT_PSIZE, 0, &bsh);
18088277Smarkm	bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_TORR, 0);
18138653Sgpalmer	bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_CTRL,
18238653Sgpalmer	    WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE);
18338653Sgpalmer
18438653Sgpalmer	while (1);
18538653Sgpalmer}
18663437Ssheldonh
187111204Sobrienstatic device_method_t rk30_wd_methods[] = {
18838653Sgpalmer	DEVMETHOD(device_probe, rk30_wd_probe),
18938653Sgpalmer	DEVMETHOD(device_attach, rk30_wd_attach),
19038653Sgpalmer
191124587Sru	DEVMETHOD_END
19238653Sgpalmer};
19338653Sgpalmer
19438653Sgpalmerstatic driver_t rk30_wd_driver = {
19538653Sgpalmer	"rk30_wd",
19638653Sgpalmer	rk30_wd_methods,
19738653Sgpalmer	sizeof(struct rk30_wd_softc),
19838653Sgpalmer};
199124587Srustatic devclass_t rk30_wd_devclass;
200124587Sru
20138653SgpalmerDRIVER_MODULE(rk30_wd, simplebus, rk30_wd_driver, rk30_wd_devclass, 0, 0);
20238653Sgpalmer