xref: /plan9-contrib/sys/src/9/rb/clock.c (revision dc100ed4690b2e24ee94b6fe674abc915c477a6d)
1 /*
2  * ar7161 clocks and timers
3  */
4 #include	"u.h"
5 #include	"../port/lib.h"
6 #include	"mem.h"
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"io.h"
10 
11 #include	"ureg.h"
12 
13 enum {
14 	Cyccntres	= 2, /* counter advances at ½ clock rate (mips 24k) */
15 };
16 
17 ulong basetickfreq = 680*Mhz / Cyccntres;	/* rb450g */
18 
19 void (*kproftimer)(ulong);
20 
21 void
silencewdog(void)22 silencewdog(void)
23 {
24 	*Rstwdogtimer = basetickfreq * 2 * 5;	/* pet the dog */
25 }
26 
27 void
sicwdog(void)28 sicwdog(void)
29 {
30 	*Rstwdogtimer = basetickfreq * 2;
31 	*Rstwdogctl = Wdogreset;		/* wake the dog */
32 }
33 
34 void
wdogreset(void)35 wdogreset(void)
36 {
37 	*Rstwdogtimer = basetickfreq / 100;
38 	*Rstwdogctl = Wdogreset;		/* wake the dog */
39 	coherence();
40 	*Rstwdogtimer = basetickfreq / 10000;
41 	coherence();
42 }
43 
44 void
stopwdog(void)45 stopwdog(void)
46 {
47 	*Rstwdogtimer = ~0;
48 	*Rstwdogctl = Wdognoaction;		/* put the dog to sleep */
49 }
50 
51 void
clockshutdown(void)52 clockshutdown(void)
53 {
54 	stopwdog();
55 }
56 
57 /*
58  *  delay for l milliseconds more or less.
59  */
60 void
delay(int l)61 delay(int l)
62 {
63 	while(l-- > 0)
64 		microdelay(1000);
65 }
66 
67 /*
68  *  microseconds delay
69  */
70 void
microdelay(int l)71 microdelay(int l)
72 {
73 	int s;
74 	ulong x, cyc, cnt, speed;
75 
76 	speed = m->speed;
77 	if (speed == 0)
78 		speed = basetickfreq / Mhz * Cyccntres;
79 	cyc = (ulong)l * (speed / Cyccntres);
80 	s = splhi();
81 	cnt = rdcount();
82 	x = cnt + cyc;
83 	if (x < cnt || x >= ~0ul - basetickfreq) {
84 		/* counter will wrap between now and x, or x is too near ~0 */
85 		wrcount(0);			/* somewhat drastic */
86 		wrcompare(rdcompare() - cnt);	/* match new count */
87 		x = cyc;
88 	}
89 	while(rdcount() < x)
90 		;
91 	splx(s);
92 	silencewdog();
93 }
94 
95 void
clock(Ureg * ureg)96 clock(Ureg *ureg)
97 {
98 	wrcompare(rdcount()+m->maxperiod);	/* side-effect: dismiss intr */
99 	silencewdog();
100 	timerintr(ureg, 0);
101 }
102 
103 enum {
104 	Instrs		= 10*Mhz,
105 };
106 
107 static long
issue1loop(void)108 issue1loop(void)
109 {
110 	register int i;
111 	long st;
112 
113 	i = Instrs;
114 	st = perfticks();
115 	do {
116 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
117 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
118 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
119 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
120 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
121 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
122 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
123 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
124 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
125 		--i; --i; --i; --i; --i;
126 		/* omit 3 (--i) to account for conditional branch, nop & jump */
127 		i -= 1+3;	 /* --i plus 3 omitted (--i) instructions */
128 	} while(--i >= 0);
129 	return perfticks() - st;
130 }
131 
132 /* estimate instructions/s. */
133 static int
guessmips(long (* loop)(void),char *)134 guessmips(long (*loop)(void), char *)
135 {
136 	int s;
137 	long cyc;
138 
139 	do {
140 		s = splhi();
141 		cyc = loop();
142 		splx(s);
143 		if (cyc < 0)
144 			iprint("again...");
145 	} while (cyc < 0);
146 	/*
147 	 * Instrs instructions took cyc cycles @ basetickfreq Hz.
148 	 * round the result.
149 	 */
150 	return (((vlong)basetickfreq * Instrs) / cyc + Mhz/2) / Mhz;
151 }
152 
153 void
clockinit(void)154 clockinit(void)
155 {
156 	int mips;
157 
158 	basetickfreq = m->hz / Cyccntres;
159 	silencewdog();
160 
161 	/*
162 	 * calibrate fastclock
163 	 */
164 	mips = guessmips(issue1loop, "single");
165 
166 	/*
167 	 * m->delayloop should be the number of delay loop iterations
168 	 * needed to consume 1 ms, assuming 2 instr'ns in the delay loop.
169 	 */
170 	m->delayloop = mips*Mhz / (1000 * 2);
171 	if(m->delayloop == 0)
172 		m->delayloop = 1;
173 
174 	m->maxperiod = basetickfreq / HZ;
175 	m->minperiod = basetickfreq / (100*HZ);
176 	wrcompare(rdcount()+m->maxperiod);
177 
178 	/*
179 	 *  desynchronize the processor clocks so that they all don't
180 	 *  try to resched at the same time.
181 	 */
182 	delay(m->machno*2);
183 
184 	syncclock();
185 	intron(INTR7);
186 }
187 
188 /*
189  * Tval is supposed to be in fastticks units.
190  * One fasttick unit is 1/basetickfreq seconds.
191  */
192 void
timerset(Tval next)193 timerset(Tval next)
194 {
195 	int x;
196 	long period;
197 
198 	if(next == 0)
199 		return;
200 	x = splhi();			/* don't let us get scheduled */
201 	period = next - fastticks(nil);
202 	if(period > m->maxperiod - m->minperiod)
203 		period = m->maxperiod;
204 	else if(period < m->minperiod)
205 		period = m->minperiod;
206 	wrcompare(rdcount()+period);
207 	silencewdog();
208 	splx(x);
209 }
210 
211 /*
212  *  The rewriting of compare in this routine shouldn't be necessary.
213  *  However, we lose clock interrupts if I don't, either a chip bug
214  *  or one of ours -- presotto
215  */
216 uvlong
fastticks(uvlong * hz)217 fastticks(uvlong *hz)
218 {
219 	int x;
220 	ulong delta, count;
221 
222 	if(hz)
223 		*hz = basetickfreq;
224 
225 	/* avoid reentry on interrupt or trap, to prevent recursion */
226 	x = splhi();
227 	count = rdcount();
228 	if(rdcompare() - count > m->maxperiod)
229 		wrcompare(count+m->maxperiod);
230 	silencewdog();
231 
232 	if (count < m->lastcount)		/* wrapped around? */
233 		delta = count + ((1ull<<32) - m->lastcount);
234 	else
235 		delta = count - m->lastcount;
236 	m->lastcount = count;
237 	m->fastticks += delta;
238 	splx(x);
239 	return m->fastticks;
240 }
241 
242 ulong
s(void)243 µs(void)
244 {
245 	return fastticks2us(fastticks(nil));
246 }
247 
248 /*
249  *  performance measurement ticks.  must be low overhead.
250  *  doesn't have to count over a second.
251  */
252 ulong
perfticks(void)253 perfticks(void)
254 {
255 	return rdcount();
256 }
257 
258 long
lcycles(void)259 lcycles(void)
260 {
261 	return perfticks();
262 }
263 
264 /* should use vlong hw counters ideally; lcycles is inadequate */
265 void
cycles(uvlong * cycp)266 cycles(uvlong *cycp)
267 {
268 	*cycp = fastticks(nil);
269 }
270 
271 static Lock mpsynclock;
272 
273 /*
274  *  synchronize all clocks with processor 0
275  */
276 void
syncclock(void)277 syncclock(void)
278 {
279 	uvlong x;
280 
281 	if(m->machno == 0){
282 		m->lastcount = rdcount();
283 		m->fastticks = 0;
284 		m->ticks = 0;
285 		wrcompare(rdcount()+m->maxperiod);
286 	} else {
287 		/* wait for processor 0's soft clock to change and then sync ours */
288 		lock(&mpsynclock);
289 		x = MACHP(0)->fastticks;
290 		while(MACHP(0)->fastticks == x)
291 			;
292 		m->lastcount = rdcount();
293 		m->fastticks = MACHP(0)->fastticks;
294 		m->ticks = MACHP(0)->ticks;
295 		wrcompare(rdcount()+m->maxperiod);
296 		unlock(&mpsynclock);
297 	}
298 }
299