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