changelog shortlog tags branches changeset file revisions annotate raw help

Mercurial > hg > plan9front / sys/src/9/bcm64/clock.c

revision 7199: ba62683c0e2d
child 7312: 4dbf2522f668
     1.1new file mode 100644
     1.2--- /dev/null
     1.3+++ b/sys/src/9/bcm64/clock.c
     1.4@@ -0,0 +1,267 @@
     1.5+/*
     1.6+ * bcm283[56] timers
     1.7+ *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
     1.8+ *	ARM timer usually runs at 250MHz (may be slower in low power modes)
     1.9+ *	Cycle counter runs at 700MHz (unless overclocked)
    1.10+ *    All are free-running up-counters
    1.11+ *
    1.12+ * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
    1.13+ *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
    1.14+ * Use ARM timer (32 bits) for perfticks
    1.15+ * Use ARM timer to force immediate interrupt
    1.16+ * Use cycle counter for cycles()
    1.17+ */
    1.18+
    1.19+#include "u.h"
    1.20+#include "../port/lib.h"
    1.21+#include "mem.h"
    1.22+#include "dat.h"
    1.23+#include "fns.h"
    1.24+#include "io.h"
    1.25+#include "ureg.h"
    1.26+#include "sysreg.h"
    1.27+
    1.28+enum {
    1.29+	SYSTIMERS	= VIRTIO+0x3000,
    1.30+	ARMTIMER	= VIRTIO+0xB400,
    1.31+
    1.32+	Localctl	= 0x00,
    1.33+	Prescaler	= 0x08,
    1.34+	GPUirqroute	= 0x0C,
    1.35+
    1.36+	SystimerFreq	= 1*Mhz,
    1.37+	MaxPeriod	= SystimerFreq / HZ,
    1.38+	MinPeriod	= 10,
    1.39+};
    1.40+
    1.41+typedef struct Systimers Systimers;
    1.42+typedef struct Armtimer Armtimer;
    1.43+
    1.44+struct Systimers {
    1.45+	u32int	cs;
    1.46+	u32int	clo;
    1.47+	u32int	chi;
    1.48+	u32int	c0;
    1.49+	u32int	c1;
    1.50+	u32int	c2;
    1.51+	u32int	c3;
    1.52+};
    1.53+
    1.54+struct Armtimer {
    1.55+	u32int	load;
    1.56+	u32int	val;
    1.57+	u32int	ctl;
    1.58+	u32int	irqack;
    1.59+	u32int	irq;
    1.60+	u32int	maskedirq;
    1.61+	u32int	reload;
    1.62+	u32int	predivider;
    1.63+	u32int	count;
    1.64+};
    1.65+
    1.66+enum {
    1.67+	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
    1.68+	CntPrescaleMask	= 0xFF,
    1.69+	CntEnable	= 1<<9,
    1.70+	TmrDbgHalt	= 1<<8,
    1.71+	TmrEnable	= 1<<7,
    1.72+	TmrIntEnable	= 1<<5,
    1.73+	TmrPrescale1	= 0x00<<2,
    1.74+	TmrPrescale16	= 0x01<<2,
    1.75+	TmrPrescale256	= 0x02<<2,
    1.76+	CntWidth16	= 0<<1,
    1.77+	CntWidth32	= 1<<1,
    1.78+
    1.79+	/* generic timer (cortex-a7) */
    1.80+	Enable	= 1<<0,
    1.81+	Imask	= 1<<1,
    1.82+	Istatus = 1<<2,
    1.83+};
    1.84+
    1.85+static void
    1.86+clockintr(Ureg *ureg, void *)
    1.87+{
    1.88+	Systimers *tn;
    1.89+
    1.90+	if(m->machno != 0)
    1.91+		panic("cpu%d: unexpected system timer interrupt", m->machno);
    1.92+	tn = (Systimers*)SYSTIMERS;
    1.93+	/* dismiss interrupt */
    1.94+	tn->cs = 1<<3;
    1.95+	timerintr(ureg, 0);
    1.96+}
    1.97+
    1.98+static void
    1.99+localclockintr(Ureg *ureg, void *)
   1.100+{
   1.101+	if(m->machno == 0)
   1.102+		panic("cpu0: Unexpected local generic timer interrupt");
   1.103+	timerintr(ureg, 0);
   1.104+}
   1.105+
   1.106+void
   1.107+clockshutdown(void)
   1.108+{
   1.109+	Armtimer *tm;
   1.110+
   1.111+	tm = (Armtimer*)ARMTIMER;
   1.112+	tm->ctl = 0;
   1.113+}
   1.114+
   1.115+void
   1.116+clockinit(void)
   1.117+{
   1.118+	Systimers *tn;
   1.119+	Armtimer *tm;
   1.120+	ulong t0, t1, tstart, tend;
   1.121+
   1.122+	syswr(PMCR_EL0, 1<<6 | 7);
   1.123+	syswr(PMCNTENSET, 1<<31);
   1.124+	syswr(PMUSERENR_EL0, 1<<2);
   1.125+
   1.126+	syswr(CNTP_TVAL_EL0, ~0UL);
   1.127+	if(m->machno == 0){
   1.128+		syswr(CNTP_CTL_EL0, Imask);
   1.129+
   1.130+		*(u32int*)(ARMLOCAL + GPUirqroute) = 0;
   1.131+
   1.132+		/* input clock is 19.2Mhz crystal */
   1.133+		*(u32int*)(ARMLOCAL + Localctl) = 0;
   1.134+		/* divide by (2^31/Prescaler) */
   1.135+		*(u32int*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
   1.136+	} else {
   1.137+		syswr(CNTP_CTL_EL0, Enable);
   1.138+		intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
   1.139+	}
   1.140+
   1.141+	tn = (Systimers*)SYSTIMERS;
   1.142+	tstart = tn->clo;
   1.143+	do{
   1.144+		t0 = lcycles();
   1.145+	}while(tn->clo == tstart);
   1.146+	tend = tstart + (SystimerFreq/100);
   1.147+	do{
   1.148+		t1 = lcycles();
   1.149+	}while(tn->clo < tend);
   1.150+	t1 -= t0;
   1.151+	m->cpuhz = 100 * t1;
   1.152+	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
   1.153+	m->cyclefreq = m->cpuhz;
   1.154+
   1.155+	if(m->machno == 0){
   1.156+		tn->cs = 1<<3;
   1.157+		tn->c3 = tn->clo - 1;
   1.158+		intrenable(IRQtimer3, clockintr, nil, 0, "clock");
   1.159+
   1.160+		tm = (Armtimer*)ARMTIMER;
   1.161+		tm->load = 0;
   1.162+		tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
   1.163+	}
   1.164+}
   1.165+
   1.166+void
   1.167+timerset(uvlong next)
   1.168+{
   1.169+	Systimers *tn;
   1.170+	uvlong now;
   1.171+	long period;
   1.172+
   1.173+	now = fastticks(nil);
   1.174+	period = next - now;
   1.175+	if(period < MinPeriod)
   1.176+		period = MinPeriod;
   1.177+	else if(period > MaxPeriod)
   1.178+		period = MaxPeriod;
   1.179+	if(m->machno)
   1.180+		syswr(CNTP_TVAL_EL0, period);
   1.181+	else{
   1.182+		tn = (Systimers*)SYSTIMERS;
   1.183+		tn->c3 = tn->clo + period;
   1.184+	}
   1.185+}
   1.186+
   1.187+uvlong
   1.188+fastticks(uvlong *hz)
   1.189+{
   1.190+	Systimers *tn;
   1.191+	ulong lo, hi;
   1.192+	uvlong now;
   1.193+
   1.194+	if(hz)
   1.195+		*hz = SystimerFreq;
   1.196+	tn = (Systimers*)SYSTIMERS;
   1.197+	do{
   1.198+		hi = tn->chi;
   1.199+		lo = tn->clo;
   1.200+	}while(tn->chi != hi);
   1.201+	now = (uvlong)hi<<32 | lo;
   1.202+	return now;
   1.203+}
   1.204+
   1.205+ulong
   1.206+perfticks(void)
   1.207+{
   1.208+	Armtimer *tm;
   1.209+
   1.210+	tm = (Armtimer*)ARMTIMER;
   1.211+	return tm->count;
   1.212+}
   1.213+
   1.214+void
   1.215+armtimerset(int n)
   1.216+{
   1.217+	Armtimer *tm;
   1.218+
   1.219+	tm = (Armtimer*)ARMTIMER;
   1.220+	if(n > 0){
   1.221+		tm->ctl |= TmrEnable|TmrIntEnable;
   1.222+		tm->load = n;
   1.223+	}else{
   1.224+		tm->load = 0;
   1.225+		tm->ctl &= ~(TmrEnable|TmrIntEnable);
   1.226+		tm->irq = 1;
   1.227+	}
   1.228+}
   1.229+
   1.230+ulong
   1.231+µs(void)
   1.232+{
   1.233+	if(SystimerFreq != 1*Mhz)
   1.234+		return fastticks2us(fastticks(nil));
   1.235+	return ((Systimers*)SYSTIMERS)->clo;
   1.236+}
   1.237+
   1.238+void
   1.239+microdelay(int n)
   1.240+{
   1.241+	ulong now;
   1.242+
   1.243+	now = µs();
   1.244+	while(µs() - now < n);
   1.245+}
   1.246+
   1.247+void
   1.248+delay(int n)
   1.249+{
   1.250+	while(--n >= 0)
   1.251+		microdelay(1000);
   1.252+}
   1.253+
   1.254+void
   1.255+synccycles(void)
   1.256+{
   1.257+	static Ref r1, r2;
   1.258+	int s;
   1.259+
   1.260+	s = splhi();
   1.261+	r2.ref = 0;
   1.262+	incref(&r1);
   1.263+	while(r1.ref != conf.nmach)
   1.264+		;
   1.265+//	syswr(PMCR_EL0, 1<<6 | 7);
   1.266+	incref(&r2);
   1.267+	while(r2.ref != conf.nmach)
   1.268+		;
   1.269+	r1.ref = 0;
   1.270+	splx(s);
   1.271+}