Bring in msi support
authorRonald G. Minnich <rminnich@google.com>
Tue, 18 Mar 2014 21:58:50 +0000 (14:58 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Sat, 29 Mar 2014 01:17:04 +0000 (18:17 -0700)
We're going to have to have it.

Signed-off-by: Ronald G. Minnich <rminnich@google.com>
kern/arch/x86/Kbuild
kern/arch/x86/ioapic.c
kern/arch/x86/msi.c [new file with mode: 0644]
kern/arch/x86/trap.h

index 0720bda..19aa14b 100644 (file)
@@ -12,6 +12,7 @@ obj-y                                         += kclock.o
 obj-y                                          += kdebug.o
 obj-y                                          += mpacpi.o
 obj-y                                          += mp.o
+obj-y                                          += msi.o
 obj-y                                          += page_alloc.o
 obj-y                                          += pci.o
 obj-y                                          += pic.o
index 2e3ec52..85fd392 100644 (file)
@@ -65,6 +65,7 @@ static spinlock_t idtnolock;
 static int idtno = IdtIOAPIC;
 
 struct apic xioapic[Napic];
+int pcimsimask(struct pci_device *p, int mask);
 
 static bool ioapic_exists(void)
 {
@@ -373,28 +374,46 @@ int nextvec(void)
 
        return vecno;
 }
-
-/* TODO: MSI work */
-#if 0
-static int msimask(struct Vkey *v, int mask)
+static struct pci_device *pcimatchtbdf(int tbdf)
+{
+       /* don't we have macros for this somewhere? */
+       int bus, dev, func;
+       bus = tbdf >> 16;
+       dev = (tbdf>>11)&0x1f;
+       func = (tbdf>>8)&3;
+
+       struct pci_device *search;
+       STAILQ_FOREACH(search, &pci_devices, all_dev) {
+               if ((search->bus == bus) &&
+                   (search->dev == dev) &&
+                   (search->func == func))
+                       return search;
+       }
+       return NULL;
+}
+static int msimask(struct vkey *v, int mask)
 {
-       Pcidev *p;
+       int pcimsimask(struct pci_device *p, int mask);
 
+       struct pci_device *p;
        p = pcimatchtbdf(v->tbdf);
        if (p == NULL)
                return -1;
        return pcimsimask(p, mask);
 }
 
-static int intrenablemsi(struct vctl *v, Pcidev * p)
+static int intrenablemsi(struct irq_handler *v, struct pci_device *p)
 {
-       unsigned int vno, lo, hi;
+       int pcimsienable(struct pci_device *p, uint64_t vec);
+
+       unsigned int vno, lo, hi = 0;
        uint64_t msivec;
 
        vno = nextvec();
 
        lo = IPlow | TMedge | vno;
-       ioapicintrdd(&hi, &lo);
+#warning "what happened to ioapicintrdd"
+//     ioapicintrdd(&hi, &lo);
 
        if (lo & Lm)
                lo |= MTlp;
@@ -402,24 +421,26 @@ static int intrenablemsi(struct vctl *v, Pcidev * p)
        msivec = (uint64_t) hi << 32 | lo;
        if (pcimsienable(p, msivec) == -1)
                return -1;
-       v->isr = apicisr;
-       v->eoi = apiceoi;
-       v->vno = vno;
+#warning "apicisr? apiceoi? "
+//     v->isr = apicisr;
+//     v->eoi = apiceoi;
+       v->apic_vector = vno;
        v->type = "msi";
        v->mask = msimask;
 
-       printk("msiirq: %T: enabling %.16llp %s irq %d vno %d\n", p->tbdf, msivec,
-                  v->name, v->irq, vno);
+       printk("msiirq: (%d,%d,%d): enabling %.16llp %s irq %d vno %d\n", 
+              p->bus, p->dev, p->func, msivec,
+                  v->name, v->apic_vector, vno);
        return vno;
 }
 
-int disablemsi(Vctl *, Pcidev * p)
+int disablemsi(void *unused, struct pci_device *p)
 {
+
        if (p == NULL)
                return -1;
        return pcimsimask(p, 1);
 }
-#endif
 
 static struct Rdt *ioapic_vector2rdt(int apic_vector)
 {
@@ -518,6 +539,7 @@ int bus_irq_setup(struct irq_handler *irq_h)
        struct Rdt *rdt;
        int busno = 0, devno, vecno;
        struct pci_device pcidev;
+       struct pci_device *msidev;
 
        if (!ioapic_exists() && (BUSTYPE(irq_h->tbdf) != BusLAPIC)) {
                irq_h->check_spurious = pic_check_spurious;
@@ -562,16 +584,14 @@ int bus_irq_setup(struct irq_handler *irq_h)
                        break;
                case BusPCI:
                        /* TODO: we'll assume it's there.  (fix when adding MSI) */
-#if 0
-                       Pcidev *pcidev;
 
+                       if ((msidev = pcimatchtbdf(irq_h->tbdf)) == NULL) {
+                               printk("no PCI dev for tbdf %p", irq_h->tbdf);
+                               if ((vecno = intrenablemsi(irq_h, msidev)) != -1)
+                                       return vecno;
+                               disablemsi(irq_h, msidev);
+                       }
                        busno = BUSBNO(irq_h->tbdf);
-                       if ((pcidev = pcimatchtbdf(irq_h->tbdf)) == NULL)
-                               panic("no PCI dev for tbdf %p", irq_h->tbdf);
-                       if ((vecno = intrenablemsi(irq_h, pcidev)) != -1)
-                               return vecno;
-                       disablemsi(irq_h, pcidev);
-#endif
                        explode_tbdf(irq_h->tbdf);
                        devno = pcidev_read8(&pcidev, PciINTP);
 
diff --git a/kern/arch/x86/msi.c b/kern/arch/x86/msi.c
new file mode 100644 (file)
index 0000000..e628a45
--- /dev/null
@@ -0,0 +1,162 @@
+/* 
+ * 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 <vfs.h>
+#include <kfs.h>
+#include <slab.h>
+#include <kmalloc.h>
+#include <kref.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <error.h>
+#include <cpio.h>
+#include <pmap.h>
+#include <smp.h>
+#include <ip.h>
+
+enum {
+       Dpcicap         = 1<<0,
+       Dmsicap         = 1<<1,
+       Dvec            = 1<<2,
+       Debug           = 0,
+};
+
+enum {
+       /* address */
+       Msiabase                = 0xfee00000u,
+       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,
+};
+
+int
+pcicap(struct pci_device *p, int cap)
+{
+       int i, c, off;
+
+       /* status register bit 4 has capabilities */
+       if((pcidev_read16(p, PciPSR) & 1<<4) == 0)
+               return -1;
+       switch(pcidev_read8(p, PciHDT) & 0x7f){
+       default:
+               return -1;
+       case 0:                         /* etc */
+       case 1:                         /* pci to pci bridge */
+               off = 0x34;
+               break;
+       case 2:                         /* cardbus bridge */
+               off = 0x14;
+               break;
+       }
+       for(i = 48; i--;){
+               off = pcidev_read8(p, off);
+               if(off < 0x40 || (off & 3))
+                       break;
+               off &= ~3;
+               c = pcidev_read8(p, off);
+               if(c == 0xff)
+                       break;
+               if(c == cap)
+                       return off;
+               off++;
+       }
+       return -1;
+}
+
+static int
+msicap(struct pci_device *p)
+{
+       int c;
+
+       c = pcicap(p, PciCapMSI);
+       if(c == -1)
+               return 0;
+       return c;
+}
+
+static int
+blacklist(struct pci_device *p)
+{
+       switch(p->ven_id<<16 | p->dev_id){
+       case 0x11ab<<16 | 0x6485:
+               return -1;
+       }
+       return 0;
+}
+
+int
+pcimsienable(struct pci_device *p, uint64_t vec)
+{
+       char *s;
+       unsigned int c, f, d, datao, lopri, dmode, logical;
+
+       c = msicap(p);
+       if(c == 0)
+               return -1;
+
+       f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
+
+       if(blacklist(p) != 0)
+               return -1;
+       datao = 8;
+       d = vec>>48;
+       lopri = (vec & 0x700) == MTlp;
+       logical = (vec & Lm) != 0;
+       pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
+               | Msialowpri * lopri | Msialogical * logical);
+       if(f & Cap64){
+               datao += 4;
+               pcidev_write32(p, c + 8, 0);
+       }
+       dmode = (vec >> 8) & 7;
+       pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
+                      | Msidmode * dmode | ((unsigned int)vec & 0xff));
+       if(f & Vmask)
+               pcidev_write32(p, c + datao + 4, 0);
+
+       pcidev_write16(p, c + 2, f);
+       return 0;
+}
+
+int
+pcimsimask(struct pci_device *p, int mask)
+{
+       unsigned int c, f;
+
+       c = msicap(p);
+       if(c == 0)
+               return -1;
+       f = pcidev_read16(p, c + 2) & ~Msienable;
+       if(mask){
+               pcidev_write16(p, c + 2, f & ~Msienable);
+//             pciclrbme(p);           cheeze
+       }else{
+               pci_set_bus_master(p);
+               pcidev_write16(p, c + 2, f | Msienable);
+       }
+       return 0;
+}
index 683d2d4..c154ba2 100644 (file)
 #include <arch/coreid.h>
 #include <arch/io.h>
 
+struct vkey {
+       int tbdf;
+       int dev_irq;
+};
+
 struct irq_handler {
        struct irq_handler *next;
        void (*isr)(struct hw_trapframe *hw_tf, void *data);