180ee5cbfSDavid du Colombier #include "u.h"
280ee5cbfSDavid du Colombier #include "../port/lib.h"
380ee5cbfSDavid du Colombier #include "mem.h"
480ee5cbfSDavid du Colombier #include "dat.h"
580ee5cbfSDavid du Colombier #include "fns.h"
680ee5cbfSDavid du Colombier #include "io.h"
780ee5cbfSDavid du Colombier #include "ureg.h"
8aa72973aSDavid du Colombier #include "../port/error.h"
980ee5cbfSDavid du Colombier
10*27acba7cSDavid du Colombier enum {
11*27acba7cSDavid du Colombier Maxtimerloops = 20*1000,
12*27acba7cSDavid du Colombier };
13*27acba7cSDavid du Colombier
149a747e4fSDavid du Colombier struct Timers
1580ee5cbfSDavid du Colombier {
169a747e4fSDavid du Colombier Lock;
179a747e4fSDavid du Colombier Timer *head;
189a747e4fSDavid du Colombier };
1980ee5cbfSDavid du Colombier
209a747e4fSDavid du Colombier static Timers timers[MAXMACH];
21*27acba7cSDavid du Colombier static int timersinited;
229a747e4fSDavid du Colombier
239a747e4fSDavid du Colombier ulong intrcount[MAXMACH];
249a747e4fSDavid du Colombier ulong fcallcount[MAXMACH];
259a747e4fSDavid du Colombier
262cca75a1SDavid du Colombier static vlong
tadd(Timers * tt,Timer * nt)2728495efeSDavid du Colombier tadd(Timers *tt, Timer *nt)
289a747e4fSDavid du Colombier {
29e288d156SDavid du Colombier Timer *t, **last;
309a747e4fSDavid du Colombier
31e288d156SDavid du Colombier /* Called with tt locked */
32e288d156SDavid du Colombier assert(nt->tt == nil);
33e288d156SDavid du Colombier switch(nt->tmode){
34e288d156SDavid du Colombier default:
35e288d156SDavid du Colombier panic("timer");
369a747e4fSDavid du Colombier break;
37e288d156SDavid du Colombier case Trelative:
382cca75a1SDavid du Colombier if(nt->tns <= 0)
392cca75a1SDavid du Colombier nt->tns = 1;
4028495efeSDavid du Colombier nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
41e288d156SDavid du Colombier break;
42e288d156SDavid du Colombier case Tperiodic:
4328495efeSDavid du Colombier assert(nt->tns >= 100000); /* At least 100 µs period */
44e288d156SDavid du Colombier if(nt->twhen == 0){
45d9306527SDavid du Colombier /* look for another timer at same frequency for combining */
46e288d156SDavid du Colombier for(t = tt->head; t; t = t->tnext){
47e288d156SDavid du Colombier if(t->tmode == Tperiodic && t->tns == nt->tns)
489a747e4fSDavid du Colombier break;
499a747e4fSDavid du Colombier }
50e288d156SDavid du Colombier if (t)
51e288d156SDavid du Colombier nt->twhen = t->twhen;
529a747e4fSDavid du Colombier else
5328495efeSDavid du Colombier nt->twhen = fastticks(nil);
5428495efeSDavid du Colombier }
5528495efeSDavid du Colombier nt->twhen += ns2fastticks(nt->tns);
56e288d156SDavid du Colombier break;
57e288d156SDavid du Colombier }
58e288d156SDavid du Colombier
59e288d156SDavid du Colombier for(last = &tt->head; t = *last; last = &t->tnext){
60e288d156SDavid du Colombier if(t->twhen > nt->twhen)
61e288d156SDavid du Colombier break;
62e288d156SDavid du Colombier }
63e288d156SDavid du Colombier nt->tnext = *last;
64e288d156SDavid du Colombier *last = nt;
65e288d156SDavid du Colombier nt->tt = tt;
66e288d156SDavid du Colombier if(last == &tt->head)
67e288d156SDavid du Colombier return nt->twhen;
689a747e4fSDavid du Colombier return 0;
699a747e4fSDavid du Colombier }
709a747e4fSDavid du Colombier
712e976735SDavid du Colombier static uvlong
tdel(Timer * dt)722e976735SDavid du Colombier tdel(Timer *dt)
732e976735SDavid du Colombier {
742e976735SDavid du Colombier
752e976735SDavid du Colombier Timer *t, **last;
762e976735SDavid du Colombier Timers *tt;
772e976735SDavid du Colombier
782e976735SDavid du Colombier tt = dt->tt;
792e976735SDavid du Colombier if (tt == nil)
802e976735SDavid du Colombier return 0;
812e976735SDavid du Colombier for(last = &tt->head; t = *last; last = &t->tnext){
822e976735SDavid du Colombier if(t == dt){
832e976735SDavid du Colombier assert(dt->tt);
842e976735SDavid du Colombier dt->tt = nil;
852e976735SDavid du Colombier *last = t->tnext;
862e976735SDavid du Colombier break;
872e976735SDavid du Colombier }
882e976735SDavid du Colombier }
892e976735SDavid du Colombier if(last == &tt->head && tt->head)
902e976735SDavid du Colombier return tt->head->twhen;
912e976735SDavid du Colombier return 0;
922e976735SDavid du Colombier }
932e976735SDavid du Colombier
94e288d156SDavid du Colombier /* add or modify a timer */
959a747e4fSDavid du Colombier void
timeradd(Timer * nt)969a747e4fSDavid du Colombier timeradd(Timer *nt)
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier Timers *tt;
99da51d93aSDavid du Colombier vlong when;
1009a747e4fSDavid du Colombier
1012e976735SDavid du Colombier /* Must lock Timer struct before Timers struct */
1022e976735SDavid du Colombier ilock(nt);
1032e976735SDavid du Colombier if(tt = nt->tt){
1042e976735SDavid du Colombier ilock(tt);
1052e976735SDavid du Colombier tdel(nt);
1062e976735SDavid du Colombier iunlock(tt);
1072e976735SDavid du Colombier }
1089a747e4fSDavid du Colombier tt = &timers[m->machno];
1099a747e4fSDavid du Colombier ilock(tt);
11028495efeSDavid du Colombier when = tadd(tt, nt);
1119a747e4fSDavid du Colombier if(when)
1129a747e4fSDavid du Colombier timerset(when);
1139a747e4fSDavid du Colombier iunlock(tt);
1142e976735SDavid du Colombier iunlock(nt);
11580ee5cbfSDavid du Colombier }
11680ee5cbfSDavid du Colombier
1172e976735SDavid du Colombier
11880ee5cbfSDavid du Colombier void
timerdel(Timer * dt)1199a747e4fSDavid du Colombier timerdel(Timer *dt)
12080ee5cbfSDavid du Colombier {
1219a747e4fSDavid du Colombier Timers *tt;
1222e976735SDavid du Colombier uvlong when;
12380ee5cbfSDavid du Colombier
1242e976735SDavid du Colombier ilock(dt);
1252e976735SDavid du Colombier if(tt = dt->tt){
1269a747e4fSDavid du Colombier ilock(tt);
1272e976735SDavid du Colombier when = tdel(dt);
1282e976735SDavid du Colombier if(when && tt == &timers[m->machno])
129e288d156SDavid du Colombier timerset(tt->head->twhen);
1309a747e4fSDavid du Colombier iunlock(tt);
1319a747e4fSDavid du Colombier }
1322e976735SDavid du Colombier iunlock(dt);
133e288d156SDavid du Colombier }
1349a747e4fSDavid du Colombier
1359a747e4fSDavid du Colombier void
hzclock(Ureg * ur)1369a747e4fSDavid du Colombier hzclock(Ureg *ur)
1379a747e4fSDavid du Colombier {
13880ee5cbfSDavid du Colombier m->ticks++;
13980ee5cbfSDavid du Colombier if(m->proc)
14080ee5cbfSDavid du Colombier m->proc->pc = ur->pc;
14180ee5cbfSDavid du Colombier
1429a747e4fSDavid du Colombier if(m->flushmmu){
1439a747e4fSDavid du Colombier if(up)
1449a747e4fSDavid du Colombier flushmmu();
1459a747e4fSDavid du Colombier m->flushmmu = 0;
1469a747e4fSDavid du Colombier }
14780ee5cbfSDavid du Colombier
14880ee5cbfSDavid du Colombier accounttime();
1499a747e4fSDavid du Colombier kmapinval();
15080ee5cbfSDavid du Colombier
15180ee5cbfSDavid du Colombier if(kproftimer != nil)
15280ee5cbfSDavid du Colombier kproftimer(ur->pc);
15380ee5cbfSDavid du Colombier
15480ee5cbfSDavid du Colombier if((active.machs&(1<<m->machno)) == 0)
15580ee5cbfSDavid du Colombier return;
15680ee5cbfSDavid du Colombier
157e288d156SDavid du Colombier if(active.exiting) {
15880ee5cbfSDavid du Colombier print("someone's exiting\n");
15980ee5cbfSDavid du Colombier exit(0);
16080ee5cbfSDavid du Colombier }
16180ee5cbfSDavid du Colombier
16280ee5cbfSDavid du Colombier checkalarms();
1639a747e4fSDavid du Colombier
164e288d156SDavid du Colombier if(up && up->state == Running)
165a6a9e072SDavid du Colombier hzsched(); /* in proc.c */
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier
1689a747e4fSDavid du Colombier void
timerintr(Ureg * u,Tval)169ea58ad6fSDavid du Colombier timerintr(Ureg *u, Tval)
1709a747e4fSDavid du Colombier {
1719a747e4fSDavid du Colombier Timer *t;
1729a747e4fSDavid du Colombier Timers *tt;
1739a747e4fSDavid du Colombier uvlong when, now;
174*27acba7cSDavid du Colombier int count, callhzclock;
1753ff48bf5SDavid du Colombier
1769a747e4fSDavid du Colombier intrcount[m->machno]++;
1779a747e4fSDavid du Colombier callhzclock = 0;
1789a747e4fSDavid du Colombier tt = &timers[m->machno];
1799a747e4fSDavid du Colombier now = fastticks(nil);
180*27acba7cSDavid du Colombier if(now == 0)
181*27acba7cSDavid du Colombier panic("timerintr: zero fastticks()");
1829a747e4fSDavid du Colombier ilock(tt);
183*27acba7cSDavid du Colombier count = Maxtimerloops;
184*27acba7cSDavid du Colombier while((t = tt->head) != nil){
1852e976735SDavid du Colombier /*
1862e976735SDavid du Colombier * No need to ilock t here: any manipulation of t
1872e976735SDavid du Colombier * requires tdel(t) and this must be done with a
1882e976735SDavid du Colombier * lock to tt held. We have tt, so the tdel will
1892e976735SDavid du Colombier * wait until we're done
1902e976735SDavid du Colombier */
191e288d156SDavid du Colombier when = t->twhen;
1929a747e4fSDavid du Colombier if(when > now){
1939a747e4fSDavid du Colombier timerset(when);
194ef9eff0bSDavid du Colombier iunlock(tt);
1959a747e4fSDavid du Colombier if(callhzclock)
1969a747e4fSDavid du Colombier hzclock(u);
1979a747e4fSDavid du Colombier return;
1989a747e4fSDavid du Colombier }
199e288d156SDavid du Colombier tt->head = t->tnext;
200e288d156SDavid du Colombier assert(t->tt == tt);
201e288d156SDavid du Colombier t->tt = nil;
2029a747e4fSDavid du Colombier fcallcount[m->machno]++;
2039a747e4fSDavid du Colombier iunlock(tt);
20428495efeSDavid du Colombier if(t->tf)
205e288d156SDavid du Colombier (*t->tf)(u, t);
20628495efeSDavid du Colombier else
2079a747e4fSDavid du Colombier callhzclock++;
2089a747e4fSDavid du Colombier ilock(tt);
20928495efeSDavid du Colombier if(t->tmode == Tperiodic)
21028495efeSDavid du Colombier tadd(tt, t);
211*27acba7cSDavid du Colombier if (--count <= 0) {
212*27acba7cSDavid du Colombier count = Maxtimerloops;
213*27acba7cSDavid du Colombier iprint("timerintr: probably stuck in while loop; "
214*27acba7cSDavid du Colombier "scrutinise clock.c or use faster cycle "
215*27acba7cSDavid du Colombier "counter\n");
216*27acba7cSDavid du Colombier }
2179a747e4fSDavid du Colombier }
2189a747e4fSDavid du Colombier iunlock(tt);
2199a747e4fSDavid du Colombier }
2209a747e4fSDavid du Colombier
2219a747e4fSDavid du Colombier void
timersinit(void)2229a747e4fSDavid du Colombier timersinit(void)
2239a747e4fSDavid du Colombier {
2249a747e4fSDavid du Colombier Timer *t;
2259a747e4fSDavid du Colombier
2269acf0835SDavid du Colombier /*
227fc567a6bSDavid du Colombier * T->tf == nil means the HZ clock for this processor.
2289acf0835SDavid du Colombier */
229*27acba7cSDavid du Colombier timersinited = 1;
230e288d156SDavid du Colombier todinit();
2319a747e4fSDavid du Colombier t = malloc(sizeof(*t));
232aa72973aSDavid du Colombier if(t == nil)
233aa72973aSDavid du Colombier error(Enomem);
234e288d156SDavid du Colombier t->tmode = Tperiodic;
235e288d156SDavid du Colombier t->tt = nil;
236e288d156SDavid du Colombier t->tns = 1000000000/HZ;
237e288d156SDavid du Colombier t->tf = nil;
2389a747e4fSDavid du Colombier timeradd(t);
2399a747e4fSDavid du Colombier }
2409a747e4fSDavid du Colombier
2415243b8d1SDavid du Colombier Timer*
addclock0link(void (* f)(void),int ms)242d9306527SDavid du Colombier addclock0link(void (*f)(void), int ms)
2439a747e4fSDavid du Colombier {
2449a747e4fSDavid du Colombier Timer *nt;
24528495efeSDavid du Colombier uvlong when;
2469a747e4fSDavid du Colombier
247*27acba7cSDavid du Colombier if(!timersinited)
248*27acba7cSDavid du Colombier panic("addclock0link: timersinit not called yet");
249e288d156SDavid du Colombier /* Synchronize to hztimer if ms is 0 */
2509a747e4fSDavid du Colombier nt = malloc(sizeof(Timer));
251aa72973aSDavid du Colombier if(nt == nil)
252aa72973aSDavid du Colombier error(Enomem);
253d9306527SDavid du Colombier if(ms == 0)
254d9306527SDavid du Colombier ms = 1000/HZ;
255e288d156SDavid du Colombier nt->tns = (vlong)ms*1000000LL;
256e288d156SDavid du Colombier nt->tmode = Tperiodic;
257e288d156SDavid du Colombier nt->tt = nil;
258e288d156SDavid du Colombier nt->tf = (void (*)(Ureg*, Timer*))f;
259e288d156SDavid du Colombier
2609a747e4fSDavid du Colombier ilock(&timers[0]);
26128495efeSDavid du Colombier when = tadd(&timers[0], nt);
26228495efeSDavid du Colombier if(when)
26328495efeSDavid du Colombier timerset(when);
2649a747e4fSDavid du Colombier iunlock(&timers[0]);
2655243b8d1SDavid du Colombier return nt;
26680ee5cbfSDavid du Colombier }
2673ff48bf5SDavid du Colombier
2683ff48bf5SDavid du Colombier /*
2693ff48bf5SDavid du Colombier * This tk2ms avoids overflows that the macro version is prone to.
2703ff48bf5SDavid du Colombier * It is a LOT slower so shouldn't be used if you're just converting
2713ff48bf5SDavid du Colombier * a delta.
2723ff48bf5SDavid du Colombier */
2733ff48bf5SDavid du Colombier ulong
tk2ms(ulong ticks)2743ff48bf5SDavid du Colombier tk2ms(ulong ticks)
2753ff48bf5SDavid du Colombier {
2763ff48bf5SDavid du Colombier uvlong t, hz;
2773ff48bf5SDavid du Colombier
2783ff48bf5SDavid du Colombier t = ticks;
2793ff48bf5SDavid du Colombier hz = HZ;
2803ff48bf5SDavid du Colombier t *= 1000L;
2813ff48bf5SDavid du Colombier t = t/hz;
2823ff48bf5SDavid du Colombier ticks = t;
2833ff48bf5SDavid du Colombier return ticks;
2843ff48bf5SDavid du Colombier }
2853ff48bf5SDavid du Colombier
2863ff48bf5SDavid du Colombier ulong
ms2tk(ulong ms)2873ff48bf5SDavid du Colombier ms2tk(ulong ms)
2883ff48bf5SDavid du Colombier {
2893ff48bf5SDavid du Colombier /* avoid overflows at the cost of precision */
2903ff48bf5SDavid du Colombier if(ms >= 1000000000/HZ)
2913ff48bf5SDavid du Colombier return (ms/1000)*HZ;
2923ff48bf5SDavid du Colombier return (ms*HZ+500)/1000;
2933ff48bf5SDavid du Colombier }
294