1 /* $OpenBSD: agtimer.c,v 1.14 2021/05/16 03:39:27 jsg Exp $ */ 2 /* 3 * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/kernel.h> 23 #include <sys/timetc.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <arm/cpufunc.h> 30 31 #include <dev/ofw/fdt.h> 32 #include <dev/ofw/openfirm.h> 33 34 /* registers */ 35 #define GTIMER_CNTP_CTL_ENABLE (1 << 0) 36 #define GTIMER_CNTP_CTL_IMASK (1 << 1) 37 #define GTIMER_CNTP_CTL_ISTATUS (1 << 2) 38 39 #define TIMER_FREQUENCY 24 * 1000 * 1000 /* ARM core clock */ 40 int32_t agtimer_frequency = TIMER_FREQUENCY; 41 42 u_int agtimer_get_timecount(struct timecounter *); 43 44 static struct timecounter agtimer_timecounter = { 45 .tc_get_timecount = agtimer_get_timecount, 46 .tc_poll_pps = NULL, 47 .tc_counter_mask = 0xffffffff, 48 .tc_frequency = 0, 49 .tc_name = "agtimer", 50 .tc_quality = 0, 51 .tc_priv = NULL, 52 }; 53 54 struct agtimer_pcpu_softc { 55 uint64_t pc_nexttickevent; 56 uint64_t pc_nextstatevent; 57 u_int32_t pc_ticks_err_sum; 58 }; 59 60 struct agtimer_softc { 61 struct device sc_dev; 62 int sc_node; 63 64 struct agtimer_pcpu_softc sc_pstat[MAXCPUS]; 65 66 u_int32_t sc_ticks_err_cnt; 67 u_int32_t sc_ticks_per_second; 68 u_int32_t sc_ticks_per_intr; 69 u_int32_t sc_statvar; 70 u_int32_t sc_statmin; 71 72 #ifdef AMPTIMER_DEBUG 73 struct evcount sc_clk_count; 74 struct evcount sc_stat_count; 75 #endif 76 }; 77 78 int agtimer_match(struct device *, void *, void *); 79 void agtimer_attach(struct device *, struct device *, void *); 80 uint64_t agtimer_readcnt64(void); 81 int agtimer_intr(void *); 82 void agtimer_cpu_initclocks(void); 83 void agtimer_delay(u_int); 84 void agtimer_setstatclockrate(int stathz); 85 void agtimer_set_clockrate(int32_t new_frequency); 86 void agtimer_startclock(void); 87 88 struct cfattach agtimer_ca = { 89 sizeof (struct agtimer_softc), agtimer_match, agtimer_attach 90 }; 91 92 struct cfdriver agtimer_cd = { 93 NULL, "agtimer", DV_DULL 94 }; 95 96 uint64_t 97 agtimer_readcnt64(void) 98 { 99 uint64_t val; 100 101 __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 102 103 return (val); 104 } 105 106 static inline int 107 agtimer_get_ctrl(void) 108 { 109 uint32_t val; 110 111 __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 112 113 return (val); 114 } 115 116 static inline int 117 agtimer_set_ctrl(uint32_t val) 118 { 119 __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 120 [val] "r" (val)); 121 122 cpu_drain_writebuf(); 123 //isb(); 124 125 return (0); 126 } 127 128 static inline int 129 agtimer_set_tval(uint32_t val) 130 { 131 __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 132 [val] "r" (val)); 133 cpu_drain_writebuf(); 134 //isb(); 135 136 return (0); 137 } 138 139 int 140 agtimer_match(struct device *parent, void *cfdata, void *aux) 141 { 142 struct fdt_attach_args *faa = (struct fdt_attach_args *)aux; 143 144 return OF_is_compatible(faa->fa_node, "arm,armv7-timer"); 145 } 146 147 void 148 agtimer_attach(struct device *parent, struct device *self, void *aux) 149 { 150 struct agtimer_softc *sc = (struct agtimer_softc *)self; 151 struct fdt_attach_args *faa = aux; 152 153 sc->sc_node = faa->fa_node; 154 155 agtimer_frequency = 156 OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency); 157 sc->sc_ticks_per_second = agtimer_frequency; 158 159 printf(": %d kHz\n", sc->sc_ticks_per_second / 1000); 160 161 /* XXX: disable user access */ 162 163 #ifdef AMPTIMER_DEBUG 164 evcount_attach(&sc->sc_clk_count, "clock", NULL); 165 evcount_attach(&sc->sc_stat_count, "stat", NULL); 166 #endif 167 168 /* 169 * private timer and interrupts not enabled until 170 * timer configures 171 */ 172 173 arm_clock_register(agtimer_cpu_initclocks, agtimer_delay, 174 agtimer_setstatclockrate, agtimer_startclock); 175 176 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 177 agtimer_timecounter.tc_priv = sc; 178 179 tc_init(&agtimer_timecounter); 180 } 181 182 u_int 183 agtimer_get_timecount(struct timecounter *tc) 184 { 185 return agtimer_readcnt64(); 186 } 187 188 int 189 agtimer_intr(void *frame) 190 { 191 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 192 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 193 uint64_t now; 194 uint64_t nextevent; 195 uint32_t r; 196 #if defined(USE_GTIMER_CMP) 197 int skip = 1; 198 #else 199 int64_t delay; 200 #endif 201 int rc = 0; 202 203 /* 204 * DSR - I know that the tick timer is 64 bits, but the following 205 * code deals with rollover, so there is no point in dealing 206 * with the 64 bit math, just let the 32 bit rollover 207 * do the right thing 208 */ 209 210 now = agtimer_readcnt64(); 211 212 while (pc->pc_nexttickevent <= now) { 213 pc->pc_nexttickevent += sc->sc_ticks_per_intr; 214 pc->pc_ticks_err_sum += sc->sc_ticks_err_cnt; 215 216 /* looping a few times is faster than divide */ 217 while (pc->pc_ticks_err_sum > hz) { 218 pc->pc_nexttickevent += 1; 219 pc->pc_ticks_err_sum -= hz; 220 } 221 222 #ifdef AMPTIMER_DEBUG 223 sc->sc_clk_count.ec_count++; 224 #endif 225 rc = 1; 226 hardclock(frame); 227 } 228 while (pc->pc_nextstatevent <= now) { 229 do { 230 r = random() & (sc->sc_statvar -1); 231 } while (r == 0); /* random == 0 not allowed */ 232 pc->pc_nextstatevent += sc->sc_statmin + r; 233 234 /* XXX - correct nextstatevent? */ 235 #ifdef AMPTIMER_DEBUG 236 sc->sc_stat_count.ec_count++; 237 #endif 238 rc = 1; 239 statclock(frame); 240 } 241 242 if (pc->pc_nexttickevent < pc->pc_nextstatevent) 243 nextevent = pc->pc_nexttickevent; 244 else 245 nextevent = pc->pc_nextstatevent; 246 247 delay = nextevent - now; 248 if (delay < 0) 249 delay = 1; 250 251 agtimer_set_tval(delay); 252 253 return (rc); 254 } 255 256 void 257 agtimer_set_clockrate(int32_t new_frequency) 258 { 259 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 260 261 agtimer_frequency = new_frequency; 262 263 if (sc == NULL) 264 return; 265 266 sc->sc_ticks_per_second = agtimer_frequency; 267 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 268 printf("agtimer0: adjusting clock: new rate %d kHz\n", 269 sc->sc_ticks_per_second / 1000); 270 } 271 272 void 273 agtimer_cpu_initclocks(void) 274 { 275 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 276 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 277 uint32_t reg; 278 uint64_t next; 279 280 stathz = hz; 281 profhz = hz * 10; 282 283 if (sc->sc_ticks_per_second != agtimer_frequency) { 284 agtimer_set_clockrate(agtimer_frequency); 285 } 286 287 agtimer_setstatclockrate(stathz); 288 289 sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz; 290 sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz; 291 pc->pc_ticks_err_sum = 0; 292 293 /* Setup secure and non-secure timer IRQs. */ 294 arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK, 295 agtimer_intr, NULL, "tick"); 296 arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK, 297 agtimer_intr, NULL, "tick"); 298 299 next = agtimer_readcnt64() + sc->sc_ticks_per_intr; 300 pc->pc_nexttickevent = pc->pc_nextstatevent = next; 301 302 reg = agtimer_get_ctrl(); 303 reg &= ~GTIMER_CNTP_CTL_IMASK; 304 reg |= GTIMER_CNTP_CTL_ENABLE; 305 agtimer_set_tval(sc->sc_ticks_per_second); 306 agtimer_set_ctrl(reg); 307 } 308 309 void 310 agtimer_delay(u_int usecs) 311 { 312 u_int32_t clock, oclock, delta, delaycnt; 313 volatile int j; 314 int csec, usec; 315 316 if (usecs > (0x80000000 / agtimer_frequency)) { 317 csec = usecs / 10000; 318 usec = usecs % 10000; 319 320 delaycnt = (agtimer_frequency / 100) * csec + 321 (agtimer_frequency / 100) * usec / 10000; 322 } else { 323 delaycnt = agtimer_frequency * usecs / 1000000; 324 } 325 if (delaycnt <= 1) 326 for (j = 100; j > 0; j--) 327 ; 328 329 oclock = agtimer_readcnt64(); 330 while (1) { 331 for (j = 100; j > 0; j--) 332 ; 333 clock = agtimer_readcnt64(); 334 delta = clock - oclock; 335 if (delta > delaycnt) 336 break; 337 } 338 } 339 340 void 341 agtimer_setstatclockrate(int newhz) 342 { 343 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 344 int minint, statint; 345 int s; 346 347 s = splclock(); 348 349 statint = sc->sc_ticks_per_second / newhz; 350 /* calculate largest 2^n which is smaller that just over half statint */ 351 sc->sc_statvar = 0x40000000; /* really big power of two */ 352 minint = statint / 2 + 100; 353 while (sc->sc_statvar > minint) 354 sc->sc_statvar >>= 1; 355 356 sc->sc_statmin = statint - (sc->sc_statvar >> 1); 357 358 splx(s); 359 360 /* 361 * XXX this allows the next stat timer to occur then it switches 362 * to the new frequency. Rather than switching instantly. 363 */ 364 } 365 366 void 367 agtimer_startclock(void) 368 { 369 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 370 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 371 uint64_t nextevent; 372 uint32_t reg; 373 374 nextevent = agtimer_readcnt64() + sc->sc_ticks_per_intr; 375 pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent; 376 377 reg = agtimer_get_ctrl(); 378 reg &= ~GTIMER_CNTP_CTL_IMASK; 379 reg |= GTIMER_CNTP_CTL_ENABLE; 380 agtimer_set_tval(sc->sc_ticks_per_second); 381 agtimer_set_ctrl(reg); 382 } 383 384 void 385 agtimer_init(void) 386 { 387 uint32_t id_pfr1, cntfrq = 0; 388 389 /* Check for Generic Timer support. */ 390 __asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1)); 391 if ((id_pfr1 & 0x000f0000) == 0x00010000) 392 __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq)); 393 394 if (cntfrq != 0) { 395 agtimer_frequency = cntfrq; 396 arm_clock_register(NULL, agtimer_delay, NULL, NULL); 397 } 398 } 399