summaryrefslogtreecommitdiff
path: root/sys/src/9/bcm64/clock.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2019-05-03 23:14:57 +0200
committercinap_lenrek <cinap_lenrek@felloff.net>2019-05-03 23:14:57 +0200
commitc6ad540af56be95b458008ae3abd3432b71d49dd (patch)
treedf132f9c9bc39be8c5120d00f49e22b535174eea /sys/src/9/bcm64/clock.c
parent1a7c224b3e36342623d4050953ca0cf3cb8a8bd5 (diff)
bcm64: add experimental work in progress arm64 kernel for raspberry pi 3
Diffstat (limited to 'sys/src/9/bcm64/clock.c')
-rw-r--r--sys/src/9/bcm64/clock.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/sys/src/9/bcm64/clock.c b/sys/src/9/bcm64/clock.c
new file mode 100644
index 000000000..bbc66a687
--- /dev/null
+++ b/sys/src/9/bcm64/clock.c
@@ -0,0 +1,267 @@
+/*
+ * bcm283[56] timers
+ * System timers run at 1MHz (timers 1 and 2 are used by GPU)
+ * ARM timer usually runs at 250MHz (may be slower in low power modes)
+ * Cycle counter runs at 700MHz (unless overclocked)
+ * All are free-running up-counters
+ *
+ * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
+ * For smp on bcm2836, use local generic timer for interrupts on cpu1-3
+ * Use ARM timer (32 bits) for perfticks
+ * Use ARM timer to force immediate interrupt
+ * Use cycle counter for cycles()
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "sysreg.h"
+
+enum {
+ SYSTIMERS = VIRTIO+0x3000,
+ ARMTIMER = VIRTIO+0xB400,
+
+ Localctl = 0x00,
+ Prescaler = 0x08,
+ GPUirqroute = 0x0C,
+
+ SystimerFreq = 1*Mhz,
+ MaxPeriod = SystimerFreq / HZ,
+ MinPeriod = 10,
+};
+
+typedef struct Systimers Systimers;
+typedef struct Armtimer Armtimer;
+
+struct Systimers {
+ u32int cs;
+ u32int clo;
+ u32int chi;
+ u32int c0;
+ u32int c1;
+ u32int c2;
+ u32int c3;
+};
+
+struct Armtimer {
+ u32int load;
+ u32int val;
+ u32int ctl;
+ u32int irqack;
+ u32int irq;
+ u32int maskedirq;
+ u32int reload;
+ u32int predivider;
+ u32int count;
+};
+
+enum {
+ CntPrescaleShift= 16, /* freq is sys_clk/(prescale+1) */
+ CntPrescaleMask = 0xFF,
+ CntEnable = 1<<9,
+ TmrDbgHalt = 1<<8,
+ TmrEnable = 1<<7,
+ TmrIntEnable = 1<<5,
+ TmrPrescale1 = 0x00<<2,
+ TmrPrescale16 = 0x01<<2,
+ TmrPrescale256 = 0x02<<2,
+ CntWidth16 = 0<<1,
+ CntWidth32 = 1<<1,
+
+ /* generic timer (cortex-a7) */
+ Enable = 1<<0,
+ Imask = 1<<1,
+ Istatus = 1<<2,
+};
+
+static void
+clockintr(Ureg *ureg, void *)
+{
+ Systimers *tn;
+
+ if(m->machno != 0)
+ panic("cpu%d: unexpected system timer interrupt", m->machno);
+ tn = (Systimers*)SYSTIMERS;
+ /* dismiss interrupt */
+ tn->cs = 1<<3;
+ timerintr(ureg, 0);
+}
+
+static void
+localclockintr(Ureg *ureg, void *)
+{
+ if(m->machno == 0)
+ panic("cpu0: Unexpected local generic timer interrupt");
+ timerintr(ureg, 0);
+}
+
+void
+clockshutdown(void)
+{
+ Armtimer *tm;
+
+ tm = (Armtimer*)ARMTIMER;
+ tm->ctl = 0;
+}
+
+void
+clockinit(void)
+{
+ Systimers *tn;
+ Armtimer *tm;
+ ulong t0, t1, tstart, tend;
+
+ syswr(PMCR_EL0, 1<<6 | 7);
+ syswr(PMCNTENSET, 1<<31);
+ syswr(PMUSERENR_EL0, 1<<2);
+
+ syswr(CNTP_TVAL_EL0, ~0UL);
+ if(m->machno == 0){
+ syswr(CNTP_CTL_EL0, Imask);
+
+ *(u32int*)(ARMLOCAL + GPUirqroute) = 0;
+
+ /* input clock is 19.2Mhz crystal */
+ *(u32int*)(ARMLOCAL + Localctl) = 0;
+ /* divide by (2^31/Prescaler) */
+ *(u32int*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
+ } else {
+ syswr(CNTP_CTL_EL0, Enable);
+ intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
+ }
+
+ tn = (Systimers*)SYSTIMERS;
+ tstart = tn->clo;
+ do{
+ t0 = lcycles();
+ }while(tn->clo == tstart);
+ tend = tstart + (SystimerFreq/100);
+ do{
+ t1 = lcycles();
+ }while(tn->clo < tend);
+ t1 -= t0;
+ m->cpuhz = 100 * t1;
+ m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
+ m->cyclefreq = m->cpuhz;
+
+ if(m->machno == 0){
+ tn->cs = 1<<3;
+ tn->c3 = tn->clo - 1;
+ intrenable(IRQtimer3, clockintr, nil, 0, "clock");
+
+ tm = (Armtimer*)ARMTIMER;
+ tm->load = 0;
+ tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+ }
+}
+
+void
+timerset(uvlong next)
+{
+ Systimers *tn;
+ uvlong now;
+ long period;
+
+ now = fastticks(nil);
+ period = next - now;
+ if(period < MinPeriod)
+ period = MinPeriod;
+ else if(period > MaxPeriod)
+ period = MaxPeriod;
+ if(m->machno)
+ syswr(CNTP_TVAL_EL0, period);
+ else{
+ tn = (Systimers*)SYSTIMERS;
+ tn->c3 = tn->clo + period;
+ }
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ Systimers *tn;
+ ulong lo, hi;
+ uvlong now;
+
+ if(hz)
+ *hz = SystimerFreq;
+ tn = (Systimers*)SYSTIMERS;
+ do{
+ hi = tn->chi;
+ lo = tn->clo;
+ }while(tn->chi != hi);
+ now = (uvlong)hi<<32 | lo;
+ return now;
+}
+
+ulong
+perfticks(void)
+{
+ Armtimer *tm;
+
+ tm = (Armtimer*)ARMTIMER;
+ return tm->count;
+}
+
+void
+armtimerset(int n)
+{
+ Armtimer *tm;
+
+ tm = (Armtimer*)ARMTIMER;
+ if(n > 0){
+ tm->ctl |= TmrEnable|TmrIntEnable;
+ tm->load = n;
+ }else{
+ tm->load = 0;
+ tm->ctl &= ~(TmrEnable|TmrIntEnable);
+ tm->irq = 1;
+ }
+}
+
+ulong
+µs(void)
+{
+ if(SystimerFreq != 1*Mhz)
+ return fastticks2us(fastticks(nil));
+ return ((Systimers*)SYSTIMERS)->clo;
+}
+
+void
+microdelay(int n)
+{
+ ulong now;
+
+ now = µs();
+ while(µs() - now < n);
+}
+
+void
+delay(int n)
+{
+ while(--n >= 0)
+ microdelay(1000);
+}
+
+void
+synccycles(void)
+{
+ static Ref r1, r2;
+ int s;
+
+ s = splhi();
+ r2.ref = 0;
+ incref(&r1);
+ while(r1.ref != conf.nmach)
+ ;
+// syswr(PMCR_EL0, 1<<6 | 7);
+ incref(&r2);
+ while(r2.ref != conf.nmach)
+ ;
+ r1.ref = 0;
+ splx(s);
+}