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