Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
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
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
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
365
watchdoginit(void)
366
{
367
	wdogassure();
368
}
369
 
370
ulong
371
µs(void)
372
{
373
	return fastticks2us(fastticks(nil));
374
}
375
 
376
void
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
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
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
416
perfticks(void)
417
{
418
	return rdcycles();
419
}
420
 
421
long
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
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
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
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
}