15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier * bcm2835 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
75d9de2d3SDavid du Colombier *
85d9de2d3SDavid du Colombier * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
95d9de2d3SDavid du Colombier * Use ARM timer (32 bits) for perfticks
105d9de2d3SDavid du Colombier * Use ARM timer to force immediate interrupt
115d9de2d3SDavid du Colombier * Use cycle counter for cycles()
125d9de2d3SDavid du Colombier */
135d9de2d3SDavid du Colombier
145d9de2d3SDavid du Colombier #include "u.h"
155d9de2d3SDavid du Colombier #include "../port/lib.h"
165d9de2d3SDavid du Colombier #include "mem.h"
175d9de2d3SDavid du Colombier #include "dat.h"
185d9de2d3SDavid du Colombier #include "fns.h"
195d9de2d3SDavid du Colombier #include "io.h"
205d9de2d3SDavid du Colombier
215d9de2d3SDavid du Colombier enum {
225d9de2d3SDavid du Colombier SYSTIMERS = VIRTIO+0x3000,
235d9de2d3SDavid du Colombier ARMTIMER = VIRTIO+0xB400,
245d9de2d3SDavid du Colombier
255d9de2d3SDavid du Colombier SystimerFreq = 1*Mhz,
265d9de2d3SDavid du Colombier MaxPeriod = SystimerFreq / HZ,
275d9de2d3SDavid du Colombier MinPeriod = SystimerFreq / (100*HZ),
285d9de2d3SDavid du Colombier };
295d9de2d3SDavid du Colombier
305d9de2d3SDavid du Colombier typedef struct Systimers Systimers;
315d9de2d3SDavid du Colombier typedef struct Armtimer Armtimer;
325d9de2d3SDavid du Colombier
335d9de2d3SDavid du Colombier struct Systimers {
345d9de2d3SDavid du Colombier u32int cs;
355d9de2d3SDavid du Colombier u32int clo;
365d9de2d3SDavid du Colombier u32int chi;
375d9de2d3SDavid du Colombier u32int c0;
385d9de2d3SDavid du Colombier u32int c1;
395d9de2d3SDavid du Colombier u32int c2;
405d9de2d3SDavid du Colombier u32int c3;
415d9de2d3SDavid du Colombier };
425d9de2d3SDavid du Colombier
435d9de2d3SDavid du Colombier struct Armtimer {
445d9de2d3SDavid du Colombier u32int load;
455d9de2d3SDavid du Colombier u32int val;
465d9de2d3SDavid du Colombier u32int ctl;
475d9de2d3SDavid du Colombier u32int irqack;
485d9de2d3SDavid du Colombier u32int irq;
495d9de2d3SDavid du Colombier u32int maskedirq;
505d9de2d3SDavid du Colombier u32int reload;
515d9de2d3SDavid du Colombier u32int predivider;
525d9de2d3SDavid du Colombier u32int count;
535d9de2d3SDavid du Colombier };
545d9de2d3SDavid du Colombier
555d9de2d3SDavid du Colombier enum {
565d9de2d3SDavid du Colombier CntPrescaleShift= 16, /* freq is sys_clk/(prescale+1) */
575d9de2d3SDavid du Colombier CntPrescaleMask = 0xFF,
585d9de2d3SDavid du Colombier CntEnable = 1<<9,
595d9de2d3SDavid du Colombier TmrDbgHalt = 1<<8,
605d9de2d3SDavid du Colombier TmrEnable = 1<<7,
615d9de2d3SDavid du Colombier TmrIntEnable = 1<<5,
625d9de2d3SDavid du Colombier TmrPrescale1 = 0x00<<2,
635d9de2d3SDavid du Colombier TmrPrescale16 = 0x01<<2,
645d9de2d3SDavid du Colombier TmrPrescale256 = 0x02<<2,
655d9de2d3SDavid du Colombier CntWidth16 = 0<<1,
665d9de2d3SDavid du Colombier CntWidth32 = 1<<1,
675d9de2d3SDavid du Colombier };
685d9de2d3SDavid du Colombier
695d9de2d3SDavid du Colombier static void
clockintr(Ureg * ureg,void *)705d9de2d3SDavid du Colombier clockintr(Ureg *ureg, void *)
715d9de2d3SDavid du Colombier {
725d9de2d3SDavid du Colombier Systimers *tn;
735d9de2d3SDavid du Colombier
745d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
755d9de2d3SDavid du Colombier /* dismiss interrupt */
765d9de2d3SDavid du Colombier tn->cs = 1<<3;
775d9de2d3SDavid du Colombier timerintr(ureg, 0);
785d9de2d3SDavid du Colombier }
795d9de2d3SDavid du Colombier
805d9de2d3SDavid du Colombier void
clockshutdown(void)815d9de2d3SDavid du Colombier clockshutdown(void)
825d9de2d3SDavid du Colombier {
835d9de2d3SDavid du Colombier Armtimer *tm;
845d9de2d3SDavid du Colombier
855d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
865d9de2d3SDavid du Colombier tm->ctl = 0;
87*da091934SDavid du Colombier wdogoff();
885d9de2d3SDavid du Colombier }
895d9de2d3SDavid du Colombier
905d9de2d3SDavid du Colombier void
clockinit(void)915d9de2d3SDavid du Colombier clockinit(void)
925d9de2d3SDavid du Colombier {
935d9de2d3SDavid du Colombier Systimers *tn;
945d9de2d3SDavid du Colombier Armtimer *tm;
955d9de2d3SDavid du Colombier u32int t0, t1, tstart, tend;
965d9de2d3SDavid du Colombier
975d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
985d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
995d9de2d3SDavid du Colombier tm->load = 0;
1005d9de2d3SDavid du Colombier tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
1015d9de2d3SDavid du Colombier
1025d9de2d3SDavid du Colombier tstart = tn->clo;
1035d9de2d3SDavid du Colombier do{
1045d9de2d3SDavid du Colombier t0 = lcycles();
1055d9de2d3SDavid du Colombier }while(tn->clo == tstart);
1065d9de2d3SDavid du Colombier tend = tstart + 10000;
1075d9de2d3SDavid du Colombier do{
1085d9de2d3SDavid du Colombier t1 = lcycles();
1095d9de2d3SDavid du Colombier }while(tn->clo != tend);
1105d9de2d3SDavid du Colombier t1 -= t0;
1115d9de2d3SDavid du Colombier m->cpuhz = 100 * t1;
1125d9de2d3SDavid du Colombier m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
1135d9de2d3SDavid du Colombier m->cyclefreq = m->cpuhz;
1145d9de2d3SDavid du Colombier
1155d9de2d3SDavid du Colombier tn->c3 = tn->clo - 1;
1165d9de2d3SDavid du Colombier intrenable(IRQtimer3, clockintr, nil, 0, "clock");
1175d9de2d3SDavid du Colombier }
1185d9de2d3SDavid du Colombier
1195d9de2d3SDavid du Colombier void
timerset(uvlong next)1205d9de2d3SDavid du Colombier timerset(uvlong next)
1215d9de2d3SDavid du Colombier {
1225d9de2d3SDavid du Colombier Systimers *tn;
1235d9de2d3SDavid du Colombier vlong now, period;
1245d9de2d3SDavid du Colombier
1255d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
1265d9de2d3SDavid du Colombier now = fastticks(nil);
1275d9de2d3SDavid du Colombier period = next - fastticks(nil);
1285d9de2d3SDavid du Colombier if(period < MinPeriod)
1295d9de2d3SDavid du Colombier next = now + MinPeriod;
1305d9de2d3SDavid du Colombier else if(period > MaxPeriod)
1315d9de2d3SDavid du Colombier next = now + MaxPeriod;
1325d9de2d3SDavid du Colombier tn->c3 = (ulong)next;
1335d9de2d3SDavid du Colombier }
1345d9de2d3SDavid du Colombier
1355d9de2d3SDavid du Colombier uvlong
fastticks(uvlong * hz)1365d9de2d3SDavid du Colombier fastticks(uvlong *hz)
1375d9de2d3SDavid du Colombier {
1385d9de2d3SDavid du Colombier Systimers *tn;
1395d9de2d3SDavid du Colombier ulong lo, hi;
1405d9de2d3SDavid du Colombier
1415d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
1425d9de2d3SDavid du Colombier if(hz)
1435d9de2d3SDavid du Colombier *hz = SystimerFreq;
1445d9de2d3SDavid du Colombier do{
1455d9de2d3SDavid du Colombier hi = tn->chi;
1465d9de2d3SDavid du Colombier lo = tn->clo;
1475d9de2d3SDavid du Colombier }while(tn->chi != hi);
1485d9de2d3SDavid du Colombier m->fastclock = (uvlong)hi<<32 | lo;
1495d9de2d3SDavid du Colombier return m->fastclock;
1505d9de2d3SDavid du Colombier }
1515d9de2d3SDavid du Colombier
1525d9de2d3SDavid du Colombier ulong
perfticks(void)1535d9de2d3SDavid du Colombier perfticks(void)
1545d9de2d3SDavid du Colombier {
1555d9de2d3SDavid du Colombier Armtimer *tm;
1565d9de2d3SDavid du Colombier
1575d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
1585d9de2d3SDavid du Colombier return tm->count;
1595d9de2d3SDavid du Colombier }
1605d9de2d3SDavid du Colombier
1615d9de2d3SDavid du Colombier void
armtimerset(int n)1625d9de2d3SDavid du Colombier armtimerset(int n)
1635d9de2d3SDavid du Colombier {
1645d9de2d3SDavid du Colombier Armtimer *tm;
1655d9de2d3SDavid du Colombier
1665d9de2d3SDavid du Colombier tm = (Armtimer*)ARMTIMER;
1675d9de2d3SDavid du Colombier if(n > 0){
1685d9de2d3SDavid du Colombier tm->ctl |= TmrEnable|TmrIntEnable;
1695d9de2d3SDavid du Colombier tm->load = n;
1705d9de2d3SDavid du Colombier }else{
1715d9de2d3SDavid du Colombier tm->load = 0;
1725d9de2d3SDavid du Colombier tm->ctl &= ~(TmrEnable|TmrIntEnable);
1735d9de2d3SDavid du Colombier tm->irq = 1;
1745d9de2d3SDavid du Colombier }
1755d9de2d3SDavid du Colombier }
1765d9de2d3SDavid du Colombier
1775d9de2d3SDavid du Colombier ulong
s(void)1785d9de2d3SDavid du Colombier µs(void)
1795d9de2d3SDavid du Colombier {
1805d9de2d3SDavid du Colombier if(SystimerFreq != 1*Mhz)
1815d9de2d3SDavid du Colombier return fastticks2us(fastticks(nil));
1825d9de2d3SDavid du Colombier return fastticks(nil);
1835d9de2d3SDavid du Colombier }
1845d9de2d3SDavid du Colombier
1855d9de2d3SDavid du Colombier void
microdelay(int n)1865d9de2d3SDavid du Colombier microdelay(int n)
1875d9de2d3SDavid du Colombier {
1885d9de2d3SDavid du Colombier Systimers *tn;
1895d9de2d3SDavid du Colombier u32int now, diff;
1905d9de2d3SDavid du Colombier
1915d9de2d3SDavid du Colombier tn = (Systimers*)SYSTIMERS;
1925d9de2d3SDavid du Colombier diff = n + 1;
1935d9de2d3SDavid du Colombier now = tn->clo;
1945d9de2d3SDavid du Colombier while(tn->clo - now < diff)
1955d9de2d3SDavid du Colombier ;
1965d9de2d3SDavid du Colombier }
1975d9de2d3SDavid du Colombier
1985d9de2d3SDavid du Colombier void
delay(int n)1995d9de2d3SDavid du Colombier delay(int n)
2005d9de2d3SDavid du Colombier {
2015d9de2d3SDavid du Colombier while(--n >= 0)
2025d9de2d3SDavid du Colombier microdelay(1000);
2035d9de2d3SDavid du Colombier }
204