xref: /freebsd/sys/riscv/sifive/fe310_aon.c (revision a3557ef0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Axiado Corporation
5  * All rights reserved.
6  *
7  * This software was developed in part by Nick O'Brien and Rishul Naik
8  * for Axiado Corporation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/clock.h>
39 #include <sys/eventhandler.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/rman.h>
45 #include <sys/sdt.h>
46 #include <sys/time.h>
47 #include <sys/timespec.h>
48 #include <sys/timex.h>
49 #include <sys/watchdog.h>
50 
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
53 
54 #include <machine/bus.h>
55 #include <machine/clock.h>
56 #include <machine/intr.h>
57 #include <machine/resource.h>
58 
59 #include "clock_if.h"
60 
61 #define FEAON_AON_WDT_BASE		0x0
62 #define FEAON_AON_RTC_BASE		0x40
63 #define FEAON_AON_CLKCFG_BASE		0x70
64 #define FEAON_AON_BACKUP_BASE		0x80
65 #define FEAON_AON_PMU_BASE		0x100
66 
67 /* Watchdog specific */
68 #define FEAON_WDT_CFG			0x0
69 #define FEAON_WDT_COUNT			0x8
70 #define FEAON_WDT_DOGS			0x10
71 #define FEAON_WDT_FEED			0x18
72 #define FEAON_WDT_KEY			0x1C
73 #define FEAON_WDT_CMP			0x20
74 
75 #define FEAON_WDT_CFG_SCALE_MASK	0xF
76 #define FEAON_WDT_CFG_RST_EN		(1 << 8)
77 #define FEAON_WDT_CFG_ZERO_CMP		(1 << 9)
78 #define FEAON_WDT_CFG_EN_ALWAYS		(1 << 12)
79 #define FEAON_WDT_CFG_EN_CORE_AWAKE	(1 << 13)
80 #define FEAON_WDT_CFG_IP		(1 << 28)
81 
82 #define FEAON_WDT_CMP_MASK		0xFFFF
83 
84 #define FEAON_WDT_FEED_FOOD		0xD09F00D
85 
86 #define FEAON_WDT_KEY_UNLOCK		0x51F15E
87 
88 #define FEAON_WDT_TIMEBASE_FREQ		31250
89 #define FEAON_WDT_TIMEBASE_RATIO	(NANOSECOND / FEAON_WDT_TIMEBASE_FREQ)
90 
91 /* Real-time clock specific */
92 #define FEAON_RTC_CFG			0x40
93 #define FEAON_RTC_LO			0x48
94 #define FEAON_RTC_HI			0x4C
95 #define FEAON_RTC_CMP			0x60
96 
97 #define FEAON_RTC_CFG_SCALE_MASK	0xF
98 #define FEAON_RTC_CFG_EN		(1 << 12)
99 #define FEAON_RTC_CFG_IP		(1 << 28)
100 
101 #define FEAON_RTC_HI_MASK		0xFFFF
102 
103 #define FEAON_RTC_TIMEBASE_FREQ		31250LL
104 
105 #define FEAON_LOCK(sc)			mtx_lock(&(sc)->mtx)
106 #define FEAON_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
107 #define FEAON_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED)
108 #define FEAON_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED)
109 
110 #define FEAON_READ_4(sc, reg)		bus_read_4(sc->reg_res, reg)
111 #define FEAON_WRITE_4(sc, reg, val)	bus_write_4(sc->reg_res, reg, val)
112 
113 #define FEAON_WDT_WRITE_4(sc, reg, val) do {					\
114 		FEAON_WRITE_4(sc, (FEAON_WDT_KEY), (FEAON_WDT_KEY_UNLOCK));	\
115 		FEAON_WRITE_4(sc, reg, val);					\
116 	} while (0)
117 
118 struct feaon_softc {
119 	device_t		dev;
120 	struct mtx		mtx;
121 
122 	/* Resources */
123 	int			reg_rid;
124 	struct resource		*reg_res;
125 
126 	/* WDT */
127 	eventhandler_tag	ev_tag;
128 };
129 
130 static void
131 feaon_wdt_event(void *arg, unsigned int cmd, int *err)
132 {
133 	struct feaon_softc *sc;
134 	uint32_t scale, val;
135 	uint64_t time;
136 
137 	sc = (struct feaon_softc *)arg;
138 	FEAON_LOCK(sc);
139 
140 	/* First feed WDT */
141 	FEAON_WDT_WRITE_4(sc, FEAON_WDT_FEED, FEAON_WDT_FEED_FOOD);
142 
143 	if ((cmd & WD_INTERVAL) == WD_TO_NEVER) {
144 		/* Disable WDT */
145 		val = FEAON_READ_4(sc, FEAON_WDT_CFG);
146 		val &= ~(FEAON_WDT_CFG_EN_ALWAYS | FEAON_WDT_CFG_EN_CORE_AWAKE);
147 		FEAON_WDT_WRITE_4(sc, FEAON_WDT_CFG, val);
148 		goto exit;
149 	}
150 
151 	/* Calculate time in WDT frequency */
152 	time = 1LL << (cmd & WD_INTERVAL);
153 	time /= FEAON_WDT_TIMEBASE_RATIO;
154 
155 	/* Fit time in CMP register with scale */
156 	scale = 0;
157 	while (time > FEAON_WDT_CMP_MASK) {
158 		time >>= 1;
159 		scale++;
160 	}
161 
162 	if (time > FEAON_WDT_CMP_MASK || scale > FEAON_WDT_CFG_SCALE_MASK) {
163 		device_printf(sc->dev, "Time interval too large for WDT\n");
164 		*err = EINVAL;
165 		goto exit;
166 	}
167 
168 	/* Program WDT */
169 	val = FEAON_READ_4(sc, FEAON_WDT_CFG);
170 	val &= ~FEAON_WDT_CFG_SCALE_MASK;
171 	val |= scale | FEAON_WDT_CFG_RST_EN | FEAON_WDT_CFG_EN_ALWAYS |
172 	    FEAON_WDT_CFG_ZERO_CMP;
173 
174 	FEAON_WDT_WRITE_4(sc, FEAON_WDT_CMP, (uint32_t)time);
175 	FEAON_WDT_WRITE_4(sc, FEAON_WDT_CFG, val);
176 
177 exit:
178 	FEAON_UNLOCK(sc);
179 }
180 
181 static int
182 feaon_rtc_settime(device_t dev, struct timespec *ts)
183 {
184 	struct feaon_softc *sc;
185 	uint64_t time;
186 	uint32_t cfg;
187 	uint8_t scale;
188 
189 	scale = 0;
190 	sc = device_get_softc(dev);
191 
192 	FEAON_LOCK(sc);
193 
194 	clock_dbgprint_ts(dev, CLOCK_DBG_WRITE, ts);
195 
196 	time = ts->tv_sec * FEAON_RTC_TIMEBASE_FREQ;
197 
198 	/* Find an appropriate scale */
199 	while (time >= 0xFFFFFFFFFFFFLL) {
200 		scale++;
201 		time >>= 1;
202 	}
203 	if (scale > FEAON_RTC_CFG_SCALE_MASK) {
204 		device_printf(sc->dev, "Time value too large for RTC\n");
205 		FEAON_UNLOCK(sc);
206 		return (1);
207 	}
208 	cfg = FEAON_READ_4(sc, FEAON_RTC_CFG) & ~FEAON_RTC_CFG_SCALE_MASK;
209 	cfg |= scale;
210 
211 	FEAON_WRITE_4(sc, FEAON_RTC_CFG, cfg);
212 	FEAON_WRITE_4(sc, FEAON_RTC_LO, (uint32_t)time);
213 	FEAON_WRITE_4(sc, FEAON_RTC_HI, (time >> 32) & FEAON_RTC_HI_MASK);
214 
215 	FEAON_UNLOCK(sc);
216 
217 	return (0);
218 }
219 
220 static int
221 feaon_rtc_gettime(device_t dev, struct timespec *ts)
222 {
223 	struct feaon_softc *sc;
224 	uint64_t time;
225 	uint8_t scale;
226 
227 	sc = device_get_softc(dev);
228 	FEAON_LOCK(sc);
229 
230 	time = FEAON_READ_4(sc, FEAON_RTC_LO);
231 	time |= ((uint64_t)FEAON_READ_4(sc, FEAON_RTC_HI)) << 32;
232 
233 	scale = FEAON_READ_4(sc, FEAON_RTC_CFG) & FEAON_RTC_CFG_SCALE_MASK;
234 	time <<= scale;
235 
236 	ts->tv_sec = time / FEAON_RTC_TIMEBASE_FREQ;
237 	ts->tv_nsec = (time % FEAON_RTC_TIMEBASE_FREQ) *
238 	    (NANOSECOND / FEAON_RTC_TIMEBASE_FREQ);
239 
240 	clock_dbgprint_ts(dev, CLOCK_DBG_READ, ts);
241 
242 	FEAON_UNLOCK(sc);
243 
244 	return (0);
245 }
246 
247 static int
248 feaon_attach(device_t dev)
249 {
250 	struct feaon_softc *sc;
251 	int err;
252 
253 	sc = device_get_softc(dev);
254 	sc->dev = dev;
255 
256 	/* Mutex setup */
257 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
258 
259 	/* Resource setup */
260 	sc->reg_rid = 0;
261 	if ((sc->reg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
262 	    &sc->reg_rid, RF_ACTIVE)) == NULL) {
263 		device_printf(dev, "Error allocating memory resource.\n");
264 		err = ENXIO;
265 		goto error;
266 	}
267 
268 	/* Enable RTC */
269 	clock_register(dev, 1000000); /* 1 sec resolution */
270 	FEAON_LOCK(sc);
271 	FEAON_WRITE_4(sc, FEAON_RTC_CFG, FEAON_RTC_CFG_EN);
272 	FEAON_UNLOCK(sc);
273 
274 	/* Register WDT */
275 	sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, feaon_wdt_event, sc, 0);
276 
277 	return (0);
278 
279 error:
280 	bus_release_resource(dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg_res);
281 	mtx_destroy(&sc->mtx);
282 	return (err);
283 }
284 
285 static int
286 feaon_probe(device_t dev)
287 {
288 
289 	if (!ofw_bus_status_okay(dev))
290 		return (ENXIO);
291 
292 	if (!ofw_bus_is_compatible(dev, "sifive,aon0"))
293 		return (ENXIO);
294 
295 	device_set_desc(dev, "SiFive FE310 Always-On Controller");
296 	return (BUS_PROBE_DEFAULT);
297 }
298 
299 static device_method_t feaon_methods[] = {
300 	DEVMETHOD(device_probe, feaon_probe),
301 	DEVMETHOD(device_attach, feaon_attach),
302 
303 	/* RTC */
304 	DEVMETHOD(clock_gettime, feaon_rtc_gettime),
305 	DEVMETHOD(clock_settime, feaon_rtc_settime),
306 
307 	DEVMETHOD_END
308 };
309 
310 static driver_t feaon_driver = {
311 	"fe310aon",
312 	feaon_methods,
313 	sizeof(struct feaon_softc)
314 };
315 
316 static devclass_t feaon_devclass;
317 
318 DRIVER_MODULE(fe310aon, simplebus, feaon_driver, feaon_devclass, 0, 0);
319