/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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); #ifdef notdefulong multiplier; ulongµs(void){ uvlong x; if(multiplier == 0){ multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq; print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16); } cycles(&x); return (x*multiplier) >> 16;}#endif static voidticmstimer(Ureg*, Timer *t){ Irqconfig *ic; ic = t->ta; ic->interrupts++; wakeup(&ic->r);} voidirqenable(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;} voidirqdisable(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 intirqstat(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 voidirqclose(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 intirqtfn(void *arg){ Irqconfig *ic; ic = arg; return ic->sleepints != ic->interrupts;} static longirqread(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 longirqwrite(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 voidinterrupt(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,};
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#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);
#ifdef notdef
ulong multiplier;
ulong
µs(void)
{
uvlong x;
if(multiplier == 0){
multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
}
cycles(&x);
return (x*multiplier) >> 16;
}
#endif
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,
};