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
rtc_pl031_time(void)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
rtc_pl031_update_mis(struct rtc_pl031_softc * sc)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
rtc_pl031_next_match_ticks(struct rtc_pl031_softc * sc)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
rtc_pl031_next_timer_msecs(struct rtc_pl031_softc * sc)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
rtc_pl031_update_timer(struct rtc_pl031_softc * sc)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
rtc_pl031_tick(struct rtc_pl031_softc * sc,bool from_timer)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
rtc_pl031_callback(int fd __unused,enum ev_type type __unused,void * param)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
rtc_pl031_write(struct rtc_pl031_softc * sc,int offset,uint32_t value)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
rtc_pl031_read(struct rtc_pl031_softc * sc,int offset)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 *
rtc_pl031_init(rtc_pl031_intr_func_t intr_assert,rtc_pl031_intr_func_t intr_deassert,void * arg)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