xref: /plan9-contrib/sys/src/9/ppc/devirq.c (revision 5d9de2d38d2503efca29e12e0e32036368a7a75f)
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