diff -Nru /n/sources/plan9/sys/src/9/pc/io.h /sys/src/9/pc/io.h --- /n/sources/plan9/sys/src/9/pc/io.h Fri Apr 8 20:27:20 2011 +++ /sys/src/9/pc/io.h Wed Aug 28 00:00:00 2013 @@ -48,11 +48,13 @@ Vctl* next; /* handlers on this vector */ char name[KNAMELEN]; /* of driver */ + char *type; int isintr; /* interrupt or fault/trap */ int irq; int tbdf; int (*isr)(int); /* get isr bit for this irq */ int (*eoi)(int); /* eoi */ + int (*mask)(Vctl*, int); /* interrupt enable returns masked vector */ void (*f)(Ureg*, void*); /* handler to call */ void* a; /* argument to call it with */ diff -Nru /n/sources/plan9/sys/src/9/pc/mp.c /sys/src/9/pc/mp.c --- /n/sources/plan9/sys/src/9/pc/mp.c Thu Jun 13 21:01:42 2013 +++ /sys/src/9/pc/mp.c Wed Aug 28 00:00:00 2013 @@ -714,6 +714,70 @@ } static int +mpmsimask(Vctl *v, int mask) +{ + Pcidev *p; + + p = pcimatchtbdf(v->tbdf); + if(p == nil) + return -1; + return pcimsimask(p, mask); +} + + +static int +mpintrenablemsi(Vctl* v, int tbdf) +{ + uint vno, lo, hi; + uvlong msivec; + Pcidev *p; + + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + + lock(&mpvnoref); + vno = VectorAPIC + mpvnoref.ref*8; /* really? */ + if(vno > MaxVectorAPIC){ + unlock(&mpvnoref); + print("msiirq: out of vectors %T %s\n", tbdf, v->name); + return -1; + } + + lo = ApicLOW | ApicEDGE | vno; + lo |= ApicPHYSICAL; /* no-op */ + + if(lo & ApicLOGICAL) + lo |= ApicLOWEST; + + msivec = (uvlong)hi<<32 | lo; + if(pcimsienable(p, msivec) == -1){ + unlock(&mpvnoref); + dprint("msiirq: %T: can't enable %s\n", p->tbdf, v->name); + return -1; + } + v->type = "msi"; + v->isr = lapicisr; /* XXX */ + v->eoi = lapiceoi; + v->mask = mpmsimask; + dprint("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno); + mpvnoref.ref++; + unlock(&mpvnoref); + return vno; +} + +int +mpdisablemsi(Vctl*, int tbdf) +{ + Pcidev *p; + + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + return pcimsimask(p, 1); +} + +static int mpintrenablex(Vctl* v, int tbdf) { Bus *bus; @@ -857,8 +921,13 @@ * breakpoint and page-fault). */ tbdf = v->tbdf; - if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) - return vno; + if(tbdf != BUSUNKNOWN){ + if((vno = mpintrenablemsi(v, tbdf)) != -1) + return vno; + mpdisablemsi(v, tbdf); /* should be in pcireset? */ + if((vno = mpintrenablex(v, tbdf)) != -1) + return vno; + } irq = v->irq; if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ diff -Nru /n/sources/plan9/sys/src/9/pc/mp.h /sys/src/9/pc/mp.h --- /n/sources/plan9/sys/src/9/pc/mp.h Mon May 16 18:29:37 2011 +++ /sys/src/9/pc/mp.h Wed Aug 28 00:00:00 2013 @@ -232,4 +232,7 @@ extern int mpintrenable(Vctl*); extern void mpshutdown(void); +extern int pcimsienable(Pcidev*, uvlong); +extern int pcimsimask(Pcidev*, int); + extern _MP_ *_mp_; diff -Nru /n/sources/plan9/sys/src/9/pc/msi.c /sys/src/9/pc/msi.c --- /n/sources/plan9/sys/src/9/pc/msi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/msi.c Wed Aug 28 00:00:00 2013 @@ -0,0 +1,131 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "mp.h" + +#define pcicapdbg(...) do if(Debug&Dpcicap)print(__VA_ARGS__);while(0) +#define dprint(c, ...) do if(Debug&c)print(__VA_ARGS__);while(0) + +enum { + Dpcicap = 1<<0, + Dmsicap = 1<<1, + Dvec = 1<<2, + Debug = 0, +}; + +enum { + /* address */ + Msiabase = 0xfee00000, + Msiadest = 1<<12, /* same as 63:56 of apic vector */ + Msiaedest = 1<<4, /* same as 55:48 of apic vector */ + Msialowpri = 1<<3, /* redirection hint */ + Msialogical = 1<<2, + + /* data */ + Msidlevel = 1<<15, + Msidassert = 1<<14, + Msidlogical = 1<<11, + Msidmode = 1<<8, /* 3 bits; delivery mode */ + Msidvector = 0xff<<0, +}; + +enum{ + /* msi capabilities */ + Vmask = 1<<8, + Cap64 = 1<<7, + Mmesgmsk = 7<<4, + Mmcap = 7<<1, + Msienable = 1<<0, +}; + +static int +msicap(Pcidev *p) +{ + int c; + + c = pcicap(p, PciCapMSI); + if(c == -1){ + dprint(Dmsicap, "%T: no msi: cap not found\n", p->tbdf); + return 0; + } + return c; +} + +static int +blacklist(Pcidev *p) +{ + switch(p->vid<<16 | p->did){ + case 0x11ab<<16 | 0x6485: /* actually just a driver bug */ + return -1; + } + /* bad chipset */ + if(p = pcimatchtbdf(MKBUS(BusPCI, 0, 0, 0))) + if(p->vid == 0x8086 && p->did == 0x3590) + return -1; + return 0; +} + +int +pcimsienable(Pcidev *p, uvlong vec) +{ + char *s; + uint c, f, d, datao, lopri, dmode, logical; + + c = msicap(p); + if(c == 0) + return -1; + + f = pcicfgr16(p, c + 2) & ~Mmesgmsk; + + if(blacklist(p) != 0) + return -1; + datao = 8; + d = vec>>48; + lopri = (vec & 0x700) == ApicLOWEST; + logical = (vec & ApicLOGICAL) != 0; + dprint(Dvec, "%T: %.16llux -> ", p->tbdf, vec); + dprint(Dvec, "%.8ux ", Msiabase | Msiaedest * d + | Msialowpri * lopri | Msialogical * logical); + pcicfgw32(p, c + 4, Msiabase | Msiaedest * d + | Msialowpri * lopri | Msialogical * logical); + if(f & Cap64){ + datao += 4; + pcicfgw32(p, c + 8, 0); + } + dmode = (vec >> 8) & 7; + pcicfgw16(p, c + datao, Msidassert | Msidlogical * logical + | Msidmode * dmode | (uint)vec & 0xff); + dprint(Dvec, "%.8ux\n", Msidassert | Msidlogical * logical + | Msidmode * dmode | (uint)vec & 0xff); + if(f & Vmask) + pcicfgw32(p, c + datao + 4, 0); + + /* leave vectors configured but disabled for debugging */ + if((s = getconf("*nomsi")) != nil && atoi(s) != 0) + return -1; + + pcicfgw16(p, c + 2, f); + return 0; +} + +int +pcimsimask(Pcidev *p, int mask) +{ + uint c, f; + + c = msicap(p); + if(c == 0) + return -1; + f = pcicfgr16(p, c + 2) & ~Msienable; + if(mask){ + pcicfgw16(p, c + 2, f & ~Msienable); +// pciclrbme(p); cheeze + }else{ + pcisetbme(p); + pcicfgw16(p, c + 2, f | Msienable); + } + return 0; +}