xref: /plan9/sys/src/9/omap/clock.c (revision df2dbabf397493e64ca7440c63ccb06027111868)
1 /*
2  * omap3530 clocks
3  *
4  * timers count up to zero.
5  *
6  * the source clock signals for the timers are sometimes selectable.  for
7  * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock.  for the
8  * others, it can be the 32kHz clock or the system clock.  we use only
9  * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to
10  * use the 32kHZ clock.  WDTIMER1 is not accessible to us on GP
11  * (general-purpose) omaps.
12  */
13 #include "u.h"
14 #include "../port/lib.h"
15 #include "mem.h"
16 #include "dat.h"
17 #include "fns.h"
18 #include "arm.h"
19 
20 enum {
21 	Debug		= 0,
22 
23 	Tn0		= PHYSTIMER1,
24 	Tn1		= PHYSTIMER2,
25 
26 	/* irq 36 is watchdog timer module 3 overflow */
27 	Tn0irq		= 37,			/* base IRQ for all timers */
28 
29 	Freebase	= 1,			/* base of free-running timer */
30 
31 	/*
32 	 * clock is 32K (32,768) Hz, so one tick is 30.517µs,
33 	 * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.
34 	 */
35 	Clockfreqbase	= 32 * 1024,		/* base rate in Hz */
36 	Tcycles		= Clockfreqbase / HZ,	/* cycles per clock tick */
37 
38 	MinPeriod	= (Tcycles / 100 < 2? 2: Tcycles / 100),
39 	MaxPeriod	= Tcycles,
40 
41 	Dogtimeout	= 20 * Clockfreqbase,	/* was 4 s.; must be ≤ 21 s. */
42 };
43 
44 enum {
45 	/* ticpcfg bits */
46 	Noidle		= 1<<3,
47 	Softreset	= 1<<1,
48 
49 	/* tistat bits */
50 	Resetdone	= 1<<0,
51 
52 	/* tisr/tier bits */
53 	Ovf_it		= 1<<1,		/* gp: overflow intr */
54 	Mat_it		= 1<<0,		/* gp: match intr */
55 	Wdovf_it	= 1<<0,		/* wdog: overflow intr */
56 
57 	/* tclr bits */
58 	Ar		= 1<<1,		/* gp only: autoreload mode overflow */
59 	St		= 1<<0,		/* gp only: start the timer */
60 };
61 
62 /* omap35x timer registers */
63 typedef struct Timerregs Timerregs;
64 struct Timerregs {
65 	/* common to all timers, gp and watchdog */
66 	uchar	pad0[0x10];
67 	ulong	ticpcfg;
68 	ulong	tistat;		/* ro: low bit: reset done */
69 	ulong	tisr;
70 	ulong	tier;
71 	ulong	twer;
72 	ulong	tclr;
73 	ulong	tcrr;		/* counter: cycles to zero */
74 	ulong	tldr;
75 	ulong	ttgr;		/* trigger */
76 	ulong	twps;		/* ro: write posted pending */
77 
78 	/* gp timers only, unused by us */
79 	ulong	tmar;		/* value to compare with counter */
80 	ulong	tcar1;		/* ro */
81 	ulong	tsicr;
82 	ulong	tcar2;		/* ro */
83 	union {
84 		ulong	tpir;	/* gp: 1 ms tick generation: +ve */
85 		ulong	wspr;	/* wdog: start/stop control */
86 	};
87 	ulong	tnir;		/* 1 ms tick generation: -ve */
88 	ulong	tcvr;		/* 1 ms tick generation: next counter value */
89 	ulong	tocr;		/* intr mask for n ticks */
90 	ulong	towr;
91 };
92 
93 static int ticks; /* for sanity checking; m->ticks doesn't always get called */
94 static Lock clklck;
95 
96 static ulong	rdcycles(void), rdbaseticks(void);
97 
98 /* write a watchdog timer's start/stop register */
99 static void
wdogwrss(Timerregs * tn,ulong val)100 wdogwrss(Timerregs *tn, ulong val)
101 {
102 	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */
103 		;
104 	tn->wspr = val;
105 	coherence();
106 	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */
107 		;
108 }
109 
110 static void
resetwait(Timerregs * tn)111 resetwait(Timerregs *tn)
112 {
113 	long bound;
114 
115 	for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)
116 		;
117 	if (bound <= 0)
118 		iprint("clock reset didn't complete\n");
119 }
120 
121 
122 static void
wdogoff(Timerregs * tn)123 wdogoff(Timerregs *tn)
124 {
125 	resetwait(tn);
126 
127 	wdogwrss(tn, 0xaaaa);		/* magic off sequence */
128 	wdogwrss(tn, 0x5555);
129 
130 	tn->tldr = 1;
131 	coherence();
132 	tn->tcrr = 1;			/* paranoia */
133 	coherence();
134 }
135 
136 static void wdogassure(void);
137 
138 static void
wdogon(Timerregs * tn)139 wdogon(Timerregs *tn)
140 {
141 	static int beenhere;
142 
143 	resetwait(tn);
144 	tn->tldr = -Dogtimeout;
145 	tn->tcrr = -Dogtimeout;
146 	coherence();
147 	wdogwrss(tn, 0xbbbb);		/* magic on sequence */
148 	wdogwrss(tn, 0x4444);		/* magic on sequence */
149 
150 	if (!beenhere) {
151 		beenhere = 1;
152 		/* touching the dog is not quick, so do it infrequently */
153 		addclock0link(wdogassure, HZ);
154 	}
155 }
156 
157 static void
wdogassure(void)158 wdogassure(void)		/* reset the watch dog's counter */
159 {
160 	Timerregs *tn;
161 
162 	tn = (Timerregs *)PHYSWDOG;
163 	wdogoff(tn);
164 
165 	tn->tcrr = -Dogtimeout;
166 	coherence();
167 
168 	wdogon(tn);
169 }
170 
171 static void
clockintr(Ureg * ureg,void * arg)172 clockintr(Ureg* ureg, void *arg)
173 {
174 	Timerregs *tn;
175 	static int nesting;
176 
177 	ticks++;
178 	coherence();
179 
180 	if (nesting == 0) {	/* if the clock interrupted itself, bail out */
181 		++nesting;
182 		timerintr(ureg, 0);
183 		--nesting;
184 	}
185 
186 	tn = arg;
187 	tn->tisr = Ovf_it;			/* dismiss the interrupt */
188 	coherence();
189 }
190 
191 static void
clockreset(Timerregs * tn)192 clockreset(Timerregs *tn)
193 {
194 	if (probeaddr((uintptr)&tn->ticpcfg) < 0)
195 		panic("no clock at %#p", tn);
196 	tn->ticpcfg = Softreset | Noidle;
197 	coherence();
198 	resetwait(tn);
199 	tn->tier = tn->tclr = 0;
200 	coherence();
201 }
202 
203 /* stop clock interrupts and disable the watchdog timer */
204 void
clockshutdown(void)205 clockshutdown(void)
206 {
207 	clockreset((Timerregs *)PHYSWDT2);
208 	wdogoff((Timerregs *)PHYSWDT2);
209 	clockreset((Timerregs *)PHYSWDT3);
210 	wdogoff((Timerregs *)PHYSWDT3);
211 
212 	clockreset((Timerregs *)Tn0);
213 	clockreset((Timerregs *)Tn1);
214 }
215 
216 enum {
217 	Instrs		= 10*Mhz,
218 };
219 
220 static long
issue1loop(void)221 issue1loop(void)
222 {
223 	register int i;
224 	long st;
225 
226 	st = rdbaseticks();
227 	i = Instrs;
228 	do {
229 		--i; --i; --i; --i; --i;
230 		--i; --i; --i; --i;
231 	} while(--i >= 0);
232 	return rdbaseticks() - st;
233 }
234 
235 static long
issue2loop(void)236 issue2loop(void)
237 {
238 	register int i, j;
239 	long st;
240 
241 	st = rdbaseticks();
242 	i = Instrs / 2;
243 	j = 0;
244 	do {
245 		--i; --j; --i; --j;
246 		--i; --j; --i; --j;
247 		--j;
248 	} while(--i >= 0);
249 	return rdbaseticks() - st;
250 }
251 
252 /* estimate instructions/s. using 32kHz clock */
253 static void
guessmips(long (* loop)(void),char * lab)254 guessmips(long (*loop)(void), char *lab)
255 {
256 	int s;
257 	long tcks;
258 
259 	do {
260 		s = splhi();
261 		tcks = loop();
262 		splx(s);
263 		if (tcks < 0)
264 			iprint("again...");
265 	} while (tcks < 0);
266 	/*
267 	 * Instrs instructions took tcks ticks @ Clockfreqbase Hz.
268 	 */
269 	s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;
270 	if (Debug)
271 		iprint("%ud mips (%s-issue)", s, lab);
272 	USED(s);
273 }
274 
275 void
clockinit(void)276 clockinit(void)
277 {
278 	int i, s;
279 	Timerregs *tn;
280 
281 	clockshutdown();
282 
283 	/* turn cycle counter on */
284 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
285 
286 	/* turn all counters on and clear the cycle counter */
287 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
288 
289 	/* let users read the cycle counter directly */
290 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1);
291 
292 	ilock(&clklck);
293 	m->fastclock = 1;
294 	m->ticks = ticks = 0;
295 
296 	/*
297 	 * T0 is a freerunning timer (cycle counter); it wraps,
298 	 * automatically reloads, and does not dispatch interrupts.
299 	 */
300 	tn = (Timerregs *)Tn0;
301 	tn->tcrr = Freebase;			/* count up to 0 */
302 	tn->tldr = Freebase;
303 	coherence();
304 	tn->tclr = Ar | St;
305 	iunlock(&clklck);
306 
307 	/*
308 	 * T1 is the interrupting timer and does not participate
309 	 * in measuring time.  It is initially set to HZ.
310 	 */
311 	tn = (Timerregs *)Tn1;
312 	irqenable(Tn0irq+1, clockintr, tn, "clock");
313 	ilock(&clklck);
314 	tn->tcrr = -Tcycles;			/* approx.; count up to 0 */
315 	tn->tldr = -Tcycles;
316 	coherence();
317 	tn->tclr = Ar | St;
318 	coherence();
319 	tn->tier = Ovf_it;
320 	coherence();
321 	iunlock(&clklck);
322 
323 	/*
324 	 * verify sanity of timer1
325 	 */
326 	s = spllo();			/* risky */
327 	for (i = 0; i < 5 && ticks == 0; i++) {
328 		delay(10);
329 		cachedwbinvse(&ticks, sizeof ticks);
330 	}
331 	splx(s);
332 	if (ticks == 0) {
333 		if (tn->tcrr == 0)
334 			panic("clock not interrupting");
335 		else if (tn->tcrr == tn->tldr)
336 			panic("clock not ticking at all");
337 #ifdef PARANOID
338 		else
339 			panic("clock running very slowly");
340 #endif
341 	}
342 
343 	guessmips(issue1loop, "single");
344 	if (Debug)
345 		iprint(", ");
346 	guessmips(issue2loop, "dual");
347 	if (Debug)
348 		iprint("\n");
349 
350 	/*
351 	 * m->delayloop should be the number of delay loop iterations
352 	 * needed to consume 1 ms.  2 is min. instructions in the delay loop.
353 	 */
354 	m->delayloop = m->cpuhz / (1000 * 2);
355 //	iprint("m->delayloop = %lud\n", m->delayloop);
356 
357 	/*
358 	 *  desynchronize the processor clocks so that they all don't
359 	 *  try to resched at the same time.
360 	 */
361 	delay(m->machno*2);
362 }
363 
364 void
watchdoginit(void)365 watchdoginit(void)
366 {
367 	wdogassure();
368 }
369 
370 ulong
s(void)371 µs(void)
372 {
373 	return fastticks2us(fastticks(nil));
374 }
375 
376 void
timerset(Tval next)377 timerset(Tval next)
378 {
379 	long offset;
380 	Timerregs *tn = (Timerregs *)Tn1;
381 	static Lock setlck;
382 
383 	ilock(&setlck);
384 	offset = next - fastticks(nil);
385 	if(offset < MinPeriod)
386 		offset = MinPeriod;
387 	else if(offset > MaxPeriod)
388 		offset = MaxPeriod;
389 	tn->tcrr = -offset;
390 	coherence();
391 	iunlock(&setlck);
392 }
393 
394 static ulong
rdcycles(void)395 rdcycles(void)
396 {
397 	ulong v;
398 
399 	/* reads 32-bit cycle counter (counting up) */
400 	v = cprdsc(0, CpCLD, CpCLDcyc, 0);
401 	/* keep it positive; prevent m->fastclock ever going to 0 */
402 	return v == 0? 1: v;
403 }
404 
405 static ulong
rdbaseticks(void)406 rdbaseticks(void)
407 {
408 	ulong v;
409 
410 	v = ((Timerregs *)Tn0)->tcrr;		/* tcrr should be counting up */
411 	/* keep it positive; prevent m->fastclock ever going to 0 */
412 	return v == 0? 1: v;
413 }
414 
415 ulong
perfticks(void)416 perfticks(void)
417 {
418 	return rdcycles();
419 }
420 
421 long
lcycles(void)422 lcycles(void)
423 {
424 	return perfticks();
425 }
426 
427 /*
428  * until 5[cal] inline vlong ops, avoid them where possible,
429  * they are currently slow function calls.
430  */
431 typedef union Counter Counter;
432 union Counter {
433 	uvlong	uvl;
434 	struct {			/* little-endian */
435 		ulong	low;
436 		ulong	high;
437 	};
438 };
439 
440 enum {
441 	Fastvlongops	= 0,
442 };
443 
444 uvlong
fastticks(uvlong * hz)445 fastticks(uvlong *hz)
446 {
447 	Counter now, sclnow;
448 
449 	if(hz)
450 		*hz = m->cpuhz;
451 	ilock(&clklck);
452 	if (m->ticks > HZ/10 && m->fastclock == 0)
453 		panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
454 			m->ticks, m->fastclock);
455 
456 	now.uvl = m->fastclock;
457 	now.low = rdcycles();
458 	if(now.uvl < m->fastclock)	/* low bits must have wrapped */
459 		now.high++;
460 	m->fastclock = now.uvl;
461 	coherence();
462 
463 	sclnow.uvl = now.uvl;
464 	iunlock(&clklck);
465 	return sclnow.uvl;
466 }
467 
468 void
microdelay(int l)469 microdelay(int l)
470 {
471 	int i;
472 
473 	l = l * (vlong)m->delayloop / 1000;
474 	if(l <= 0)
475 		l = 1;
476 	for(i = 0; i < l; i++)
477 		;
478 }
479 
480 void
delay(int l)481 delay(int l)
482 {
483 	ulong i, j;
484 
485 	j = m->delayloop;
486 	while(l-- > 0)
487 		for(i=0; i < j; i++)
488 			;
489 }
490