Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/riscv/sifive/fe310_aon.c
39482 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/clock.h>
36
#include <sys/eventhandler.h>
37
#include <sys/kernel.h>
38
#include <sys/lock.h>
39
#include <sys/module.h>
40
#include <sys/mutex.h>
41
#include <sys/rman.h>
42
#include <sys/sdt.h>
43
#include <sys/time.h>
44
#include <sys/timespec.h>
45
#include <sys/timex.h>
46
#include <sys/watchdog.h>
47
48
#include <dev/ofw/ofw_bus.h>
49
#include <dev/ofw/ofw_bus_subr.h>
50
51
#include <machine/bus.h>
52
#include <machine/clock.h>
53
#include <machine/intr.h>
54
#include <machine/resource.h>
55
56
#include "clock_if.h"
57
58
#define FEAON_AON_WDT_BASE 0x0
59
#define FEAON_AON_RTC_BASE 0x40
60
#define FEAON_AON_CLKCFG_BASE 0x70
61
#define FEAON_AON_BACKUP_BASE 0x80
62
#define FEAON_AON_PMU_BASE 0x100
63
64
/* Watchdog specific */
65
#define FEAON_WDT_CFG 0x0
66
#define FEAON_WDT_COUNT 0x8
67
#define FEAON_WDT_DOGS 0x10
68
#define FEAON_WDT_FEED 0x18
69
#define FEAON_WDT_KEY 0x1C
70
#define FEAON_WDT_CMP 0x20
71
72
#define FEAON_WDT_CFG_SCALE_MASK 0xF
73
#define FEAON_WDT_CFG_RST_EN (1 << 8)
74
#define FEAON_WDT_CFG_ZERO_CMP (1 << 9)
75
#define FEAON_WDT_CFG_EN_ALWAYS (1 << 12)
76
#define FEAON_WDT_CFG_EN_CORE_AWAKE (1 << 13)
77
#define FEAON_WDT_CFG_IP (1 << 28)
78
79
#define FEAON_WDT_CMP_MASK 0xFFFF
80
81
#define FEAON_WDT_FEED_FOOD 0xD09F00D
82
83
#define FEAON_WDT_KEY_UNLOCK 0x51F15E
84
85
#define FEAON_WDT_TIMEBASE_FREQ 31250
86
#define FEAON_WDT_TIMEBASE_RATIO (NANOSECOND / FEAON_WDT_TIMEBASE_FREQ)
87
88
/* Real-time clock specific */
89
#define FEAON_RTC_CFG 0x40
90
#define FEAON_RTC_LO 0x48
91
#define FEAON_RTC_HI 0x4C
92
#define FEAON_RTC_CMP 0x60
93
94
#define FEAON_RTC_CFG_SCALE_MASK 0xF
95
#define FEAON_RTC_CFG_EN (1 << 12)
96
#define FEAON_RTC_CFG_IP (1 << 28)
97
98
#define FEAON_RTC_HI_MASK 0xFFFF
99
100
#define FEAON_RTC_TIMEBASE_FREQ 31250LL
101
102
#define FEAON_LOCK(sc) mtx_lock(&(sc)->mtx)
103
#define FEAON_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
104
#define FEAON_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
105
#define FEAON_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
106
107
#define FEAON_READ_4(sc, reg) bus_read_4(sc->reg_res, reg)
108
#define FEAON_WRITE_4(sc, reg, val) bus_write_4(sc->reg_res, reg, val)
109
110
#define FEAON_WDT_WRITE_4(sc, reg, val) do { \
111
FEAON_WRITE_4(sc, (FEAON_WDT_KEY), (FEAON_WDT_KEY_UNLOCK)); \
112
FEAON_WRITE_4(sc, reg, val); \
113
} while (0)
114
115
struct feaon_softc {
116
device_t dev;
117
struct mtx mtx;
118
119
/* Resources */
120
int reg_rid;
121
struct resource *reg_res;
122
123
/* WDT */
124
eventhandler_tag ev_tag;
125
};
126
127
static void
128
feaon_wdt_event(void *arg, unsigned int cmd, int *err)
129
{
130
struct feaon_softc *sc;
131
uint32_t scale, val;
132
uint64_t time;
133
134
sc = (struct feaon_softc *)arg;
135
FEAON_LOCK(sc);
136
137
/* First feed WDT */
138
FEAON_WDT_WRITE_4(sc, FEAON_WDT_FEED, FEAON_WDT_FEED_FOOD);
139
140
if ((cmd & WD_INTERVAL) == WD_TO_NEVER) {
141
/* Disable WDT */
142
val = FEAON_READ_4(sc, FEAON_WDT_CFG);
143
val &= ~(FEAON_WDT_CFG_EN_ALWAYS | FEAON_WDT_CFG_EN_CORE_AWAKE);
144
FEAON_WDT_WRITE_4(sc, FEAON_WDT_CFG, val);
145
goto exit;
146
}
147
148
/* Calculate time in WDT frequency */
149
time = 1LL << (cmd & WD_INTERVAL);
150
time /= FEAON_WDT_TIMEBASE_RATIO;
151
152
/* Fit time in CMP register with scale */
153
scale = 0;
154
while (time > FEAON_WDT_CMP_MASK) {
155
time >>= 1;
156
scale++;
157
}
158
159
if (time > FEAON_WDT_CMP_MASK || scale > FEAON_WDT_CFG_SCALE_MASK) {
160
device_printf(sc->dev, "Time interval too large for WDT\n");
161
*err = EINVAL;
162
goto exit;
163
}
164
165
/* Program WDT */
166
val = FEAON_READ_4(sc, FEAON_WDT_CFG);
167
val &= ~FEAON_WDT_CFG_SCALE_MASK;
168
val |= scale | FEAON_WDT_CFG_RST_EN | FEAON_WDT_CFG_EN_ALWAYS |
169
FEAON_WDT_CFG_ZERO_CMP;
170
171
FEAON_WDT_WRITE_4(sc, FEAON_WDT_CMP, (uint32_t)time);
172
FEAON_WDT_WRITE_4(sc, FEAON_WDT_CFG, val);
173
174
exit:
175
FEAON_UNLOCK(sc);
176
}
177
178
static int
179
feaon_rtc_settime(device_t dev, struct timespec *ts)
180
{
181
struct feaon_softc *sc;
182
uint64_t time;
183
uint32_t cfg;
184
uint8_t scale;
185
186
scale = 0;
187
sc = device_get_softc(dev);
188
189
FEAON_LOCK(sc);
190
191
clock_dbgprint_ts(dev, CLOCK_DBG_WRITE, ts);
192
193
time = ts->tv_sec * FEAON_RTC_TIMEBASE_FREQ;
194
195
/* Find an appropriate scale */
196
while (time >= 0xFFFFFFFFFFFFLL) {
197
scale++;
198
time >>= 1;
199
}
200
if (scale > FEAON_RTC_CFG_SCALE_MASK) {
201
device_printf(sc->dev, "Time value too large for RTC\n");
202
FEAON_UNLOCK(sc);
203
return (1);
204
}
205
cfg = FEAON_READ_4(sc, FEAON_RTC_CFG) & ~FEAON_RTC_CFG_SCALE_MASK;
206
cfg |= scale;
207
208
FEAON_WRITE_4(sc, FEAON_RTC_CFG, cfg);
209
FEAON_WRITE_4(sc, FEAON_RTC_LO, (uint32_t)time);
210
FEAON_WRITE_4(sc, FEAON_RTC_HI, (time >> 32) & FEAON_RTC_HI_MASK);
211
212
FEAON_UNLOCK(sc);
213
214
return (0);
215
}
216
217
static int
218
feaon_rtc_gettime(device_t dev, struct timespec *ts)
219
{
220
struct feaon_softc *sc;
221
uint64_t time;
222
uint8_t scale;
223
224
sc = device_get_softc(dev);
225
FEAON_LOCK(sc);
226
227
time = FEAON_READ_4(sc, FEAON_RTC_LO);
228
time |= ((uint64_t)FEAON_READ_4(sc, FEAON_RTC_HI)) << 32;
229
230
scale = FEAON_READ_4(sc, FEAON_RTC_CFG) & FEAON_RTC_CFG_SCALE_MASK;
231
time <<= scale;
232
233
ts->tv_sec = time / FEAON_RTC_TIMEBASE_FREQ;
234
ts->tv_nsec = (time % FEAON_RTC_TIMEBASE_FREQ) *
235
(NANOSECOND / FEAON_RTC_TIMEBASE_FREQ);
236
237
clock_dbgprint_ts(dev, CLOCK_DBG_READ, ts);
238
239
FEAON_UNLOCK(sc);
240
241
return (0);
242
}
243
244
static int
245
feaon_attach(device_t dev)
246
{
247
struct feaon_softc *sc;
248
int err;
249
250
sc = device_get_softc(dev);
251
sc->dev = dev;
252
253
/* Mutex setup */
254
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
255
256
/* Resource setup */
257
sc->reg_rid = 0;
258
if ((sc->reg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
259
&sc->reg_rid, RF_ACTIVE)) == NULL) {
260
device_printf(dev, "Error allocating memory resource.\n");
261
err = ENXIO;
262
goto error;
263
}
264
265
/* Enable RTC */
266
clock_register(dev, 1000000); /* 1 sec resolution */
267
FEAON_LOCK(sc);
268
FEAON_WRITE_4(sc, FEAON_RTC_CFG, FEAON_RTC_CFG_EN);
269
FEAON_UNLOCK(sc);
270
271
/* Register WDT */
272
sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, feaon_wdt_event, sc, 0);
273
274
return (0);
275
276
error:
277
bus_release_resource(dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg_res);
278
mtx_destroy(&sc->mtx);
279
return (err);
280
}
281
282
static int
283
feaon_probe(device_t dev)
284
{
285
286
if (!ofw_bus_status_okay(dev))
287
return (ENXIO);
288
289
if (!ofw_bus_is_compatible(dev, "sifive,aon0"))
290
return (ENXIO);
291
292
device_set_desc(dev, "SiFive FE310 Always-On Controller");
293
return (BUS_PROBE_DEFAULT);
294
}
295
296
static device_method_t feaon_methods[] = {
297
DEVMETHOD(device_probe, feaon_probe),
298
DEVMETHOD(device_attach, feaon_attach),
299
300
/* RTC */
301
DEVMETHOD(clock_gettime, feaon_rtc_gettime),
302
DEVMETHOD(clock_settime, feaon_rtc_settime),
303
304
DEVMETHOD_END
305
};
306
307
static driver_t feaon_driver = {
308
"fe310aon",
309
feaon_methods,
310
sizeof(struct feaon_softc)
311
};
312
313
DRIVER_MODULE(fe310aon, simplebus, feaon_driver, 0, 0);
314
315