]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/bcm/clock.c
merge
[plan9front.git] / sys / src / 9 / bcm / clock.c
index 42d9a7b04992431a2be717118de7fbb5dfead23c..168eac9eb025d79001873fbab0195f49a9743dad 100644 (file)
@@ -1,11 +1,13 @@
 /*
- * bcm2835 timers
+ * 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
+ *  Cortex-a7 has local generic timers per cpu (which we run at 1MHz)
  *
  * 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 "dat.h"
 #include "fns.h"
 #include "io.h"
+#include "ureg.h"
+#include "arm.h"
 
 enum {
        SYSTIMERS       = VIRTIO+0x3000,
        ARMTIMER        = VIRTIO+0xB400,
 
+       Localctl        = 0x00,
+       Prescaler       = 0x08,
+
        SystimerFreq    = 1*Mhz,
        MaxPeriod       = SystimerFreq / HZ,
-       MinPeriod       = SystimerFreq / (100*HZ),
+       MinPeriod       = 10,
+
 };
 
 typedef struct Systimers Systimers;
@@ -64,6 +72,11 @@ enum {
        TmrPrescale256  = 0x02<<2,
        CntWidth16      = 0<<1,
        CntWidth32      = 1<<1,
+
+       /* generic timer (cortex-a7) */
+       Enable  = 1<<0,
+       Imask   = 1<<1,
+       Istatus = 1<<2,
 };
 
 static void
@@ -71,12 +84,22 @@ 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)
 {
@@ -91,19 +114,30 @@ clockinit(void)
 {
        Systimers *tn;
        Armtimer *tm;
-       u32int t0, t1, tstart, tend;
+       ulong t0, t1, tstart, tend;
+
+       if(((cprdsc(0, CpID, CpIDfeat, 1) >> 16) & 0xF) != 0) {
+               /* generic timer supported */
+               cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, ~0);
+               if(m->machno == 0){
+                       cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask);
+
+                       /* input clock is 19.2Mhz crystal */
+                       *(ulong*)(ARMLOCAL + Localctl) = 0;
+                       /* divide by (2^31/Prescaler) */
+                       *(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
+               } else {
+                       cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Enable);
+                       intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock");
+               }
+       }
 
        tn = (Systimers*)SYSTIMERS;
-       tm = (Armtimer*)ARMTIMER;
-       tm->load = 0;
-       tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
-       coherence();
-
        tstart = tn->clo;
        do{
                t0 = lcycles();
        }while(tn->clo == tstart);
-       tend = tstart + 10000;
+       tend = tstart + (SystimerFreq/100);
        do{
                t1 = lcycles();
        }while(tn->clo != tend);
@@ -112,24 +146,35 @@ clockinit(void)
        m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
        m->cyclefreq = m->cpuhz;
 
-       tn->c3 = tn->clo - 1;
-       intrenable(IRQtimer3, clockintr, nil, 0, "clock");
+       if(m->machno == 0){
+               tn->c3 = tn->clo - 1;
+               intrenable(IRQtimer3, clockintr, nil, BUSUNKNOWN, "clock");
+
+               tm = (Armtimer*)ARMTIMER;
+               tm->load = 0;
+               tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+       }
 }
 
 void
 timerset(uvlong next)
 {
        Systimers *tn;
-       vlong now, period;
+       uvlong now;
+       long period;
 
-       tn = (Systimers*)SYSTIMERS;
        now = fastticks(nil);
-       period = next - fastticks(nil);
+       period = next - now;
        if(period < MinPeriod)
-               next = now + MinPeriod;
+               period = MinPeriod;
        else if(period > MaxPeriod)
-               next = now + MaxPeriod;
-       tn->c3 = (ulong)next;
+               period = MaxPeriod;
+       if(m->machno)
+               cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, period);
+       else{
+               tn = (Systimers*)SYSTIMERS;
+               tn->c3 = tn->clo + period;
+       }
 }
 
 uvlong
@@ -137,16 +182,17 @@ fastticks(uvlong *hz)
 {
        Systimers *tn;
        ulong lo, hi;
+       uvlong now;
 
-       tn = (Systimers*)SYSTIMERS;
        if(hz)
                *hz = SystimerFreq;
+       tn = (Systimers*)SYSTIMERS;
        do{
                hi = tn->chi;
                lo = tn->clo;
        }while(tn->chi != hi);
-       m->fastclock = (uvlong)hi<<32 | lo;
-       return m->fastclock;
+       now = (uvlong)hi<<32 | lo;
+       return now;
 }
 
 ulong
@@ -172,7 +218,6 @@ armtimerset(int n)
                tm->ctl &= ~(TmrEnable|TmrIntEnable);
                tm->irq = 1;
        }
-       coherence();
 }
 
 ulong
@@ -180,20 +225,16 @@ ulong
 {
        if(SystimerFreq != 1*Mhz)
                return fastticks2us(fastticks(nil));
-       return fastticks(nil);
+       return ((Systimers*)SYSTIMERS)->clo;
 }
 
 void
 microdelay(int n)
 {
-       Systimers *tn;
-       u32int now, diff;
+       ulong now;
 
-       tn = (Systimers*)SYSTIMERS;
-       diff = n + 1;
-       now = tn->clo;
-       while(tn->clo - now < diff)
-               ;
+       now = µs();
+       while(µs() - now < n);
 }
 
 void