15d9de2d3SDavid du Colombier /*
2*5c47fe09SDavid du Colombier * bcm283[56] timers
35d9de2d3SDavid du Colombier * System timers run at 1MHz (timers 1 and 2 are used by GPU)
45d9de2d3SDavid du Colombier * ARM timer usually runs at 250MHz (may be slower in low power modes)
55d9de2d3SDavid du Colombier * Cycle counter runs at 700MHz (unless overclocked)
65d9de2d3SDavid du Colombier * All are free-running up-counters
7*5c47fe09SDavid du Colombier * Cortex-a7 has local generic timers per cpu (which we run at 1MHz)
85d9de2d3SDavid du Colombier *
95d9de2d3SDavid du Colombier * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
10*5c47fe09SDavid du Colombier * For smp on bcm2836, use local generic timer for interrupts on cpu1-3
115d9de2d3SDavid du Colombier * Use ARM timer (32 bits) for perfticks
125d9de2d3SDavid du Colombier * Use ARM timer to force immediate interrupt
135d9de2d3SDavid du Colombier * Use cycle counter for cycles()
145d9de2d3SDavid du Colombier */
155d9de2d3SDavid du Colombier
165d9de2d3SDavid du Colombier #include "u.h"
175d9de2d3SDavid du Colombier #include "../port/lib.h"
185d9de2d3SDavid du Colombier #include "mem.h"
195d9de2d3SDavid du Colombier #include "dat.h"
205d9de2d3SDavid du Colombier #include "fns.h"
215d9de2d3SDavid du Colombier #include "io.h"
22*5c47fe09SDavid du Colombier #include "ureg.h"
23*5c47fe09SDavid du Colombier #include "arm.h"
245d9de2d3SDavid du Colombier
255d9de2d3SDavid du Colombier enum {
265d9de2d3SDavid du Colombier SYSTIMERS = VIRTIO+0x3000,
275d9de2d3SDavid du Colombier ARMTIMER = VIRTIO+0xB400,
285d9de2d3SDavid du Colombier
29*5c47fe09SDavid du Colombier Localctl = 0x00,
30*5c47fe09SDavid du Colombier Prescaler = 0x08,
31*5c47fe09SDavid du Colombier Localintpending = 0x60,
32*5c47fe09SDavid du Colombier
335d9de2d3SDavid du Colombier SystimerFreq = 1*Mhz,
345d9de2d3SDavid du Colombier MaxPeriod = SystimerFreq / HZ,
35*5c47fe09SDavid du Colombier MinPeriod = 10,
36*5c47fe09SDavid du Colombier
375d9de2d3SDavid du Colombier };
385d9de2d3SDavid du Colombier
395d9de2d3SDavid du Colombier typedef struct Systimers Systimers;
405d9de2d3SDavid du Colombier typedef struct Armtimer Armtimer;
415d9de2d3SDavid du Colombier
425d9de2d3SDavid du Colombier struct Systimers {
435d9de2d3SDavid du Colombier u32int cs;
445d9de2d3SDavid du Colombier u32int clo;
455d9de2d3SDavid du Colombier u32int chi;
465d9de2d3SDavid du Colombier u32int c0;
475d9de2d3SDavid du Colombier u32int c1;
485d9de2d3SDavid du Colombier u32int c2;
495d9de2d3SDavid du Colombier u32int c3;
505d9de2d3SDavid du Colombier };
515d9de2d3SDavid du Colombier
525d9de2d3SDavid du Colombier struct Armtimer {
535d9de2d3SDavid du Colombier u32int load;
545d9de2d3SDavid du Colombier u32int val;
555d9de2d3SDavid du Colombier u32int ctl;
565d9de2d3SDavid du Colombier u32int irqack;
575d9de2d3SDavid du Colombier u32int irq;
585d9de2d3SDavid du Colombier u32int maskedirq;
595d9de2d3SDavid du Colombier u32int reload;
605d9de2d3SDavid du Colombier u32int predivider;
615d9de2d3SDavid du Colombier u32int count;
625d9de2d3SDavid du Colombier };
635d9de2d3SDavid du Colombier
645d9de2d3SDavid du Colombier enum {
655d9de2d3SDavid du Colombier CntPrescaleShift= 16, /* freq is sys_clk/(prescale+1) */
665d9de2d3SDavid du Colombier CntPrescaleMask = 0xFF,
675d9de2d3SDavid du Colombier CntEnable = 1<<9,
685d9de2d3SDavid du Colombier TmrDbgHalt = 1<<8,
695d9de2d3SDavid du Colombier TmrEnable = 1<<7,
705d9de2d3SDavid du Colombier TmrIntEnable = 1<<5,
715d9de2d3SDavid du Colombier TmrPrescale1 = 0x00<<2,
725d9de2d3SDavid du Colombier TmrPrescale16 = 0x01<<2,
735d9de2d3SDavid du Colombier TmrPrescale256 = 0x02<<2,
745d9de2d3SDavid du Colombier CntWidth16 = 0<<1,
755d9de2d3SDavid du Colombier CntWidth32 = 1<<1,
76*5c47fe09SDavid du Colombier
77*5c47fe09SDavid du Colombier /* generic timer (cortex-a7) */
78*5c47fe09SDavid du Colombier Enable = 1<<0,
79*5c47fe09SDavid du Colombier Imask = 1<<1,
80*5c47fe09SDavid du Colombier Istatus = 1<<2,
815d9de2d3SDavid du Colombier };
825d9de2d3SDavid du Colombier
835d9de2d3SDavid du Colombier static void
clockintr(Ureg * ureg,void *)845d9de2d3SDavid du Colombier clockintr(Ureg *ureg, void *)
855d9de2d3SDavid du Colombier {
865d9de2d3SDavid du Colombier Systimers *tn;
875d9de2d3SDavid du Colombier
88*5c47fe09SDavid du Colombier if(m->machno != 0)
89*5c47fe09SDavid du Colombier panic("cpu%d: unexpected system timer interrupt", m->machno);
905d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
915d9de2d3SDavid du Colombier /* dismiss interrupt */
92*5c47fe09SDavid du Colombier tn->c3 = tn->clo - 1;
935d9de2d3SDavid du Colombier tn->cs = 1<<3;
945d9de2d3SDavid du Colombier timerintr(ureg, 0);
955d9de2d3SDavid du Colombier }
965d9de2d3SDavid du Colombier
97*5c47fe09SDavid du Colombier static void
localclockintr(Ureg * ureg,void *)98*5c47fe09SDavid du Colombier localclockintr(Ureg *ureg, void *)
99*5c47fe09SDavid du Colombier {
100*5c47fe09SDavid du Colombier if(m->machno == 0)
101*5c47fe09SDavid du Colombier panic("cpu0: Unexpected local generic timer interrupt");
102*5c47fe09SDavid du Colombier cpwrtimerphysctl(Imask|Enable);
103*5c47fe09SDavid du Colombier timerintr(ureg, 0);
104*5c47fe09SDavid du Colombier }
105*5c47fe09SDavid du Colombier
1065d9de2d3SDavid du Colombier void
clockshutdown(void)1075d9de2d3SDavid du Colombier clockshutdown(void)
1085d9de2d3SDavid du Colombier {
1095d9de2d3SDavid du Colombier Armtimer *tm;
1105d9de2d3SDavid du Colombier
1115d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
1125d9de2d3SDavid du Colombier tm->ctl = 0;
113*5c47fe09SDavid du Colombier if(cpuserver)
114*5c47fe09SDavid du Colombier wdogfeed();
115*5c47fe09SDavid du Colombier else
116da091934SDavid du Colombier wdogoff();
1175d9de2d3SDavid du Colombier }
1185d9de2d3SDavid du Colombier
1195d9de2d3SDavid du Colombier void
clockinit(void)1205d9de2d3SDavid du Colombier clockinit(void)
1215d9de2d3SDavid du Colombier {
1225d9de2d3SDavid du Colombier Systimers *tn;
1235d9de2d3SDavid du Colombier Armtimer *tm;
1245d9de2d3SDavid du Colombier u32int t0, t1, tstart, tend;
1255d9de2d3SDavid du Colombier
126*5c47fe09SDavid du Colombier if(((cprdfeat1() >> 16) & 0xF) != 0) {
127*5c47fe09SDavid du Colombier /* generic timer supported */
128*5c47fe09SDavid du Colombier if(m->machno == 0){
129*5c47fe09SDavid du Colombier /* input clock is 19.2MHz or 54MHz crystal */
130*5c47fe09SDavid du Colombier *(ulong*)(ARMLOCAL + Localctl) = 0;
131*5c47fe09SDavid du Colombier /* divide by (2^31/Prescaler) for 1Mhz */
132*5c47fe09SDavid du Colombier *(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/soc.oscfreq)&~1UL;
133*5c47fe09SDavid du Colombier }
134*5c47fe09SDavid du Colombier cpwrtimerphysctl(Imask);
135*5c47fe09SDavid du Colombier }
1365d9de2d3SDavid du Colombier
137*5c47fe09SDavid du Colombier tn = (Systimers*)SYSTIMERS;
1385d9de2d3SDavid du Colombier tstart = tn->clo;
1395d9de2d3SDavid du Colombier do{
1405d9de2d3SDavid du Colombier t0 = lcycles();
1415d9de2d3SDavid du Colombier }while(tn->clo == tstart);
1425d9de2d3SDavid du Colombier tend = tstart + 10000;
1435d9de2d3SDavid du Colombier do{
1445d9de2d3SDavid du Colombier t1 = lcycles();
1455d9de2d3SDavid du Colombier }while(tn->clo != tend);
1465d9de2d3SDavid du Colombier t1 -= t0;
1475d9de2d3SDavid du Colombier m->cpuhz = 100 * t1;
1485d9de2d3SDavid du Colombier m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
1495d9de2d3SDavid du Colombier m->cyclefreq = m->cpuhz;
150*5c47fe09SDavid du Colombier if(m->machno == 0){
1515d9de2d3SDavid du Colombier tn->c3 = tn->clo - 1;
152*5c47fe09SDavid du Colombier tm = (Armtimer*)ARMTIMER;
153*5c47fe09SDavid du Colombier tm->load = 0;
154*5c47fe09SDavid du Colombier tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
1555d9de2d3SDavid du Colombier intrenable(IRQtimer3, clockintr, nil, 0, "clock");
156*5c47fe09SDavid du Colombier }else
157*5c47fe09SDavid du Colombier intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
1585d9de2d3SDavid du Colombier }
1595d9de2d3SDavid du Colombier
1605d9de2d3SDavid du Colombier void
timerset(uvlong next)1615d9de2d3SDavid du Colombier timerset(uvlong next)
1625d9de2d3SDavid du Colombier {
1635d9de2d3SDavid du Colombier Systimers *tn;
164*5c47fe09SDavid du Colombier uvlong now;
165*5c47fe09SDavid du Colombier long period;
1665d9de2d3SDavid du Colombier
1675d9de2d3SDavid du Colombier now = fastticks(nil);
168*5c47fe09SDavid du Colombier period = next - now;
1695d9de2d3SDavid du Colombier if(period < MinPeriod)
170*5c47fe09SDavid du Colombier period = MinPeriod;
1715d9de2d3SDavid du Colombier else if(period > MaxPeriod)
172*5c47fe09SDavid du Colombier period = MaxPeriod;
173*5c47fe09SDavid du Colombier if(m->machno > 0){
174*5c47fe09SDavid du Colombier cpwrtimerphysval(period);
175*5c47fe09SDavid du Colombier cpwrtimerphysctl(Enable);
176*5c47fe09SDavid du Colombier }else{
177*5c47fe09SDavid du Colombier tn = (Systimers*)SYSTIMERS;
178*5c47fe09SDavid du Colombier tn->c3 = tn->clo + period;
179*5c47fe09SDavid du Colombier }
1805d9de2d3SDavid du Colombier }
1815d9de2d3SDavid du Colombier
1825d9de2d3SDavid du Colombier uvlong
fastticks(uvlong * hz)1835d9de2d3SDavid du Colombier fastticks(uvlong *hz)
1845d9de2d3SDavid du Colombier {
1855d9de2d3SDavid du Colombier Systimers *tn;
1865d9de2d3SDavid du Colombier ulong lo, hi;
187*5c47fe09SDavid du Colombier uvlong now;
1885d9de2d3SDavid du Colombier
1895d9de2d3SDavid du Colombier if(hz)
1905d9de2d3SDavid du Colombier *hz = SystimerFreq;
191*5c47fe09SDavid du Colombier tn = (Systimers*)SYSTIMERS;
1925d9de2d3SDavid du Colombier do{
1935d9de2d3SDavid du Colombier hi = tn->chi;
1945d9de2d3SDavid du Colombier lo = tn->clo;
1955d9de2d3SDavid du Colombier }while(tn->chi != hi);
196*5c47fe09SDavid du Colombier now = (uvlong)hi<<32 | lo;
197*5c47fe09SDavid du Colombier return now;
1985d9de2d3SDavid du Colombier }
1995d9de2d3SDavid du Colombier
2005d9de2d3SDavid du Colombier ulong
perfticks(void)2015d9de2d3SDavid du Colombier perfticks(void)
2025d9de2d3SDavid du Colombier {
2035d9de2d3SDavid du Colombier Armtimer *tm;
2045d9de2d3SDavid du Colombier
2055d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
2065d9de2d3SDavid du Colombier return tm->count;
2075d9de2d3SDavid du Colombier }
2085d9de2d3SDavid du Colombier
2095d9de2d3SDavid du Colombier void
armtimerset(int n)2105d9de2d3SDavid du Colombier armtimerset(int n)
2115d9de2d3SDavid du Colombier {
2125d9de2d3SDavid du Colombier Armtimer *tm;
2135d9de2d3SDavid du Colombier
2145d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
2155d9de2d3SDavid du Colombier if(n > 0){
2165d9de2d3SDavid du Colombier tm->ctl |= TmrEnable|TmrIntEnable;
2175d9de2d3SDavid du Colombier tm->load = n;
2185d9de2d3SDavid du Colombier }else{
2195d9de2d3SDavid du Colombier tm->load = 0;
2205d9de2d3SDavid du Colombier tm->ctl &= ~(TmrEnable|TmrIntEnable);
2215d9de2d3SDavid du Colombier tm->irq = 1;
2225d9de2d3SDavid du Colombier }
2235d9de2d3SDavid du Colombier }
2245d9de2d3SDavid du Colombier
2255d9de2d3SDavid du Colombier ulong
s(void)2265d9de2d3SDavid du Colombier µs(void)
2275d9de2d3SDavid du Colombier {
2285d9de2d3SDavid du Colombier if(SystimerFreq != 1*Mhz)
2295d9de2d3SDavid du Colombier return fastticks2us(fastticks(nil));
230*5c47fe09SDavid du Colombier return ((Systimers*)SYSTIMERS)->clo;
2315d9de2d3SDavid du Colombier }
2325d9de2d3SDavid du Colombier
2335d9de2d3SDavid du Colombier void
microdelay(int n)2345d9de2d3SDavid du Colombier microdelay(int n)
2355d9de2d3SDavid du Colombier {
2365d9de2d3SDavid du Colombier Systimers *tn;
2375d9de2d3SDavid du Colombier u32int now, diff;
2385d9de2d3SDavid du Colombier
2395d9de2d3SDavid du Colombier diff = n + 1;
240*5c47fe09SDavid du Colombier tn = (Systimers*)SYSTIMERS;
2415d9de2d3SDavid du Colombier now = tn->clo;
2425d9de2d3SDavid du Colombier while(tn->clo - now < diff)
2435d9de2d3SDavid du Colombier ;
2445d9de2d3SDavid du Colombier }
2455d9de2d3SDavid du Colombier
2465d9de2d3SDavid du Colombier void
delay(int n)2475d9de2d3SDavid du Colombier delay(int n)
2485d9de2d3SDavid du Colombier {
2495d9de2d3SDavid du Colombier while(--n >= 0)
2505d9de2d3SDavid du Colombier microdelay(1000);
2515d9de2d3SDavid du Colombier }
252