1458db832SDavid du Colombier #include "u.h"
2458db832SDavid du Colombier #include "../port/lib.h"
3458db832SDavid du Colombier #include "mem.h"
4458db832SDavid du Colombier #include "dat.h"
5458db832SDavid du Colombier #include "fns.h"
6458db832SDavid du Colombier #include "m8260.h"
7458db832SDavid du Colombier #include "../port/error.h"
8458db832SDavid du Colombier
9458db832SDavid du Colombier enum{
10458db832SDavid du Colombier IRQ0 = 18,
11458db832SDavid du Colombier Level = 0,
12458db832SDavid du Colombier Edge = 1,
13458db832SDavid du Colombier };
14458db832SDavid du Colombier
15458db832SDavid du Colombier enum{
16458db832SDavid du Colombier Qdir,
17458db832SDavid du Colombier Qirq1,
18458db832SDavid du Colombier Qirq2,
19458db832SDavid du Colombier Qirq3,
20458db832SDavid du Colombier Qirq4,
21458db832SDavid du Colombier Qirq5,
22458db832SDavid du Colombier Qirq6,
23458db832SDavid du Colombier Qirq7,
24458db832SDavid du Colombier Qmstimer,
25458db832SDavid du Colombier Qfpgareset,
26458db832SDavid du Colombier NIRQ,
27458db832SDavid du Colombier };
28458db832SDavid du Colombier
29458db832SDavid du Colombier static Dirtab irqdir[]={
30458db832SDavid du Colombier ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
31458db832SDavid du Colombier "irq1", {Qirq1}, 0, 0666,
32458db832SDavid du Colombier "irq2", {Qirq2}, 0, 0666,
33458db832SDavid du Colombier "irq3", {Qirq1}, 0, 0666,
34458db832SDavid du Colombier "irq4", {Qirq1}, 0, 0666,
35458db832SDavid du Colombier "irq5", {Qirq1}, 0, 0666,
36458db832SDavid du Colombier "irq6", {Qirq1}, 0, 0666,
37458db832SDavid du Colombier "irq7", {Qirq1}, 0, 0666,
38458db832SDavid du Colombier "mstimer", {Qmstimer}, 0, 0666,
39458db832SDavid du Colombier "fpgareset", {Qfpgareset}, 0, 0222,
40458db832SDavid du Colombier };
41458db832SDavid du Colombier
42458db832SDavid du Colombier enum
43458db832SDavid du Colombier {
44458db832SDavid du Colombier CMinterrupt,
45458db832SDavid du Colombier CMmode,
46458db832SDavid du Colombier CMreset,
47458db832SDavid du Colombier CMwait,
48458db832SDavid du Colombier CMdebug,
49458db832SDavid du Colombier };
50458db832SDavid du Colombier
51458db832SDavid du Colombier Cmdtab irqmsg[] =
52458db832SDavid du Colombier {
53458db832SDavid du Colombier CMinterrupt, "interrupt", 2,
54458db832SDavid du Colombier CMmode, "mode", 2,
55458db832SDavid du Colombier CMreset, "reset", 1,
56458db832SDavid du Colombier CMwait, "wait", 1,
57458db832SDavid du Colombier CMdebug, "debug", 1,
58458db832SDavid du Colombier };
59458db832SDavid du Colombier
60458db832SDavid du Colombier typedef struct Irqconfig Irqconfig;
61458db832SDavid du Colombier struct Irqconfig {
62458db832SDavid du Colombier int intenable; /* Interrupts are enabled */
63458db832SDavid du Colombier int mode; /* level == 0; edge == 1 */
64458db832SDavid du Colombier ulong interrupts; /* Count interrupts */
6506578a4fSDavid du Colombier ulong sleepints; /* interrupt count when waiting */
66458db832SDavid du Colombier Rendez r; /* Rendez-vous point for interrupt waiting */
67458db832SDavid du Colombier Irqconfig *next;
68458db832SDavid du Colombier Timer;
69458db832SDavid du Colombier };
70458db832SDavid du Colombier
71458db832SDavid du Colombier Irqconfig *irqconfig[NIRQ]; /* irqconfig[0] is not used */
72458db832SDavid du Colombier Lock irqlock;
73458db832SDavid du Colombier
74458db832SDavid du Colombier static void interrupt(Ureg*, void*);
7506578a4fSDavid du Colombier void dumpvno(void);
76458db832SDavid du Colombier
77*5d9de2d3SDavid du Colombier #ifdef notdef
78*5d9de2d3SDavid du Colombier ulong multiplier;
79*5d9de2d3SDavid du Colombier
80*5d9de2d3SDavid du Colombier ulong
s(void)81*5d9de2d3SDavid du Colombier µs(void)
82*5d9de2d3SDavid du Colombier {
83*5d9de2d3SDavid du Colombier uvlong x;
84*5d9de2d3SDavid du Colombier
85*5d9de2d3SDavid du Colombier if(multiplier == 0){
86*5d9de2d3SDavid du Colombier multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
87*5d9de2d3SDavid du Colombier print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
88*5d9de2d3SDavid du Colombier }
89*5d9de2d3SDavid du Colombier cycles(&x);
90*5d9de2d3SDavid du Colombier return (x*multiplier) >> 16;
91*5d9de2d3SDavid du Colombier }
92*5d9de2d3SDavid du Colombier #endif
93*5d9de2d3SDavid du Colombier
94458db832SDavid du Colombier static void
ticmstimer(Ureg *,Timer * t)95458db832SDavid du Colombier ticmstimer(Ureg*, Timer *t)
96458db832SDavid du Colombier {
97458db832SDavid du Colombier Irqconfig *ic;
98458db832SDavid du Colombier
99e288d156SDavid du Colombier ic = t->ta;
100458db832SDavid du Colombier ic->interrupts++;
101458db832SDavid du Colombier wakeup(&ic->r);
102458db832SDavid du Colombier }
103458db832SDavid du Colombier
104458db832SDavid du Colombier void
irqenable(Irqconfig * ic,int irq)105458db832SDavid du Colombier irqenable(Irqconfig *ic, int irq)
106458db832SDavid du Colombier {
107458db832SDavid du Colombier /* call with ilock(&irqlock) held */
108458db832SDavid du Colombier
109458db832SDavid du Colombier if (ic->intenable)
110458db832SDavid du Colombier return;
111458db832SDavid du Colombier if (irq == Qmstimer){
1124fec87e5SDavid du Colombier if (ic->tnext == nil)
113e288d156SDavid du Colombier ic->tns = MS2NS(ic->mode);
114e288d156SDavid du Colombier ic->tmode = Tperiodic;
115458db832SDavid du Colombier timeradd(&ic->Timer);
116458db832SDavid du Colombier }else{
117458db832SDavid du Colombier if (irqconfig[irq]){
118458db832SDavid du Colombier ic->next = irqconfig[irq];
119458db832SDavid du Colombier irqconfig[irq] = ic;
120458db832SDavid du Colombier }else{
121458db832SDavid du Colombier ic->next = nil;
122458db832SDavid du Colombier irqconfig[irq] = ic;
123458db832SDavid du Colombier intrenable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
124458db832SDavid du Colombier }
125458db832SDavid du Colombier }
126458db832SDavid du Colombier ic->intenable = 1;
127458db832SDavid du Colombier }
128458db832SDavid du Colombier
129458db832SDavid du Colombier void
irqdisable(Irqconfig * ic,int irq)130458db832SDavid du Colombier irqdisable(Irqconfig *ic, int irq)
131458db832SDavid du Colombier {
132458db832SDavid du Colombier Irqconfig **pic;
133458db832SDavid du Colombier
134458db832SDavid du Colombier /* call with ilock(&irqlock) held */
135458db832SDavid du Colombier
136458db832SDavid du Colombier if (ic->intenable == 0)
137458db832SDavid du Colombier return;
138458db832SDavid du Colombier if (irq == Qmstimer){
139458db832SDavid du Colombier timerdel(&ic->Timer);
140458db832SDavid du Colombier }else{
141458db832SDavid du Colombier for(pic = &irqconfig[irq]; *pic != ic; pic = &(*pic)->next)
142458db832SDavid du Colombier assert(*pic);
143458db832SDavid du Colombier *pic = (*pic)->next;
144458db832SDavid du Colombier if (irqconfig[irq] == nil)
145458db832SDavid du Colombier intrdisable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
146458db832SDavid du Colombier }
147458db832SDavid du Colombier ic->intenable = 0;
148458db832SDavid du Colombier }
149458db832SDavid du Colombier
150458db832SDavid du Colombier static Chan*
irqattach(char * spec)151458db832SDavid du Colombier irqattach(char *spec)
152458db832SDavid du Colombier {
153458db832SDavid du Colombier return devattach('b', spec);
154458db832SDavid du Colombier }
155458db832SDavid du Colombier
156458db832SDavid du Colombier static Walkqid*
irqwalk(Chan * c,Chan * nc,char ** name,int nname)157458db832SDavid du Colombier irqwalk(Chan *c, Chan *nc, char **name, int nname)
158458db832SDavid du Colombier {
159458db832SDavid du Colombier return devwalk(c, nc, name,nname, irqdir, nelem(irqdir), devgen);
160458db832SDavid du Colombier }
161458db832SDavid du Colombier
162458db832SDavid du Colombier static int
irqstat(Chan * c,uchar * dp,int n)163458db832SDavid du Colombier irqstat(Chan *c, uchar *dp, int n)
164458db832SDavid du Colombier {
165458db832SDavid du Colombier return devstat(c, dp, n, irqdir, nelem(irqdir), devgen);
166458db832SDavid du Colombier }
167458db832SDavid du Colombier
168458db832SDavid du Colombier static Chan*
irqopen(Chan * c,int omode)169458db832SDavid du Colombier irqopen(Chan *c, int omode)
170458db832SDavid du Colombier {
171458db832SDavid du Colombier Irqconfig *ic;
172458db832SDavid du Colombier int irq;
173458db832SDavid du Colombier
174458db832SDavid du Colombier irq = (ulong)c->qid.path;
175458db832SDavid du Colombier if(irq != Qdir){
176458db832SDavid du Colombier ic = mallocz(sizeof(Irqconfig), 1);
177e288d156SDavid du Colombier ic->tf = ticmstimer;
178e288d156SDavid du Colombier ic->ta = ic;
179458db832SDavid du Colombier if (irq == Qmstimer)
180458db832SDavid du Colombier ic->mode = 1000;
181458db832SDavid du Colombier c->aux = ic;
182458db832SDavid du Colombier }
183458db832SDavid du Colombier return devopen(c, omode, irqdir, nelem(irqdir), devgen);
184458db832SDavid du Colombier }
185458db832SDavid du Colombier
186458db832SDavid du Colombier static void
irqclose(Chan * c)187458db832SDavid du Colombier irqclose(Chan *c)
188458db832SDavid du Colombier {
189458db832SDavid du Colombier int irq;
190458db832SDavid du Colombier Irqconfig *ic;
191458db832SDavid du Colombier
192458db832SDavid du Colombier irq = (ulong)c->qid.path;
193458db832SDavid du Colombier if(irq == Qdir)
194458db832SDavid du Colombier return;
195458db832SDavid du Colombier ic = c->aux;
196458db832SDavid du Colombier if (irq > Qmstimer)
197458db832SDavid du Colombier return;
198458db832SDavid du Colombier ilock(&irqlock);
199458db832SDavid du Colombier irqdisable(ic, irq);
200458db832SDavid du Colombier iunlock(&irqlock);
201458db832SDavid du Colombier free(ic);
202458db832SDavid du Colombier }
203458db832SDavid du Colombier
20406578a4fSDavid du Colombier static int
irqtfn(void * arg)20506578a4fSDavid du Colombier irqtfn(void *arg)
20606578a4fSDavid du Colombier {
20706578a4fSDavid du Colombier Irqconfig *ic;
20806578a4fSDavid du Colombier
20906578a4fSDavid du Colombier ic = arg;
21006578a4fSDavid du Colombier return ic->sleepints != ic->interrupts;
21106578a4fSDavid du Colombier }
21206578a4fSDavid du Colombier
213458db832SDavid du Colombier static long
irqread(Chan * c,void * buf,long n,vlong)214458db832SDavid du Colombier irqread(Chan *c, void *buf, long n, vlong)
215458db832SDavid du Colombier {
216458db832SDavid du Colombier int irq;
217458db832SDavid du Colombier Irqconfig *ic;
218458db832SDavid du Colombier char tmp[24];
219458db832SDavid du Colombier
220458db832SDavid du Colombier if(n <= 0)
221458db832SDavid du Colombier return n;
222458db832SDavid du Colombier irq = (ulong)c->qid.path;
223458db832SDavid du Colombier if(irq == Qdir)
224458db832SDavid du Colombier return devdirread(c, buf, n, irqdir, nelem(irqdir), devgen);
22506578a4fSDavid du Colombier if(irq > Qmstimer){
226458db832SDavid du Colombier print("irqread 0x%llux\n", c->qid.path);
227458db832SDavid du Colombier error(Egreg);
228458db832SDavid du Colombier }
229458db832SDavid du Colombier ic = c->aux;
230458db832SDavid du Colombier if (ic->intenable == 0)
231458db832SDavid du Colombier error("disabled");
23206578a4fSDavid du Colombier ic->sleepints = ic->interrupts;
23306578a4fSDavid du Colombier sleep(&ic->r, irqtfn, ic);
234458db832SDavid du Colombier if (irq == Qmstimer)
235458db832SDavid du Colombier snprint(tmp, sizeof tmp, "%11lud %d", ic->interrupts, ic->mode);
236458db832SDavid du Colombier else
237458db832SDavid du Colombier snprint(tmp, sizeof tmp, "%11lud %s", ic->interrupts, ic->mode ?"edge":"level");
238458db832SDavid du Colombier n = readstr(0, buf, n, tmp);
239458db832SDavid du Colombier return n;
240458db832SDavid du Colombier }
241458db832SDavid du Colombier
242458db832SDavid du Colombier static long
irqwrite(Chan * c,void * a,long n,vlong)243458db832SDavid du Colombier irqwrite(Chan *c, void *a, long n, vlong)
244458db832SDavid du Colombier {
245458db832SDavid du Colombier int irq;
246458db832SDavid du Colombier Irqconfig *ic;
247458db832SDavid du Colombier Cmdbuf *cb;
248458db832SDavid du Colombier Cmdtab *ct;
249458db832SDavid du Colombier
250458db832SDavid du Colombier if(n <= 0)
251458db832SDavid du Colombier return n;
252458db832SDavid du Colombier
253458db832SDavid du Colombier irq = (ulong)c->qid.path;
254458db832SDavid du Colombier if(irq <= 0 || irq >= nelem(irqdir)){
255458db832SDavid du Colombier print("irqwrite 0x%llux\n", c->qid.path);
256458db832SDavid du Colombier error(Egreg);
257458db832SDavid du Colombier }
258458db832SDavid du Colombier if (irq == Qfpgareset){
259458db832SDavid du Colombier if (strncmp(a, "reset", 5) == 0)
260458db832SDavid du Colombier fpgareset();
261458db832SDavid du Colombier else
262458db832SDavid du Colombier error(Egreg);
263458db832SDavid du Colombier return n;
264458db832SDavid du Colombier }
265458db832SDavid du Colombier ic = c->aux;
266458db832SDavid du Colombier
267458db832SDavid du Colombier cb = parsecmd(a, n);
268458db832SDavid du Colombier
269458db832SDavid du Colombier if(waserror()) {
270458db832SDavid du Colombier free(cb);
271458db832SDavid du Colombier nexterror();
272458db832SDavid du Colombier }
273458db832SDavid du Colombier ct = lookupcmd(cb, irqmsg, nelem(irqmsg));
274458db832SDavid du Colombier switch(ct->index) {
275458db832SDavid du Colombier case CMinterrupt:
276458db832SDavid du Colombier /* Turn interrupts on or off */
277458db832SDavid du Colombier if (strcmp(cb->f[1], "on") == 0){
278458db832SDavid du Colombier ilock(&irqlock);
279458db832SDavid du Colombier irqenable(ic, irq);
28006578a4fSDavid du Colombier iomem->siprr = 0x65009770;
281458db832SDavid du Colombier iunlock(&irqlock);
282458db832SDavid du Colombier }else if (strcmp(cb->f[1], "off") == 0){
283458db832SDavid du Colombier ilock(&irqlock);
284458db832SDavid du Colombier irqdisable(ic, irq);
285458db832SDavid du Colombier iunlock(&irqlock);
286458db832SDavid du Colombier }else
287458db832SDavid du Colombier error(Ebadarg);
288458db832SDavid du Colombier break;
289458db832SDavid du Colombier case CMmode:
290458db832SDavid du Colombier /* Set mode */
291458db832SDavid du Colombier if (irq == Qmstimer){
292458db832SDavid du Colombier ic->mode = strtol(cb->f[1], nil, 0);
293458db832SDavid du Colombier if (ic->mode <= 0){
294e288d156SDavid du Colombier ic->tns = MS2NS(1000);
295458db832SDavid du Colombier ic->mode = 1000;
296458db832SDavid du Colombier error(Ebadarg);
297458db832SDavid du Colombier }
298e288d156SDavid du Colombier ic->tns = MS2NS(ic->mode);
299458db832SDavid du Colombier }else if (strcmp(cb->f[1], "level") == 0){
30006578a4fSDavid du Colombier ic->mode = Level;
301458db832SDavid du Colombier iomem->siexr &= ~(0x8000 >> irq);
302458db832SDavid du Colombier }else if (strcmp(cb->f[1], "edge") == 0){
30306578a4fSDavid du Colombier ic->mode = Edge;
304458db832SDavid du Colombier iomem->siexr |= 0x8000 >> irq;
305458db832SDavid du Colombier }else
306458db832SDavid du Colombier error(Ebadarg);
307458db832SDavid du Colombier break;
308458db832SDavid du Colombier case CMreset:
309458db832SDavid du Colombier ic->interrupts = 0;
310458db832SDavid du Colombier break;
311458db832SDavid du Colombier case CMwait:
312458db832SDavid du Colombier if (ic->intenable == 0)
313458db832SDavid du Colombier error("interrupts are off");
31406578a4fSDavid du Colombier ic->sleepints = ic->interrupts;
31506578a4fSDavid du Colombier sleep(&ic->r, irqtfn, ic);
316458db832SDavid du Colombier break;
317458db832SDavid du Colombier case CMdebug:
31806578a4fSDavid du Colombier print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux, siprr 0x%lux\n",
319458db832SDavid du Colombier iomem->simr_h, iomem->simr_l,
320458db832SDavid du Colombier iomem->sipnr_h, iomem->sipnr_l,
32106578a4fSDavid du Colombier iomem->siexr, iomem->siprr);
322*5d9de2d3SDavid du Colombier // dumpvno();
323458db832SDavid du Colombier }
324458db832SDavid du Colombier poperror();
325458db832SDavid du Colombier free(cb);
326458db832SDavid du Colombier
327458db832SDavid du Colombier /* Irqi */
328458db832SDavid du Colombier return n;
329458db832SDavid du Colombier }
330458db832SDavid du Colombier
331458db832SDavid du Colombier static void
interrupt(Ureg *,void * arg)332458db832SDavid du Colombier interrupt(Ureg*, void *arg)
333458db832SDavid du Colombier {
334458db832SDavid du Colombier Irqconfig **pic, *ic;
335458db832SDavid du Colombier int irq;
336458db832SDavid du Colombier
337458db832SDavid du Colombier pic = arg;
338458db832SDavid du Colombier irq = pic - irqconfig;
339458db832SDavid du Colombier if (irq <= 0 || irq > nelem(irqdir)){
340458db832SDavid du Colombier print("Unexpected interrupt: %d\n", irq);
341458db832SDavid du Colombier return;
342458db832SDavid du Colombier }
343e288d156SDavid du Colombier ilock(&irqlock);
344458db832SDavid du Colombier if (irq <= Qirq7)
345458db832SDavid du Colombier iomem->sipnr_h |= 0x8000 >> irq; /* Clear the interrupt */
346458db832SDavid du Colombier for(ic = *pic; ic; ic = ic->next){
347458db832SDavid du Colombier ic->interrupts++;
348458db832SDavid du Colombier wakeup(&ic->r);
349458db832SDavid du Colombier }
350458db832SDavid du Colombier iunlock(&irqlock);
351458db832SDavid du Colombier }
352458db832SDavid du Colombier
353458db832SDavid du Colombier Dev irqdevtab = {
354458db832SDavid du Colombier 'b',
355458db832SDavid du Colombier "irq",
356458db832SDavid du Colombier
357458db832SDavid du Colombier devreset,
358458db832SDavid du Colombier devinit,
359458db832SDavid du Colombier devshutdown,
360458db832SDavid du Colombier irqattach,
361458db832SDavid du Colombier irqwalk,
362458db832SDavid du Colombier irqstat,
363458db832SDavid du Colombier irqopen,
364458db832SDavid du Colombier devcreate,
365458db832SDavid du Colombier irqclose,
366458db832SDavid du Colombier irqread,
367458db832SDavid du Colombier devbread,
368458db832SDavid du Colombier irqwrite,
369458db832SDavid du Colombier devbwrite,
370458db832SDavid du Colombier devremove,
371458db832SDavid du Colombier devwstat,
372458db832SDavid du Colombier };
373