1 /* 2 * cortex-a clocks; excludes tegra 2 SoC clocks 3 * 4 * cortex-a processors include private `global' and local timers 5 * at soc.scu + 0x200 (global) and + 0x600 (local). 6 * the global timer is a single count-up timer shared by all cores 7 * but with per-cpu comparator and auto-increment registers. 8 * a local count-down timer can be used as a watchdog. 9 * 10 * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case) 11 * but it's unsuitable as our source of fastticks, because it stops advancing 12 * when the cpu is suspended by WFI. 13 */ 14 #include "u.h" 15 #include "../port/lib.h" 16 #include "mem.h" 17 #include "dat.h" 18 #include "fns.h" 19 #include "arm.h" 20 21 enum { 22 Debug = 0, 23 24 Basetickfreq = Mhz, /* soc.µs rate in Hz */ 25 /* the local timers seem to run at half the expected rate */ 26 Clockfreqbase = 250*Mhz / 2, /* private timer rate (PERIPHCLK/2) */ 27 Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */ 28 29 MinPeriod = Tcycles / 100, 30 MaxPeriod = Tcycles, 31 32 Dogtimeout = Dogsectimeout * Clockfreqbase, 33 }; 34 35 typedef struct Ltimer Ltimer; 36 typedef struct Pglbtmr Pglbtmr; 37 typedef struct Ploctmr Ploctmr; 38 39 /* 40 * cortex-a private-intr local timer registers. all cpus see their 41 * own local timers at the same base address. 42 */ 43 struct Ltimer { 44 ulong load; /* new value + 1 */ 45 ulong cnt; /* counts down */ 46 ulong ctl; 47 ulong isr; 48 49 /* watchdog only */ 50 ulong wdrst; 51 ulong wddis; /* wo */ 52 53 ulong _pad0[2]; 54 }; 55 struct Ploctmr { 56 Ltimer loc; 57 Ltimer wd; 58 }; 59 60 enum { 61 /* ctl bits */ 62 Tmrena = 1<<0, /* timer enabled */ 63 Wdogena = Tmrena, /* watchdog enabled */ 64 Xreload = 1<<1, /* reload on intr; periodic interrupts */ 65 Tintena = 1<<2, /* enable irq 29 at cnt==0 (30 for watchdog) */ 66 Wdog = 1<<3, /* watchdog, not timer, mode */ 67 Xsclrshift = 8, 68 Xsclrmask = MASK(8), 69 70 /* isr bits */ 71 Xisrclk = 1<<0, /* write to clear */ 72 73 /* wdrst bits */ 74 Wdrst = 1<<0, 75 76 /* wddis values */ 77 Wdon = 1, 78 Wdoff1 = 0x12345678, /* send these two to switch to timer mode */ 79 Wdoff2 = 0x87654321, 80 }; 81 82 /* cortex-a private-intr globl timer registers */ 83 struct Pglbtmr { 84 ulong cnt[2]; /* counts up; little-endian uvlong */ 85 ulong ctl; 86 ulong isr; 87 ulong cmp[2]; /* little-endian uvlong */ 88 ulong inc; 89 }; 90 91 enum { 92 /* unique ctl bits (otherwise see X* above) */ 93 Gcmp = 1<<1, 94 // Gtintena= 1<<2, /* enable irq 27 */ 95 Gincr = 1<<3, 96 }; 97 98 /* 99 * until 5[cal] inline vlong ops, avoid them where possible, 100 * they are currently slow function calls. 101 */ 102 typedef union Counter Counter; 103 union Counter { 104 uvlong uvl; 105 struct { /* little-endian */ 106 ulong low; 107 ulong high; 108 }; 109 }; 110 111 static int fired; 112 static int ticking[MAXMACH]; 113 114 /* no lock is needed to update our local timer. splhi keeps it tight. */ 115 static void 116 setltimer(Ltimer *tn, ulong ticks) 117 { 118 int s; 119 120 assert(ticks <= Clockfreqbase); 121 s = splhi(); 122 tn->load = ticks - 1; 123 coherence(); 124 tn->ctl = Tmrena | Tintena | Xreload; 125 coherence(); 126 splx(s); 127 } 128 129 static void 130 ckstuck(int cpu, long myticks, long histicks) 131 { 132 if (labs(histicks - myticks) > HZ) { 133 // iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); " 134 // "apparently stopped\n", 135 // cpu, histicks, myticks, MACHP(0)->ticks); 136 if (!ticking[cpu]) 137 panic("cpu%d: clock not interrupting", cpu); 138 } 139 } 140 141 static void 142 mpclocksanity(void) 143 { 144 int cpu, mycpu; 145 long myticks, histicks; 146 147 if (conf.nmach <= 1 || active.exiting || navailcpus == 0) 148 return; 149 150 mycpu = m->machno; 151 myticks = m->ticks; 152 if (myticks == HZ) 153 ticking[mycpu] = 1; 154 155 if (myticks < 5*HZ) 156 return; 157 158 for (cpu = 0; cpu < navailcpus; cpu++) { 159 if (cpu == mycpu) 160 continue; 161 histicks = MACHP(cpu)->ticks; 162 if (myticks == 5*HZ || histicks > 1) 163 ckstuck(cpu, myticks, histicks); 164 } 165 } 166 167 static void 168 clockintr(Ureg* ureg, void *arg) 169 { 170 Ltimer *wd, *tn; 171 Ploctmr *lt; 172 173 lt = (Ploctmr *)arg; 174 tn = <->loc; 175 tn->isr = Xisrclk; 176 coherence(); 177 178 timerintr(ureg, 0); 179 180 #ifdef watchdog_not_bloody_useless 181 /* appease the dogs */ 182 wd = <->wd; 183 if (wd->cnt == 0 && 184 (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena)) 185 panic("cpu%d: zero watchdog count but no system reset", 186 m->machno); 187 wd->load = Dogtimeout - 1; 188 coherence(); 189 #endif 190 SET(wd); USED(wd); 191 tegclockintr(); 192 193 mpclocksanity(); 194 } 195 196 void 197 clockprod(Ureg *ureg) 198 { 199 Ltimer *tn; 200 201 timerintr(ureg, 0); 202 tegclockintr(); 203 if (m->machno != 0) { /* cpu1 gets stuck */ 204 tn = &((Ploctmr *)soc.loctmr)->loc; 205 setltimer(tn, Tcycles); 206 } 207 } 208 209 static void 210 clockreset(Ltimer *tn) 211 { 212 if (probeaddr((uintptr)tn) < 0) 213 panic("no clock at %#p", tn); 214 tn->ctl = 0; 215 coherence(); 216 } 217 218 void 219 watchdogoff(Ltimer *wd) 220 { 221 wd->ctl &= ~Wdogena; 222 coherence(); 223 wd->wddis = Wdoff1; 224 coherence(); 225 wd->wddis = Wdoff2; 226 coherence(); 227 } 228 229 /* clear any pending watchdog intrs or causes */ 230 void 231 wdogclrintr(Ltimer *wd) 232 { 233 #ifdef watchdog_not_bloody_useless 234 wd->isr = Xisrclk; 235 coherence(); 236 wd->wdrst = Wdrst; 237 coherence(); 238 #endif 239 USED(wd); 240 } 241 242 /* 243 * stop clock interrupts on this cpu and disable the local watchdog timer, 244 * and, if on cpu0, shutdown the shared tegra2 watchdog timer. 245 */ 246 void 247 clockshutdown(void) 248 { 249 Ploctmr *lt; 250 251 lt = (Ploctmr *)soc.loctmr; 252 clockreset(<->loc); 253 watchdogoff(<->wd); 254 255 tegclockshutdown(); 256 } 257 258 enum { 259 Instrs = 10*Mhz, 260 }; 261 262 /* we assume that perfticks are microseconds */ 263 static long 264 issue1loop(void) 265 { 266 register int i; 267 long st; 268 269 i = Instrs; 270 st = perfticks(); 271 do { 272 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 273 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 274 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 275 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 276 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 277 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 278 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 279 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 280 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i; 281 --i; --i; --i; --i; --i; --i; --i; --i; --i; 282 } while(--i >= 0); 283 return perfticks() - st; 284 } 285 286 static long 287 issue2loop(void) 288 { 289 register int i, j; 290 long st; 291 292 i = Instrs / 2; /* j gets half the decrements */ 293 j = 0; 294 st = perfticks(); 295 do { 296 --j; --i; --j; --i; --j; --i; --j; --i; --j; 297 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 298 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 299 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 300 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 301 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 302 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 303 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 304 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 305 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 306 307 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 308 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 309 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 310 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 311 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 312 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 313 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 314 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 315 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 316 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 317 } while(--i >= 0); 318 return perfticks() - st; 319 } 320 321 /* estimate instructions/s. */ 322 static void 323 guessmips(long (*loop)(void), char *lab) 324 { 325 int s; 326 long tcks; 327 328 do { 329 s = splhi(); 330 tcks = loop(); 331 splx(s); 332 if (tcks < 0) 333 iprint("again..."); 334 } while (tcks < 0); 335 /* 336 * Instrs instructions took tcks ticks @ Basetickfreq Hz. 337 * round the result. 338 */ 339 s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000; 340 if (Debug) 341 iprint("%ud mips (%s-issue)", s, lab); 342 USED(s); 343 } 344 345 void 346 wdogintr(Ureg *, void *ltmr) 347 { 348 #ifdef watchdog_not_bloody_useless 349 Ltimer *wd; 350 351 wd = ltmr; 352 fired++; 353 wdogclrintr(wd); 354 #endif 355 USED(ltmr); 356 } 357 358 static void 359 ckcounting(Ltimer *lt) 360 { 361 ulong old; 362 363 old = lt->cnt; 364 if (old == lt->cnt) 365 delay(1); 366 if (old == lt->cnt) 367 panic("cpu%d: watchdog timer not counting down", m->machno); 368 } 369 370 /* test fire with interrupt to see that it's working */ 371 static void 372 ckwatchdog(Ltimer *wd) 373 { 374 #ifdef watchdog_not_bloody_useless 375 int s; 376 377 fired = 0; 378 wd->load = Tcycles - 1; 379 coherence(); 380 /* Tintena is supposed to be ignored in watchdog mode */ 381 wd->ctl |= Wdogena | Tintena; 382 coherence(); 383 384 ckcounting(wd); 385 386 s = spllo(); 387 delay(2 * 1000/HZ); 388 splx(s); 389 if (!fired) 390 /* useless local watchdog */ 391 iprint("cpu%d: local watchdog failed to interrupt\n", m->machno); 392 /* clean up */ 393 wd->ctl &= ~Wdogena; 394 coherence(); 395 #endif 396 USED(wd); 397 } 398 399 static void 400 startwatchdog(void) 401 { 402 #ifdef watchdog_not_bloody_useless 403 Ltimer *wd; 404 Ploctmr *lt; 405 406 lt = (Ploctmr *)soc.loctmr; 407 wd = <->wd; 408 watchdogoff(wd); 409 wdogclrintr(wd); 410 irqenable(Wdtmrirq, wdogintr, wd, "watchdog"); 411 412 ckwatchdog(wd); 413 414 /* set up for normal use, causing reset */ 415 wd->ctl &= ~Tintena; /* reset, don't interrupt */ 416 coherence(); 417 wd->ctl |= Wdog; 418 coherence(); 419 wd->load = Dogtimeout - 1; 420 coherence(); 421 wd->ctl |= Wdogena; 422 coherence(); 423 424 ckcounting(wd); 425 #endif 426 } 427 428 static void 429 clock0init(Ltimer *tn) 430 { 431 int s; 432 ulong old, fticks; 433 434 /* 435 * calibrate fastclock 436 */ 437 s = splhi(); 438 tn->load = ~0ul >> 1; 439 coherence(); 440 tn->ctl = Tmrena; 441 coherence(); 442 443 old = perfticks(); 444 fticks = tn->cnt; 445 delay(1); 446 fticks = abs(tn->cnt - fticks); 447 old = perfticks() - old; 448 splx(s); 449 if (Debug) 450 iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n", 451 m->machno, fticks, old, (fticks + old/2 - 1) / old); 452 USED(fticks, old); 453 454 if (Debug) 455 iprint("cpu%d: ", m->machno); 456 guessmips(issue1loop, "single"); 457 if (Debug) 458 iprint(", "); 459 guessmips(issue2loop, "dual"); 460 if (Debug) 461 iprint("\n"); 462 463 /* 464 * m->delayloop should be the number of delay loop iterations 465 * needed to consume 1 ms. 2 is instr'ns in the delay loop. 466 */ 467 m->delayloop = m->cpuhz / (1000 * 2); 468 // iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop); 469 470 tegclock0init(); 471 } 472 473 /* 474 * the local timer is the interrupting timer and does not 475 * participate in measuring time. It is initially set to HZ. 476 */ 477 void 478 clockinit(void) 479 { 480 ulong old; 481 Ltimer *tn; 482 Ploctmr *lt; 483 484 clockshutdown(); 485 486 /* turn my cycle counter on */ 487 cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31); 488 489 /* turn all my counters on and clear my cycle counter */ 490 cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1); 491 492 /* let users read my cycle counter directly */ 493 cpwrsc(0, CpCLD, CpCLDuser, CpCLDenapmnc, 1); 494 495 /* verify µs counter sanity */ 496 tegclockinit(); 497 498 lt = (Ploctmr *)soc.loctmr; 499 tn = <->loc; 500 if (m->machno == 0) 501 irqenable(Loctmrirq, clockintr, lt, "clock"); 502 else 503 intcunmask(Loctmrirq); 504 505 /* 506 * verify sanity of local timer 507 */ 508 tn->load = Clockfreqbase / 1000; 509 tn->isr = Xisrclk; 510 coherence(); 511 tn->ctl = Tmrena; 512 coherence(); 513 514 old = tn->cnt; 515 delay(5); 516 /* m->ticks won't be incremented here because timersinit hasn't run. */ 517 if (tn->cnt == old) 518 panic("cpu%d: clock not ticking at all", m->machno); 519 else if ((long)tn->cnt > 0) 520 panic("cpu%d: clock ticking slowly", m->machno); 521 522 if (m->machno == 0) 523 clock0init(tn); 524 525 /* if pci gets stuck, maybe one of the many watchdogs will nuke us. */ 526 startwatchdog(); 527 528 /* 529 * desynchronize the processor clocks so that they all don't 530 * try to resched at the same time. 531 */ 532 delay(m->machno*2); 533 setltimer(tn, Tcycles); 534 } 535 536 /* our fastticks are at 1MHz (Basetickfreq), so the conversion is trivial. */ 537 ulong 538 µs(void) 539 { 540 return fastticks2us(fastticks(nil)); 541 } 542 543 /* Tval is supposed to be in fastticks units. */ 544 void 545 timerset(Tval next) 546 { 547 int s; 548 long offset; 549 Ltimer *tn; 550 551 tn = &((Ploctmr *)soc.loctmr)->loc; 552 s = splhi(); 553 offset = fastticks2us(next - fastticks(nil)); 554 /* offset is now in µs (MHz); convert to Clockfreqbase Hz. */ 555 offset *= Clockfreqbase / Mhz; 556 if(offset < MinPeriod) 557 offset = MinPeriod; 558 else if(offset > MaxPeriod) 559 offset = MaxPeriod; 560 561 setltimer(tn, offset); 562 splx(s); 563 } 564 565 static ulong 566 cpucycles(void) /* cpu clock rate, except when waiting for intr (unused) */ 567 { 568 ulong v; 569 570 /* reads 32-bit cycle counter (counting up) */ 571 // v = cprdsc(0, CpCLD, CpCLDcyc, 0); 572 v = getcyc(); /* fast asm */ 573 /* keep it non-negative; prevent m->fastclock ever going to 0 */ 574 return v == 0? 1: v; 575 } 576 577 long 578 lcycles(void) 579 { 580 return perfticks(); 581 } 582 583 uvlong 584 fastticks(uvlong *hz) 585 { 586 vlong fastticks; 587 Counter now; 588 589 if(hz) 590 *hz = Basetickfreq; 591 /* avoid reentry on interrupt or trap, to prevent recursion */ 592 ilock(&m->clklck); 593 fastticks = m->fastclock; 594 if (m->ticks > HZ/10 && fastticks == 0) 595 panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux", 596 m->ticks, fastticks); 597 598 now.uvl = fastticks; 599 now.low = perfticks(); 600 if(now.uvl < fastticks) /* low bits must have wrapped */ 601 now.high++; 602 m->fastclock = now.uvl; 603 iunlock(&m->clklck); 604 return now.uvl; 605 } 606 607 void 608 microdelay(int l) 609 { 610 for (l = l * (vlong)m->delayloop / 1000; --l >= 0; ) 611 ; 612 } 613 614 void 615 delay(int l) 616 { 617 int i, d; 618 619 d = m->delayloop; 620 while(--l >= 0) 621 for (i = d; --i >= 0; ) 622 ; 623 } 624