1 /*
2 * tegra 2 SoC clocks; excludes cortex-a timers.
3 *
4 * SoC provides these shared clocks:
5 * 4 29-bit count-down `timers' @ 1MHz,
6 * 1 32-bit count-up time-stamp counter @ 1MHz,
7 * and a real-time clock @ 32KHz.
8 * the tegra watchdog (tegra 2 ref man §5.4.1) is tied to timers, not rtc.
9 */
10 #include "u.h"
11 #include "../port/lib.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "arm.h"
16
17 typedef struct Shrdtmr Shrdtmr;
18 typedef struct µscnt µscnt;
19
20 /* tegra2 shared-intr timer registers */
21 struct Shrdtmr { /* 29-bit count-down timer (4); unused */
22 ulong trigger;
23 ulong prescnt;
24 };
25
26 enum {
27 /* trigger bits */
28 Enable = 1u<<31,
29 Periodintr = 1<<30,
30 Countmask = MASK(29),
31
32 /* prescnt bits */
33 Intrclr = 1<<30,
34 /* Countmask is ro */
35 };
36
37 struct µscnt { /* tegra2 shared 32-bit count-up µs counter (1) */
38 ulong cntr;
39 /*
40 * oscillator clock fraction - 1; initially 0xb (11) from u-boot
41 * for 12MHz periphclk.
42 */
43 ulong cfg;
44 uchar _pad0[0x3c - 0x8];
45 ulong freeze;
46 };
47
48 enum {
49 /* cfg bits */
50 Dividendshift = 8,
51 Dividendmask = MASK(8),
52 Divisorshift = 0,
53 Divisormask = MASK(8),
54 };
55
56 void
tegclockintr(void)57 tegclockintr(void)
58 {
59 int junk;
60 Shrdtmr *tmr;
61
62 /* appease the tegra dog */
63 tmr = (Shrdtmr *)soc.tmr[0];
64 junk = tmr->trigger;
65 USED(junk);
66 }
67
68 /*
69 * if on cpu0, shutdown the shared tegra2 watchdog timer.
70 */
71 void
tegclockshutdown(void)72 tegclockshutdown(void)
73 {
74 Shrdtmr *tmr;
75
76 if (m->machno == 0) {
77 tmr = (Shrdtmr *)soc.tmr[0];
78 tmr->prescnt = tmr->trigger = 0;
79 coherence();
80 }
81 }
82
83 void
tegwdogintr(Ureg *,void * v)84 tegwdogintr(Ureg *, void *v)
85 {
86 int junk;
87 Shrdtmr *tmr;
88
89 tmr = (Shrdtmr *)v;
90 tmr->prescnt |= Intrclr;
91 coherence();
92 /* the lousy documentation says we also have to read trigger */
93 junk = tmr->trigger;
94 USED(junk);
95 }
96
97 /* start tegra2 shared watch dog */
98 void
tegclock0init(void)99 tegclock0init(void)
100 {
101 Shrdtmr *tmr;
102
103 tmr = (Shrdtmr *)soc.tmr[0];
104 irqenable(Tn0irq, tegwdogintr, tmr, "tegra watchdog");
105
106 /*
107 * tegra watchdog only fires on the second missed interrupt, thus /2.
108 */
109 tmr->trigger = (Dogsectimeout * Mhz / 2 - 1) | Periodintr | Enable;
110 coherence();
111 }
112
113 /*
114 * µscnt is a freerunning timer (cycle counter); it needs no
115 * initialisation, wraps and does not dispatch interrupts.
116 */
117 void
tegclockinit(void)118 tegclockinit(void)
119 {
120 ulong old;
121 µscnt *µs = (µscnt *)soc.µs;
122
123 /* verify µs counter sanity */
124 assert(µs->cfg == 0xb); /* set by u-boot */
125 old = µs->cntr;
126 delay(1);
127 assert(old != µs->cntr);
128 }
129
130 ulong
perfticks(void)131 perfticks(void) /* MHz rate, assumed by timing loops */
132 {
133 ulong v;
134
135 /* keep it non-zero to prevent m->fastclock ever going to zero. */
136 v = ((µscnt *)soc.µs)->cntr;
137 return v == 0? 1: v;
138 }
139