1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7
8 enum {
9 Ntimer = 4 /* maximum allowed by hardware */
10 };
11
12 static struct {
13 Lock;
14 int init;
15 int ntimer; /* actual timers on this chip revision */
16 GTimer t[Ntimer];
17 } cpmtimers;
18
19 static uchar timerirq[] = {0x19, 0x12, 0x0C, 0x07};
20
21 static void gtimerinit(int, ushort*, ushort*);
22
23 static void
gtimerreset(void)24 gtimerreset(void)
25 {
26 IMM *io;
27 int i;
28
29 ilock(&cpmtimers);
30 if(!cpmtimers.init){
31 if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001)
32 cpmtimers.ntimer = 2;
33 else
34 cpmtimers.ntimer = Ntimer;
35 io = m->iomem;
36 io->tgcr = 0x2222; /* reset timers, low-power stop */
37 for(i=0; i<cpmtimers.ntimer; i++)
38 gtimerinit(i, &io->tmr1+i, &io->ter1+i);
39 cpmtimers.init = 1;
40 }
41 iunlock(&cpmtimers);
42 }
43
44 static void
gtimerintr(Ureg * ur,void * arg)45 gtimerintr(Ureg *ur, void *arg)
46 {
47 GTimer *t;
48
49 t = arg;
50 t->event = *t->ter;
51 *t->ter = t->event;
52 if(t->inuse && t->interrupt != nil)
53 t->interrupt(ur, t->arg, t);
54 }
55
56 static void
gtimerinit(int i,ushort * tmr,ushort * ter)57 gtimerinit(int i, ushort *tmr, ushort *ter)
58 {
59 GTimer *t;
60 char name[KNAMELEN];
61
62 snprint(name, sizeof(name), "timer.%d", i);
63 t = &cpmtimers.t[i];
64 t->x = i*4; /* field in tgcr */
65 t->inuse = 0;
66 t->interrupt = nil;
67 t->tmr = tmr;
68 t->trr = tmr+2;
69 t->tcr = tmr+4;
70 t->tcn = tmr+6;
71 t->ter = ter;
72 intrenable(VectorCPIC+timerirq[i], gtimerintr, t, BUSUNKNOWN, name);
73 }
74
75 GTimer*
gtimer(ushort mode,ushort ref,void (* intr)(Ureg *,void *,GTimer *),void * arg)76 gtimer(ushort mode, ushort ref, void (*intr)(Ureg*,void*,GTimer*), void *arg)
77 {
78 GTimer *t;
79 int i;
80
81 t = cpmtimers.t;
82 if(!cpmtimers.init)
83 gtimerreset();
84 ilock(&cpmtimers);
85 for(i=0; ; i++){
86 if(i >= cpmtimers.ntimer){
87 iunlock(&cpmtimers);
88 return nil;
89 }
90 if(t->inuse == 0)
91 break;
92 t++;
93 }
94 t->inuse = 1;
95 t->interrupt = intr;
96 t->arg = arg;
97 m->iomem->tgcr &= ~(0xF<<t->x); /* reset */
98 *t->tmr = mode;
99 *t->tcn = 0;
100 *t->trr = ref;
101 *t->ter = 0xFFFF;
102 iunlock(&cpmtimers);
103 return t;
104 }
105
106 void
gtimerset(GTimer * t,ushort mode,int usec)107 gtimerset(GTimer *t, ushort mode, int usec)
108 {
109 ulong ref, ps;
110 int clk;
111
112 if(usec <= 0)
113 return;
114 ref = usec*m->speed;
115 clk = mode & (3<<1);
116 if(ref >= 0x1000000 && clk == TimerSclk){
117 mode = (mode & ~clk) | TimerSclk16;
118 ref >>= 4;
119 } else if(clk == TimerSclk16)
120 ref >>= 4;
121 ps = (ref+(1<<16))/(1<<16); /* round up */
122 ref /= ps;
123 *t->tmr = ((ps-1)<<8) | (mode&0xFF);
124 *t->trr = ref;
125 }
126
127 void
gtimerstart(GTimer * t)128 gtimerstart(GTimer *t)
129 {
130 if(t){
131 ilock(&cpmtimers);
132 m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (1<<t->x); /* enable */
133 iunlock(&cpmtimers);
134 }
135 }
136
137 void
gtimerstop(GTimer * t)138 gtimerstop(GTimer *t)
139 {
140 if(t){
141 ilock(&cpmtimers);
142 m->iomem->tgcr |= 2<<t->x; /* stop */
143 iunlock(&cpmtimers);
144 }
145 }
146
147 void
gtimerfree(GTimer * t)148 gtimerfree(GTimer *t)
149 {
150 if(t){
151 ilock(&cpmtimers);
152 t->inuse = 0;
153 *t->tmr = 0; /* disable interrupts */
154 *t->ter = 0xFFFF;
155 m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (2<<t->x); /* reset and stop */
156 iunlock(&cpmtimers);
157 }
158 }
159
160 #ifdef GTIMETEST
161 static void
gtintr(Ureg *,void *,GTimer *)162 gtintr(Ureg*, void*, GTimer*)
163 {
164 m->bcsr[4] ^= DisableVideoLamp; /* toggle an LED */
165 }
166
167 void
gtimetest(void)168 gtimetest(void)
169 {
170 GTimer *g;
171
172 g = gtimer(0, 0, gtintr, nil);
173 gtimerset(g, TimerORI|TimerRestart|TimerSclk, 64000);
174 gtimerstart(g);
175 delay(1);
176 print("started timer: #%4.4ux #%4.4ux %8.8lux #%4.4ux #%4.4ux\n", *g->tmr, *g->trr, m->iomem->tgcr, *g->tcn, *g->ter);
177 print("ter=#%8.8lux tmr=#%8.8lux trr=#%8.8lux\n", g->ter, g->tmr, g->trr);
178 }
179 #endif
180