updates from richard. there are some small differences due to my pedanticism about u32int, but in practice the generated code will not be different. Reference: /n/atom/patch/applied/pi2clock Date: Sun Jan 3 18:41:58 CET 2016 Signed-off-by: quanstro@quanstro.net --- /sys/src/9/bcm/clock.c Sun Jan 3 18:41:21 2016 +++ /sys/src/9/bcm/clock.c Sun Jan 3 18:41:22 2016 @@ -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() @@ -17,10 +19,17 @@ #include "dat.h" #include "fns.h" #include "io.h" +#include "ureg.h" +#include "arm.h" enum { SYSTIMERS = VIRTIO+0x3000, ARMTIMER = VIRTIO+0xB400, + ARMLOCAL = (VIRTIO+IOSIZE), + + Localctl = 0x00, + Prescaler = 0x08, + Localintpending = 0x60, SystimerFreq = 1*Mhz, MaxPeriod = SystimerFreq / HZ, @@ -64,6 +73,11 @@ TmrPrescale256 = 0x02<<2, CntWidth16 = 0<<1, CntWidth32 = 1<<1, + + /* generic timer (cortex-a7) */ + Enable = 1<<0, + Imask = 1<<1, + Istatus = 1<<2, }; static void @@ -77,6 +91,15 @@ timerintr(ureg, 0); } +static void +localclockintr(Ureg *ureg, void *) +{ + if(m->machno == 0) + panic("cpu0: Unexpected local generic timer interrupt"); + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask|Enable); + timerintr(ureg, 0); +} + void clockshutdown(void) { @@ -94,11 +117,16 @@ Armtimer *tm; u32int t0, t1, tstart, tend; - tn = (Systimers*)SYSTIMERS; - tm = (Armtimer*)ARMTIMER; - tm->load = 0; - tm->ctl = TmrPrescale1|CntEnable|CntWidth32; + if(((cprdsc(0, CpID, CpIDfeat, 1) >> 16) & 0xF) != 0) { + /* generic timer supported */ + if(m->machno == 0){ + *(u32int*)(ARMLOCAL + Localctl) = 0; /* magic */ + *(u32int*)(ARMLOCAL + Prescaler) = 0x06aaaaab; /* magic for 1 Mhz */ + } + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask); + } + tn = (Systimers*)SYSTIMERS; tstart = tn->clo; do{ t0 = lcycles(); @@ -111,25 +139,36 @@ m->cpuhz = 100 * t1; 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; + tm = (Armtimer*)ARMTIMER; + tm->load = 0; + tm->ctl = TmrPrescale1|CntEnable|CntWidth32; + intrenable(IRQtimer3, clockintr, nil, 0, "clock"); + }else + intrenable(IRQcntpns, localclockintr, nil, 0, "clock"); } 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 > 0){ + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, period); + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Enable); + }else{ + tn = (Systimers*)SYSTIMERS; + tn->c3 = (u32int)(now + period); + } } uvlong @@ -137,15 +176,20 @@ { Systimers *tn; ulong lo, hi; + uvlong now; + Mpl s; - tn = (Systimers*)SYSTIMERS; if(hz) *hz = SystimerFreq; + tn = (Systimers*)SYSTIMERS; + s = splhi(); do{ hi = tn->chi; lo = tn->clo; }while(tn->chi != hi); - m->fastclock = (uvlong)hi<<32 | lo; + now = (uvlong)hi<<32 | lo; + m->fastclock = now; + splx(s); return m->fastclock; } @@ -188,8 +232,8 @@ Systimers *tn; u32int now, diff; - tn = (Systimers*)SYSTIMERS; diff = n + 1; + tn = (Systimers*)SYSTIMERS; now = tn->clo; while(tn->clo - now < diff) ;