1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "m8260.h"
7 #include "../port/error.h"
8
9 enum{
10 IRQ0 = 18,
11 Level = 0,
12 Edge = 1,
13 };
14
15 enum{
16 Qdir,
17 Qirq1,
18 Qirq2,
19 Qirq3,
20 Qirq4,
21 Qirq5,
22 Qirq6,
23 Qirq7,
24 Qmstimer,
25 Qfpgareset,
26 NIRQ,
27 };
28
29 static Dirtab irqdir[]={
30 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
31 "irq1", {Qirq1}, 0, 0666,
32 "irq2", {Qirq2}, 0, 0666,
33 "irq3", {Qirq1}, 0, 0666,
34 "irq4", {Qirq1}, 0, 0666,
35 "irq5", {Qirq1}, 0, 0666,
36 "irq6", {Qirq1}, 0, 0666,
37 "irq7", {Qirq1}, 0, 0666,
38 "mstimer", {Qmstimer}, 0, 0666,
39 "fpgareset", {Qfpgareset}, 0, 0222,
40 };
41
42 enum
43 {
44 CMinterrupt,
45 CMmode,
46 CMreset,
47 CMwait,
48 CMdebug,
49 };
50
51 Cmdtab irqmsg[] =
52 {
53 CMinterrupt, "interrupt", 2,
54 CMmode, "mode", 2,
55 CMreset, "reset", 1,
56 CMwait, "wait", 1,
57 CMdebug, "debug", 1,
58 };
59
60 typedef struct Irqconfig Irqconfig;
61 struct Irqconfig {
62 int intenable; /* Interrupts are enabled */
63 int mode; /* level == 0; edge == 1 */
64 ulong interrupts; /* Count interrupts */
65 ulong sleepints; /* interrupt count when waiting */
66 Rendez r; /* Rendez-vous point for interrupt waiting */
67 Irqconfig *next;
68 Timer;
69 };
70
71 Irqconfig *irqconfig[NIRQ]; /* irqconfig[0] is not used */
72 Lock irqlock;
73
74 static void interrupt(Ureg*, void*);
75 void dumpvno(void);
76
77 #ifdef notdef
78 ulong multiplier;
79
80 ulong
s(void)81 µs(void)
82 {
83 uvlong x;
84
85 if(multiplier == 0){
86 multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
87 print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
88 }
89 cycles(&x);
90 return (x*multiplier) >> 16;
91 }
92 #endif
93
94 static void
ticmstimer(Ureg *,Timer * t)95 ticmstimer(Ureg*, Timer *t)
96 {
97 Irqconfig *ic;
98
99 ic = t->ta;
100 ic->interrupts++;
101 wakeup(&ic->r);
102 }
103
104 void
irqenable(Irqconfig * ic,int irq)105 irqenable(Irqconfig *ic, int irq)
106 {
107 /* call with ilock(&irqlock) held */
108
109 if (ic->intenable)
110 return;
111 if (irq == Qmstimer){
112 if (ic->tnext == nil)
113 ic->tns = MS2NS(ic->mode);
114 ic->tmode = Tperiodic;
115 timeradd(&ic->Timer);
116 }else{
117 if (irqconfig[irq]){
118 ic->next = irqconfig[irq];
119 irqconfig[irq] = ic;
120 }else{
121 ic->next = nil;
122 irqconfig[irq] = ic;
123 intrenable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
124 }
125 }
126 ic->intenable = 1;
127 }
128
129 void
irqdisable(Irqconfig * ic,int irq)130 irqdisable(Irqconfig *ic, int irq)
131 {
132 Irqconfig **pic;
133
134 /* call with ilock(&irqlock) held */
135
136 if (ic->intenable == 0)
137 return;
138 if (irq == Qmstimer){
139 timerdel(&ic->Timer);
140 }else{
141 for(pic = &irqconfig[irq]; *pic != ic; pic = &(*pic)->next)
142 assert(*pic);
143 *pic = (*pic)->next;
144 if (irqconfig[irq] == nil)
145 intrdisable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
146 }
147 ic->intenable = 0;
148 }
149
150 static Chan*
irqattach(char * spec)151 irqattach(char *spec)
152 {
153 return devattach('b', spec);
154 }
155
156 static Walkqid*
irqwalk(Chan * c,Chan * nc,char ** name,int nname)157 irqwalk(Chan *c, Chan *nc, char **name, int nname)
158 {
159 return devwalk(c, nc, name,nname, irqdir, nelem(irqdir), devgen);
160 }
161
162 static int
irqstat(Chan * c,uchar * dp,int n)163 irqstat(Chan *c, uchar *dp, int n)
164 {
165 return devstat(c, dp, n, irqdir, nelem(irqdir), devgen);
166 }
167
168 static Chan*
irqopen(Chan * c,int omode)169 irqopen(Chan *c, int omode)
170 {
171 Irqconfig *ic;
172 int irq;
173
174 irq = (ulong)c->qid.path;
175 if(irq != Qdir){
176 ic = mallocz(sizeof(Irqconfig), 1);
177 ic->tf = ticmstimer;
178 ic->ta = ic;
179 if (irq == Qmstimer)
180 ic->mode = 1000;
181 c->aux = ic;
182 }
183 return devopen(c, omode, irqdir, nelem(irqdir), devgen);
184 }
185
186 static void
irqclose(Chan * c)187 irqclose(Chan *c)
188 {
189 int irq;
190 Irqconfig *ic;
191
192 irq = (ulong)c->qid.path;
193 if(irq == Qdir)
194 return;
195 ic = c->aux;
196 if (irq > Qmstimer)
197 return;
198 ilock(&irqlock);
199 irqdisable(ic, irq);
200 iunlock(&irqlock);
201 free(ic);
202 }
203
204 static int
irqtfn(void * arg)205 irqtfn(void *arg)
206 {
207 Irqconfig *ic;
208
209 ic = arg;
210 return ic->sleepints != ic->interrupts;
211 }
212
213 static long
irqread(Chan * c,void * buf,long n,vlong)214 irqread(Chan *c, void *buf, long n, vlong)
215 {
216 int irq;
217 Irqconfig *ic;
218 char tmp[24];
219
220 if(n <= 0)
221 return n;
222 irq = (ulong)c->qid.path;
223 if(irq == Qdir)
224 return devdirread(c, buf, n, irqdir, nelem(irqdir), devgen);
225 if(irq > Qmstimer){
226 print("irqread 0x%llux\n", c->qid.path);
227 error(Egreg);
228 }
229 ic = c->aux;
230 if (ic->intenable == 0)
231 error("disabled");
232 ic->sleepints = ic->interrupts;
233 sleep(&ic->r, irqtfn, ic);
234 if (irq == Qmstimer)
235 snprint(tmp, sizeof tmp, "%11lud %d", ic->interrupts, ic->mode);
236 else
237 snprint(tmp, sizeof tmp, "%11lud %s", ic->interrupts, ic->mode ?"edge":"level");
238 n = readstr(0, buf, n, tmp);
239 return n;
240 }
241
242 static long
irqwrite(Chan * c,void * a,long n,vlong)243 irqwrite(Chan *c, void *a, long n, vlong)
244 {
245 int irq;
246 Irqconfig *ic;
247 Cmdbuf *cb;
248 Cmdtab *ct;
249
250 if(n <= 0)
251 return n;
252
253 irq = (ulong)c->qid.path;
254 if(irq <= 0 || irq >= nelem(irqdir)){
255 print("irqwrite 0x%llux\n", c->qid.path);
256 error(Egreg);
257 }
258 if (irq == Qfpgareset){
259 if (strncmp(a, "reset", 5) == 0)
260 fpgareset();
261 else
262 error(Egreg);
263 return n;
264 }
265 ic = c->aux;
266
267 cb = parsecmd(a, n);
268
269 if(waserror()) {
270 free(cb);
271 nexterror();
272 }
273 ct = lookupcmd(cb, irqmsg, nelem(irqmsg));
274 switch(ct->index) {
275 case CMinterrupt:
276 /* Turn interrupts on or off */
277 if (strcmp(cb->f[1], "on") == 0){
278 ilock(&irqlock);
279 irqenable(ic, irq);
280 iomem->siprr = 0x65009770;
281 iunlock(&irqlock);
282 }else if (strcmp(cb->f[1], "off") == 0){
283 ilock(&irqlock);
284 irqdisable(ic, irq);
285 iunlock(&irqlock);
286 }else
287 error(Ebadarg);
288 break;
289 case CMmode:
290 /* Set mode */
291 if (irq == Qmstimer){
292 ic->mode = strtol(cb->f[1], nil, 0);
293 if (ic->mode <= 0){
294 ic->tns = MS2NS(1000);
295 ic->mode = 1000;
296 error(Ebadarg);
297 }
298 ic->tns = MS2NS(ic->mode);
299 }else if (strcmp(cb->f[1], "level") == 0){
300 ic->mode = Level;
301 iomem->siexr &= ~(0x8000 >> irq);
302 }else if (strcmp(cb->f[1], "edge") == 0){
303 ic->mode = Edge;
304 iomem->siexr |= 0x8000 >> irq;
305 }else
306 error(Ebadarg);
307 break;
308 case CMreset:
309 ic->interrupts = 0;
310 break;
311 case CMwait:
312 if (ic->intenable == 0)
313 error("interrupts are off");
314 ic->sleepints = ic->interrupts;
315 sleep(&ic->r, irqtfn, ic);
316 break;
317 case CMdebug:
318 print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux, siprr 0x%lux\n",
319 iomem->simr_h, iomem->simr_l,
320 iomem->sipnr_h, iomem->sipnr_l,
321 iomem->siexr, iomem->siprr);
322 // dumpvno();
323 }
324 poperror();
325 free(cb);
326
327 /* Irqi */
328 return n;
329 }
330
331 static void
interrupt(Ureg *,void * arg)332 interrupt(Ureg*, void *arg)
333 {
334 Irqconfig **pic, *ic;
335 int irq;
336
337 pic = arg;
338 irq = pic - irqconfig;
339 if (irq <= 0 || irq > nelem(irqdir)){
340 print("Unexpected interrupt: %d\n", irq);
341 return;
342 }
343 ilock(&irqlock);
344 if (irq <= Qirq7)
345 iomem->sipnr_h |= 0x8000 >> irq; /* Clear the interrupt */
346 for(ic = *pic; ic; ic = ic->next){
347 ic->interrupts++;
348 wakeup(&ic->r);
349 }
350 iunlock(&irqlock);
351 }
352
353 Dev irqdevtab = {
354 'b',
355 "irq",
356
357 devreset,
358 devinit,
359 devshutdown,
360 irqattach,
361 irqwalk,
362 irqstat,
363 irqopen,
364 devcreate,
365 irqclose,
366 irqread,
367 devbread,
368 irqwrite,
369 devbwrite,
370 devremove,
371 devwstat,
372 };
373