plan9fox/sys/src/9/ppc/devirq.c
2011-03-30 19:35:09 +03:00

356 lines
6.2 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "m8260.h"
#include "../port/error.h"
enum{
IRQ0 = 18,
Level = 0,
Edge = 1,
};
enum{
Qdir,
Qirq1,
Qirq2,
Qirq3,
Qirq4,
Qirq5,
Qirq6,
Qirq7,
Qmstimer,
Qfpgareset,
NIRQ,
};
static Dirtab irqdir[]={
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"irq1", {Qirq1}, 0, 0666,
"irq2", {Qirq2}, 0, 0666,
"irq3", {Qirq1}, 0, 0666,
"irq4", {Qirq1}, 0, 0666,
"irq5", {Qirq1}, 0, 0666,
"irq6", {Qirq1}, 0, 0666,
"irq7", {Qirq1}, 0, 0666,
"mstimer", {Qmstimer}, 0, 0666,
"fpgareset", {Qfpgareset}, 0, 0222,
};
enum
{
CMinterrupt,
CMmode,
CMreset,
CMwait,
CMdebug,
};
Cmdtab irqmsg[] =
{
CMinterrupt, "interrupt", 2,
CMmode, "mode", 2,
CMreset, "reset", 1,
CMwait, "wait", 1,
CMdebug, "debug", 1,
};
typedef struct Irqconfig Irqconfig;
struct Irqconfig {
int intenable; /* Interrupts are enabled */
int mode; /* level == 0; edge == 1 */
ulong interrupts; /* Count interrupts */
ulong sleepints; /* interrupt count when waiting */
Rendez r; /* Rendez-vous point for interrupt waiting */
Irqconfig *next;
Timer;
};
Irqconfig *irqconfig[NIRQ]; /* irqconfig[0] is not used */
Lock irqlock;
static void interrupt(Ureg*, void*);
void dumpvno(void);
static void
ticmstimer(Ureg*, Timer *t)
{
Irqconfig *ic;
ic = t->ta;
ic->interrupts++;
wakeup(&ic->r);
}
void
irqenable(Irqconfig *ic, int irq)
{
/* call with ilock(&irqlock) held */
if (ic->intenable)
return;
if (irq == Qmstimer){
if (ic->tnext == nil)
ic->tns = MS2NS(ic->mode);
ic->tmode = Tperiodic;
timeradd(&ic->Timer);
}else{
if (irqconfig[irq]){
ic->next = irqconfig[irq];
irqconfig[irq] = ic;
}else{
ic->next = nil;
irqconfig[irq] = ic;
intrenable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
}
}
ic->intenable = 1;
}
void
irqdisable(Irqconfig *ic, int irq)
{
Irqconfig **pic;
/* call with ilock(&irqlock) held */
if (ic->intenable == 0)
return;
if (irq == Qmstimer){
timerdel(&ic->Timer);
}else{
for(pic = &irqconfig[irq]; *pic != ic; pic = &(*pic)->next)
assert(*pic);
*pic = (*pic)->next;
if (irqconfig[irq] == nil)
intrdisable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
}
ic->intenable = 0;
}
static Chan*
irqattach(char *spec)
{
return devattach('b', spec);
}
static Walkqid*
irqwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name,nname, irqdir, nelem(irqdir), devgen);
}
static int
irqstat(Chan *c, uchar *dp, int n)
{
return devstat(c, dp, n, irqdir, nelem(irqdir), devgen);
}
static Chan*
irqopen(Chan *c, int omode)
{
Irqconfig *ic;
int irq;
irq = (ulong)c->qid.path;
if(irq != Qdir){
ic = mallocz(sizeof(Irqconfig), 1);
ic->tf = ticmstimer;
ic->ta = ic;
if (irq == Qmstimer)
ic->mode = 1000;
c->aux = ic;
}
return devopen(c, omode, irqdir, nelem(irqdir), devgen);
}
static void
irqclose(Chan *c)
{
int irq;
Irqconfig *ic;
irq = (ulong)c->qid.path;
if(irq == Qdir)
return;
ic = c->aux;
if (irq > Qmstimer)
return;
ilock(&irqlock);
irqdisable(ic, irq);
iunlock(&irqlock);
free(ic);
}
static int
irqtfn(void *arg)
{
Irqconfig *ic;
ic = arg;
return ic->sleepints != ic->interrupts;
}
static long
irqread(Chan *c, void *buf, long n, vlong)
{
int irq;
Irqconfig *ic;
char tmp[24];
if(n <= 0)
return n;
irq = (ulong)c->qid.path;
if(irq == Qdir)
return devdirread(c, buf, n, irqdir, nelem(irqdir), devgen);
if(irq > Qmstimer){
print("irqread 0x%llux\n", c->qid.path);
error(Egreg);
}
ic = c->aux;
if (ic->intenable == 0)
error("disabled");
ic->sleepints = ic->interrupts;
sleep(&ic->r, irqtfn, ic);
if (irq == Qmstimer)
snprint(tmp, sizeof tmp, "%11lud %d", ic->interrupts, ic->mode);
else
snprint(tmp, sizeof tmp, "%11lud %s", ic->interrupts, ic->mode ?"edge":"level");
n = readstr(0, buf, n, tmp);
return n;
}
static long
irqwrite(Chan *c, void *a, long n, vlong)
{
int irq;
Irqconfig *ic;
Cmdbuf *cb;
Cmdtab *ct;
if(n <= 0)
return n;
irq = (ulong)c->qid.path;
if(irq <= 0 || irq >= nelem(irqdir)){
print("irqwrite 0x%llux\n", c->qid.path);
error(Egreg);
}
if (irq == Qfpgareset){
if (strncmp(a, "reset", 5) == 0)
fpgareset();
else
error(Egreg);
return n;
}
ic = c->aux;
cb = parsecmd(a, n);
if(waserror()) {
free(cb);
nexterror();
}
ct = lookupcmd(cb, irqmsg, nelem(irqmsg));
switch(ct->index) {
case CMinterrupt:
/* Turn interrupts on or off */
if (strcmp(cb->f[1], "on") == 0){
ilock(&irqlock);
irqenable(ic, irq);
iomem->siprr = 0x65009770;
iunlock(&irqlock);
}else if (strcmp(cb->f[1], "off") == 0){
ilock(&irqlock);
irqdisable(ic, irq);
iunlock(&irqlock);
}else
error(Ebadarg);
break;
case CMmode:
/* Set mode */
if (irq == Qmstimer){
ic->mode = strtol(cb->f[1], nil, 0);
if (ic->mode <= 0){
ic->tns = MS2NS(1000);
ic->mode = 1000;
error(Ebadarg);
}
ic->tns = MS2NS(ic->mode);
}else if (strcmp(cb->f[1], "level") == 0){
ic->mode = Level;
iomem->siexr &= ~(0x8000 >> irq);
}else if (strcmp(cb->f[1], "edge") == 0){
ic->mode = Edge;
iomem->siexr |= 0x8000 >> irq;
}else
error(Ebadarg);
break;
case CMreset:
ic->interrupts = 0;
break;
case CMwait:
if (ic->intenable == 0)
error("interrupts are off");
ic->sleepints = ic->interrupts;
sleep(&ic->r, irqtfn, ic);
break;
case CMdebug:
print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux, siprr 0x%lux\n",
iomem->simr_h, iomem->simr_l,
iomem->sipnr_h, iomem->sipnr_l,
iomem->siexr, iomem->siprr);
dumpvno();
}
poperror();
free(cb);
/* Irqi */
return n;
}
static void
interrupt(Ureg*, void *arg)
{
Irqconfig **pic, *ic;
int irq;
pic = arg;
irq = pic - irqconfig;
if (irq <= 0 || irq > nelem(irqdir)){
print("Unexpected interrupt: %d\n", irq);
return;
}
ilock(&irqlock);
if (irq <= Qirq7)
iomem->sipnr_h |= 0x8000 >> irq; /* Clear the interrupt */
for(ic = *pic; ic; ic = ic->next){
ic->interrupts++;
wakeup(&ic->r);
}
iunlock(&irqlock);
}
Dev irqdevtab = {
'b',
"irq",
devreset,
devinit,
devshutdown,
irqattach,
irqwalk,
irqstat,
irqopen,
devcreate,
irqclose,
irqread,
devbread,
irqwrite,
devbwrite,
devremove,
devwstat,
};