xref: /freebsd/usr.sbin/bhyve/rtc_pl031.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 
30 #include <assert.h>
31 #include <limits.h>
32 #include <pthread.h>
33 #include <stdlib.h>
34 #include <time.h>
35 
36 #include "config.h"
37 #include "mevent.h"
38 #include "rtc_pl031.h"
39 
40 #define	RTCDR		0x000
41 #define	RTCMR		0x004
42 #define	RTCLR		0x008
43 #define	RTCCR		0x00C
44 #define	RTCIMSC		0x010
45 #define	RTCRIS		0x014
46 #define	RTCMIS		0x018
47 #define	RTCICR		0x01C
48 
49 #define	RTCPeriphID0	0xFE0
50 #define	RTCPeriphID1	0xFE4
51 #define	RTCPeriphID2	0xFE8
52 #define	RTCPeriphID3	0xFEC
53 #define	 _RTCPeriphID_VAL	0x00141031
54 #define	 RTCPeriphID_VAL(_n)	((_RTCPeriphID_VAL >> (8 * (_n))) & 0xff)
55 
56 #define	RTCCellID0	0xFF0
57 #define	RTCCellID1	0xFF4
58 #define	RTCCellID2	0xFF8
59 #define	RTCCellID3	0xFFC
60 #define	 _RTCCellID_VAL		0xb105f00d
61 #define	 RTCCellID_VAL(_n)	((_RTCCellID_VAL >> (8 * (_n))) & 0xff)
62 
63 struct rtc_pl031_softc {
64 	pthread_mutex_t		mtx;
65 
66 	time_t			last_tick;
67 	uint32_t		dr;
68 	uint32_t		mr;
69 	uint32_t		lr;
70 	uint8_t			imsc;
71 	uint8_t			ris;
72 	uint8_t			prev_mis;
73 
74 	struct mevent		*mevp;
75 
76 	void			*arg;
77 	rtc_pl031_intr_func_t	intr_assert;
78 	rtc_pl031_intr_func_t	intr_deassert;
79 };
80 
81 static void	rtc_pl031_callback(int fd, enum ev_type type, void *param);
82 
83 /*
84  * Returns the current RTC time as number of seconds since 00:00:00 Jan 1, 1970
85  */
86 static time_t
87 rtc_pl031_time(void)
88 {
89 	struct tm tm;
90 	time_t t;
91 
92 	time(&t);
93 	if (get_config_bool_default("rtc.use_localtime", false)) {
94 		localtime_r(&t, &tm);
95 		t = timegm(&tm);
96 	}
97 	return (t);
98 }
99 
100 static void
101 rtc_pl031_update_mis(struct rtc_pl031_softc *sc)
102 {
103 	uint8_t mis;
104 
105 	mis = sc->ris & sc->imsc;
106 	if (mis == sc->prev_mis)
107 		return;
108 
109 	sc->prev_mis = mis;
110 	if (mis)
111 		(*sc->intr_assert)(sc->arg);
112 	else
113 		(*sc->intr_deassert)(sc->arg);
114 }
115 
116 static uint64_t
117 rtc_pl031_next_match_ticks(struct rtc_pl031_softc *sc)
118 {
119 	uint32_t ticks;
120 
121 	ticks = sc->mr - sc->dr;
122 	if (ticks == 0)
123 		return ((uint64_t)1 << 32);
124 
125 	return (ticks);
126 }
127 
128 static int
129 rtc_pl031_next_timer_msecs(struct rtc_pl031_softc *sc)
130 {
131 	uint64_t ticks;
132 
133 	ticks = rtc_pl031_next_match_ticks(sc);
134 	return (MIN(ticks * 1000, INT_MAX));
135 }
136 
137 static void
138 rtc_pl031_update_timer(struct rtc_pl031_softc *sc)
139 {
140 	mevent_timer_update(sc->mevp, rtc_pl031_next_timer_msecs(sc));
141 }
142 
143 static void
144 rtc_pl031_tick(struct rtc_pl031_softc *sc, bool from_timer)
145 {
146 	bool match;
147 	time_t now, ticks;
148 
149 	now = rtc_pl031_time();
150 	ticks = now - sc->last_tick;
151 	match = ticks >= 0 &&
152 	    (uint64_t)ticks >= rtc_pl031_next_match_ticks(sc);
153 	sc->dr += ticks;
154 	sc->last_tick = now;
155 
156 	if (match) {
157 		sc->ris = 1;
158 		rtc_pl031_update_mis(sc);
159 	}
160 
161 	if (match || from_timer || ticks < 0)
162 		rtc_pl031_update_timer(sc);
163 }
164 
165 static void
166 rtc_pl031_callback(int fd __unused, enum ev_type type __unused, void *param)
167 {
168 	struct rtc_pl031_softc *sc = param;
169 
170 	pthread_mutex_lock(&sc->mtx);
171 	rtc_pl031_tick(sc, true);
172 	pthread_mutex_unlock(&sc->mtx);
173 }
174 
175 void
176 rtc_pl031_write(struct rtc_pl031_softc *sc, int offset, uint32_t value)
177 {
178 	pthread_mutex_lock(&sc->mtx);
179 	rtc_pl031_tick(sc, false);
180 	switch (offset) {
181 	case RTCMR:
182 		sc->mr = value;
183 		rtc_pl031_update_timer(sc);
184 		break;
185 	case RTCLR:
186 		sc->lr = value;
187 		sc->dr = sc->lr;
188 		rtc_pl031_update_timer(sc);
189 		break;
190 	case RTCIMSC:
191 		sc->imsc = value & 1;
192 		rtc_pl031_update_mis(sc);
193 		break;
194 	case RTCICR:
195 		sc->ris &= ~value;
196 		rtc_pl031_update_mis(sc);
197 		break;
198 	default:
199 		/* Ignore writes to read-only/unassigned/ID registers */
200 		break;
201 	}
202 	pthread_mutex_unlock(&sc->mtx);
203 }
204 
205 uint32_t
206 rtc_pl031_read(struct rtc_pl031_softc *sc, int offset)
207 {
208 	uint32_t reg;
209 
210 	pthread_mutex_lock(&sc->mtx);
211 	rtc_pl031_tick(sc, false);
212 	switch (offset) {
213 	case RTCDR:
214 		reg = sc->dr;
215 		break;
216 	case RTCMR:
217 		reg = sc->mr;
218 		break;
219 	case RTCLR:
220 		reg = sc->lr;
221 		break;
222 	case RTCCR:
223 		/* RTC enabled from reset */
224 		reg = 1;
225 		break;
226 	case RTCIMSC:
227 		reg = sc->imsc;
228 		break;
229 	case RTCRIS:
230 		reg = sc->ris;
231 		break;
232 	case RTCMIS:
233 		reg = sc->ris & sc->imsc;
234 		break;
235 	case RTCPeriphID0:
236 	case RTCPeriphID1:
237 	case RTCPeriphID2:
238 	case RTCPeriphID3:
239 		reg = RTCPeriphID_VAL(offset - RTCPeriphID0);
240 		break;
241 	case RTCCellID0:
242 	case RTCCellID1:
243 	case RTCCellID2:
244 	case RTCCellID3:
245 		reg = RTCCellID_VAL(offset - RTCCellID0);
246 		break;
247 	default:
248 		/* Return 0 in reads from unasigned registers */
249 		reg = 0;
250 		break;
251 	}
252 	pthread_mutex_unlock(&sc->mtx);
253 
254 	return (reg);
255 }
256 
257 struct rtc_pl031_softc *
258 rtc_pl031_init(rtc_pl031_intr_func_t intr_assert,
259     rtc_pl031_intr_func_t intr_deassert, void *arg)
260 {
261 	struct rtc_pl031_softc *sc;
262 	time_t now;
263 
264 	sc = calloc(1, sizeof(struct rtc_pl031_softc));
265 
266 	pthread_mutex_init(&sc->mtx, NULL);
267 
268 	now = rtc_pl031_time();
269 	sc->dr = now;
270 	sc->last_tick = now;
271 	sc->arg = arg;
272 	sc->intr_assert = intr_assert;
273 	sc->intr_deassert = intr_deassert;
274 
275 	sc->mevp = mevent_add(rtc_pl031_next_timer_msecs(sc), EVF_TIMER,
276 	    rtc_pl031_callback, sc);
277 
278 	return (sc);
279 }
280