498 lines · 9.4 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * omap3530 clocks * * timers count up to zero. * * the source clock signals for the timers are sometimes selectable.  for * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock.  for the * others, it can be the 32kHz clock or the system clock.  we use only * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to * use the 32kHZ clock.  WDTIMER1 is not accessible to us on GP * (general-purpose) omaps. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "arm.h" enum {	Debug		= 0, 	Tn0		= PHYSTIMER1,	Tn1		= PHYSTIMER2, 	/* irq 36 is watchdog timer module 3 overflow */	Tn0irq		= 37,			/* base IRQ for all timers */ 	Freebase	= 1,			/* base of free-running timer */ 	/*	 * clock is 32K (32,768) Hz, so one tick is 30.517µs,	 * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.	 */	Clockfreqbase	= 32 * 1024,		/* base rate in Hz */	Tcycles		= Clockfreqbase / HZ,	/* cycles per clock tick */ 	MinPeriod	= (Tcycles / 100 < 2? 2: Tcycles / 100),	MaxPeriod	= Tcycles, 	Dogtimeout	= 20 * Clockfreqbase,	/* was 4 s.; must be ≤ 21 s. */}; enum {	/* ticpcfg bits */	Noidle		= 1<<3,	Softreset	= 1<<1, 	/* tistat bits */	Resetdone	= 1<<0, 	/* tisr/tier bits */	Ovf_it		= 1<<1,		/* gp: overflow intr */	Mat_it		= 1<<0,		/* gp: match intr */	Wdovf_it	= 1<<0,		/* wdog: overflow intr */ 	/* tclr bits */	Ar		= 1<<1,		/* gp only: autoreload mode overflow */	St		= 1<<0,		/* gp only: start the timer */}; /* omap35x timer registers */typedef struct Timerregs Timerregs;struct Timerregs {	/* common to all timers, gp and watchdog */	uchar	pad0[0x10];	ulong	ticpcfg;	ulong	tistat;		/* ro: low bit: reset done */	ulong	tisr;	ulong	tier;	ulong	twer;	ulong	tclr;	ulong	tcrr;		/* counter: cycles to zero */	ulong	tldr;	ulong	ttgr;		/* trigger */	ulong	twps;		/* ro: write posted pending */ 	/* gp timers only, unused by us */	ulong	tmar;		/* value to compare with counter */	ulong	tcar1;		/* ro */	ulong	tsicr;	ulong	tcar2;		/* ro */	union {		ulong	tpir;	/* gp: 1 ms tick generation: +ve */		ulong	wspr;	/* wdog: start/stop control */	};	ulong	tnir;		/* 1 ms tick generation: -ve */	ulong	tcvr;		/* 1 ms tick generation: next counter value */	ulong	tocr;		/* intr mask for n ticks */	ulong	towr;}; static int ticks; /* for sanity checking; m->ticks doesn't always get called */static Lock clklck; static ulong	rdcycles(void), rdbaseticks(void); /* write a watchdog timer's start/stop register */static voidwdogwrss(Timerregs *tn, ulong val){	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */		;	tn->wspr = val;	coherence();	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */		;} static voidresetwait(Timerregs *tn){	long bound; 	for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)		;	if (bound <= 0)		iprint("clock reset didn't complete\n");}  static voidwdogoff(Timerregs *tn){	resetwait(tn); 	wdogwrss(tn, 0xaaaa);		/* magic off sequence */	wdogwrss(tn, 0x5555); 	tn->tldr = 1;	coherence();	tn->tcrr = 1;			/* paranoia */	coherence();} static void wdogassure(void); static voidwdogon(Timerregs *tn){	static int beenhere; 	resetwait(tn);	tn->tldr = -Dogtimeout;	tn->tcrr = -Dogtimeout;	coherence();	wdogwrss(tn, 0xbbbb);		/* magic on sequence */	wdogwrss(tn, 0x4444);		/* magic on sequence */ 	if (!beenhere) {		beenhere = 1;		/* touching the dog is not quick, so do it infrequently */		addclock0link(wdogassure, HZ);	}} static voidwdogassure(void)		/* reset the watch dog's counter */{	Timerregs *tn; 	tn = (Timerregs *)PHYSWDOG;	wdogoff(tn); 	tn->tcrr = -Dogtimeout;	coherence(); 	wdogon(tn);} static voidclockintr(Ureg* ureg, void *arg){	Timerregs *tn;	static int nesting; 	ticks++;	coherence(); 	if (nesting == 0) {	/* if the clock interrupted itself, bail out */		++nesting;		timerintr(ureg, 0);		--nesting;	} 	tn = arg;	tn->tisr = Ovf_it;			/* dismiss the interrupt */	coherence();} static voidclockreset(Timerregs *tn){	if (probeaddr((uintptr)&tn->ticpcfg) < 0)		panic("no clock at %#p", tn);	tn->ticpcfg = Softreset | Noidle;	coherence();	resetwait(tn);	tn->tier = tn->tclr = 0;	coherence();} /* stop clock interrupts and disable the watchdog timer */voidclockshutdown(void){	clockreset((Timerregs *)PHYSWDT2);	wdogoff((Timerregs *)PHYSWDT2);	clockreset((Timerregs *)PHYSWDT3);	wdogoff((Timerregs *)PHYSWDT3); 	clockreset((Timerregs *)Tn0);	clockreset((Timerregs *)Tn1);} enum {	Instrs		= 10*Mhz,}; static longissue1loop(void){	register int i;	long st; 	st = rdbaseticks();	i = Instrs;	do {		--i; --i; --i; --i; --i;		--i; --i; --i; --i;	} while(--i >= 0);	return rdbaseticks() - st;} static longissue2loop(void){	register int i, j;	long st; 	st = rdbaseticks();	i = Instrs / 2;	j = 0;	do {		--i; --j; --i; --j;		--i; --j; --i; --j;		--j;	} while(--i >= 0);	return rdbaseticks() - st;} /* estimate instructions/s. using 32kHz clock */static voidguessmips(long (*loop)(void), char *lab){	int s;	long tcks; 	do {		s = splhi();		tcks = loop();		splx(s);		if (tcks < 0)			iprint("again...");	} while (tcks < 0);	/*	 * Instrs instructions took tcks ticks @ Clockfreqbase Hz.	 */	s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;	if (Debug)		iprint("%ud mips (%s-issue)", s, lab);	USED(s);} voidclockinit(void){	int i, s;	Timerregs *tn; 	clockshutdown(); 	/* turn cycle counter on */	cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31); 	/* turn all counters on and clear the cycle counter */	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1); 	/* let users read the cycle counter directly */	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1); 	ilock(&clklck);	m->fastclock = 1;	m->ticks = ticks = 0; 	/*	 * T0 is a freerunning timer (cycle counter); it wraps,	 * automatically reloads, and does not dispatch interrupts.	 */	tn = (Timerregs *)Tn0;	tn->tcrr = Freebase;			/* count up to 0 */	tn->tldr = Freebase;	coherence();	tn->tclr = Ar | St;	iunlock(&clklck); 	/*	 * T1 is the interrupting timer and does not participate	 * in measuring time.  It is initially set to HZ.	 */	tn = (Timerregs *)Tn1;	irqenable(Tn0irq+1, clockintr, tn, "clock");	ilock(&clklck);	tn->tcrr = -Tcycles;			/* approx.; count up to 0 */	tn->tldr = -Tcycles;	coherence();	tn->tclr = Ar | St;	coherence();	tn->tier = Ovf_it;	coherence();	iunlock(&clklck); 	/*	 * verify sanity of timer1	 */	s = spllo();			/* risky */	for (i = 0; i < 5 && ticks == 0; i++) {		delay(10);		cachedwbinvse(&ticks, sizeof ticks);	}	splx(s);	if (ticks == 0) {		if (tn->tcrr == 0)			panic("clock not interrupting");		else if (tn->tcrr == tn->tldr)			panic("clock not ticking at all");#ifdef PARANOID		else			panic("clock running very slowly");#endif	} 	guessmips(issue1loop, "single");	if (Debug)		iprint(", ");	guessmips(issue2loop, "dual");	if (Debug)		iprint("\n"); 	/*	 * m->delayloop should be the number of delay loop iterations	 * needed to consume 1 ms.  2 is min. instructions in the delay loop.	 */	m->delayloop = m->cpuhz / (1000 * 2);//	iprint("m->delayloop = %lud\n", m->delayloop); 	/*	 *  desynchronize the processor clocks so that they all don't	 *  try to resched at the same time.	 */	delay(m->machno*2);} voidwatchdoginit(void){	wdogassure();} ulongµs(void){	return fastticks2us(fastticks(nil));} voidtimerset(Tval next){	long offset;	Timerregs *tn = (Timerregs *)Tn1;	static Lock setlck; 	ilock(&setlck);	offset = next - fastticks(nil);	if(offset < MinPeriod)		offset = MinPeriod;	else if(offset > MaxPeriod)		offset = MaxPeriod;	tn->tcrr = -offset;	coherence();	iunlock(&setlck);} static ulongrdcycles(void){	ulong v; 	/* reads 32-bit cycle counter (counting up) */	v = cprdsc(0, CpCLD, CpCLDcyc, 0);	/* keep it positive; prevent m->fastclock ever going to 0 */	return v == 0? 1: v;} static ulongrdbaseticks(void){	ulong v; 	v = ((Timerregs *)Tn0)->tcrr;		/* tcrr should be counting up */	/* keep it positive; prevent m->fastclock ever going to 0 */	return v == 0? 1: v;} ulongperfticks(void){	return rdcycles();} longlcycles(void){	return perfticks();} /* * until 5[cal] inline vlong ops, avoid them where possible, * they are currently slow function calls. */typedef union Counter Counter;union Counter {	uvlong	uvl;	struct {			/* little-endian */		ulong	low;		ulong	high;	};}; enum {	Fastvlongops	= 0,}; uvlongfastticks(uvlong *hz){	Counter now, sclnow; 	if(hz)		*hz = m->cpuhz;	ilock(&clklck);	if (m->ticks > HZ/10 && m->fastclock == 0)		panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",			m->ticks, m->fastclock); 	now.uvl = m->fastclock;	now.low = rdcycles();	if(now.uvl < m->fastclock)	/* low bits must have wrapped */		now.high++;	m->fastclock = now.uvl;	coherence(); 	sclnow.uvl = now.uvl;	iunlock(&clklck);	return sclnow.uvl;} voidmicrodelay(int l){	int i; 	l = l * (vlong)m->delayloop / 1000;	if(l <= 0)		l = 1;	for(i = 0; i < l; i++)		;} voiddelay(int l){	ulong i, j; 	j = m->delayloop;	while(l-- > 0)		for(i=0; i < j; i++)			;}