633 lines · 12.9 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. */ /* * cortex-a clocks; excludes tegra 2 SoC clocks * * cortex-a processors include private `global' and local timers * at soc.scu + 0x200 (global) and + 0x600 (local). * the global timer is a single count-up timer shared by all cores * but with per-cpu comparator and auto-increment registers. * a local count-down timer can be used as a watchdog. * * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case) * but it's unsuitable as our source of fastticks, because it stops advancing * when the cpu is suspended by WFI. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "arm.h" enum {	Debug		= 0, 	Basetickfreq	= Mhz,			/* soc.µs rate in Hz */	/* the local timers seem to run at half the expected rate */	Clockfreqbase	= 250*Mhz / 2,	/* private timer rate (PERIPHCLK/2) */	Tcycles		= Clockfreqbase / HZ,	/* cycles per clock tick */ 	MinPeriod	= Tcycles / 100,	MaxPeriod	= Tcycles, 	Dogtimeout	= Dogsectimeout * Clockfreqbase,}; typedef struct Ltimer Ltimer;typedef struct Pglbtmr Pglbtmr;typedef struct Ploctmr Ploctmr; /* * cortex-a private-intr local timer registers.  all cpus see their * own local timers at the same base address. */struct Ltimer {	ulong	load;		/* new value + 1 */	ulong	cnt;		/* counts down */	ulong	ctl;	ulong	isr; 	/* watchdog only */	ulong	wdrst;	ulong	wddis;		/* wo */ 	ulong	_pad0[2];};struct Ploctmr {	Ltimer	loc;	Ltimer	wd;}; enum {	/* ctl bits */	Tmrena	= 1<<0,		/* timer enabled */	Wdogena = Tmrena,	/* watchdog enabled */	Xreload	= 1<<1,		/* reload on intr; periodic interrupts */	Tintena	= 1<<2,		/* enable irq 29 at cnt==0 (30 for watchdog) */	Wdog	= 1<<3,		/* watchdog, not timer, mode */	Xsclrshift = 8,	Xsclrmask = MASK(8), 	/* isr bits */	Xisrclk	= 1<<0,		/* write to clear */ 	/* wdrst bits */	Wdrst	= 1<<0, 	/* wddis values */	Wdon	= 1,	Wdoff1	= 0x12345678,	/* send these two to switch to timer mode */	Wdoff2	= 0x87654321,}; /* cortex-a private-intr globl timer registers */struct Pglbtmr {	ulong	cnt[2];		/* counts up; little-endian uvlong */	ulong	ctl;	ulong	isr;	ulong	cmp[2];		/* little-endian uvlong */	ulong	inc;}; enum {	/* unique ctl bits (otherwise see X* above) */	Gcmp	= 1<<1,//	Gtintena= 1<<2,		/* enable irq 27 */	Gincr	= 1<<3,}; /* * until 5[cl] inline vlong ops, avoid them where possible, * they are currently slow function calls. */typedef union Vlong Vlong;union Vlong {	uvlong	uvl;	struct {			/* little-endian */		ulong	low;		ulong	high;	};}; static int fired;static int ticking[MAXMACH]; /* no lock is needed to update our local timer.  splhi keeps it tight. */static voidsetltimer(Ltimer *tn, ulong ticks){	int s; 	assert(ticks <= Clockfreqbase);	s = splhi();	tn->load = ticks - 1;	coherence();	tn->ctl = Tmrena | Tintena | Xreload;	coherence();	splx(s);} static voidckstuck(int cpu, long myticks, long histicks){	if (labs(histicks - myticks) > HZ) {//		iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); "//			"apparently stopped\n",//			cpu, histicks, myticks, MACHP(0)->ticks);		if (!ticking[cpu])			panic("cpu%d: clock not interrupting", cpu);	}} static voidmpclocksanity(void){	int cpu, mycpu;	long myticks, histicks; 	if (conf.nmach <= 1 || active.exiting || navailcpus == 0)		return; 	mycpu = m->machno;	myticks = m->ticks;	if (myticks == HZ)		ticking[mycpu] = 1; 	if (myticks < 5*HZ)		return; 	for (cpu = 0; cpu < navailcpus; cpu++) {		if (cpu == mycpu)			continue;		histicks = MACHP(cpu)->ticks;		if (myticks == 5*HZ || histicks > 1)			ckstuck(cpu, myticks, histicks);	}} static voidclockintr(Ureg* ureg, void *arg){	Ltimer *wd, *tn;	Ploctmr *lt; 	lt = (Ploctmr *)arg;	tn = &lt->loc;	tn->isr = Xisrclk;	coherence(); 	timerintr(ureg, 0); #ifdef watchdog_not_bloody_useless	/* appease the dogs */	wd = &lt->wd;	if (wd->cnt == 0 &&	    (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena))		panic("cpu%d: zero watchdog count but no system reset",			m->machno);	wd->load = Dogtimeout - 1;	coherence();#endif	SET(wd); USED(wd);	tegclockintr(); 	mpclocksanity();} voidclockprod(Ureg *ureg){	Ltimer *tn; 	timerintr(ureg, 0);	tegclockintr();	if (m->machno != 0) {		/* cpu1 gets stuck */		tn = &((Ploctmr *)soc.loctmr)->loc;		setltimer(tn, Tcycles);	}} static voidclockreset(Ltimer *tn){	if (probeaddr((uintptr)tn) < 0)		panic("no clock at %#p", tn);	tn->ctl = 0;	coherence();} voidwatchdogoff(Ltimer *wd){	wd->ctl &= ~Wdogena;	coherence();	wd->wddis = Wdoff1;	coherence();	wd->wddis = Wdoff2;	coherence();} /* clear any pending watchdog intrs or causes */voidwdogclrintr(Ltimer *wd){#ifdef watchdog_not_bloody_useless	wd->isr = Xisrclk;	coherence();	wd->wdrst = Wdrst;	coherence();#endif	USED(wd);} /* * stop clock interrupts on this cpu and disable the local watchdog timer, * and, if on cpu0, shutdown the shared tegra2 watchdog timer. */voidclockshutdown(void){	Ploctmr *lt; 	lt = (Ploctmr *)soc.loctmr;	clockreset(&lt->loc);	watchdogoff(&lt->wd); 	tegclockshutdown();} enum {	Instrs		= 10*Mhz,}; /* we assume that perfticks are microseconds */static longissue1loop(void){	register int i;	long st; 	i = Instrs;	st = perfticks();	do {		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;		--i; --i; --i; --i; --i; --i; --i; --i; --i;	} while(--i >= 0);	return perfticks() - st;} static longissue2loop(void){	register int i, j;	long st; 	i = Instrs / 2;			/* j gets half the decrements */	j = 0;	st = perfticks();	do {		     --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j; 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;	} while(--i >= 0);	return perfticks() - st;} /* estimate instructions/s. */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 @ Basetickfreq Hz.	 * round the result.	 */	s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000;	if (Debug)		iprint("%ud mips (%s-issue)", s, lab);	USED(s);} voidwdogintr(Ureg *, void *ltmr){#ifdef watchdog_not_bloody_useless	Ltimer *wd; 	wd = ltmr;	fired++;	wdogclrintr(wd);#endif	USED(ltmr);} static voidckcounting(Ltimer *lt){	ulong old; 	old = lt->cnt;	if (old == lt->cnt)		delay(1);	if (old == lt->cnt)		panic("cpu%d: watchdog timer not counting down", m->machno);} /* test fire with interrupt to see that it's working */static voidckwatchdog(Ltimer *wd){#ifdef watchdog_not_bloody_useless	int s; 	fired = 0;	wd->load = Tcycles - 1;	coherence();	/* Tintena is supposed to be ignored in watchdog mode */	wd->ctl |= Wdogena | Tintena;	coherence(); 	ckcounting(wd); 	s = spllo();	delay(2 * 1000/HZ);	splx(s);	if (!fired)		/* useless local watchdog */		iprint("cpu%d: local watchdog failed to interrupt\n", m->machno);	/* clean up */	wd->ctl &= ~Wdogena;	coherence();#endif	USED(wd);} static voidstartwatchdog(void){#ifdef watchdog_not_bloody_useless	Ltimer *wd;	Ploctmr *lt; 	lt = (Ploctmr *)soc.loctmr;	wd = &lt->wd;	watchdogoff(wd);	wdogclrintr(wd);	irqenable(Wdtmrirq, wdogintr, wd, "watchdog"); 	ckwatchdog(wd); 	/* set up for normal use, causing reset */	wd->ctl &= ~Tintena;			/* reset, don't interrupt */	coherence();	wd->ctl |= Wdog;	coherence();	wd->load = Dogtimeout - 1;	coherence();	wd->ctl |= Wdogena;	coherence(); 	ckcounting(wd);#endif} static voidclock0init(Ltimer *tn){	int s;	ulong old, fticks; 	/*	 * calibrate fastclock	 */	s = splhi();	tn->load = ~0ul >> 1;	coherence();	tn->ctl = Tmrena;	coherence(); 	old = perfticks();	fticks = tn->cnt;	delay(1);	fticks = abs(tn->cnt - fticks);	old = perfticks() - old;	splx(s);	if (Debug)		iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n",			m->machno, fticks, old, (fticks + old/2 - 1) / old);	USED(fticks, old); 	if (Debug)		iprint("cpu%d: ", m->machno);	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 instr'ns in the delay loop.	 */	m->delayloop = m->cpuhz / (1000 * 2);//	iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop); 	tegclock0init();} /* * the local timer is the interrupting timer and does not * participate in measuring time.  It is initially set to HZ. */voidclockinit(void){	ulong old;	Ltimer *tn;	Ploctmr *lt; 	clockshutdown(); 	/* turn my cycle counter on */	cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31); 	/* turn all my counters on and clear my cycle counter */	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1); 

Lines 1–500 of 633.