The diff below improves MSI support for HyperTransport-based systems.
In practice that means all systems with 64-bit AMD CPUs.

The HyperTransport bus itself does not support MSI.  Devices on the
HyperTransport bus may have support to map MSI messages into
HyperTransport interrupt messages.  This is indicated by a "MSI
remapping" capability.  This diff looks for those capabilities and if
they're present and indicate that remapping is enabled, sets
PCI_FLAGS_MSI_ENABLED for the device.  If the device is a bridge, this
will mean that PCI_FLAGS_MSI_ENABLES will be passed to devices behind
that bridge as well, which is exactly what we want.

On some machines the MSI remapping capabilities are not automatically
enabled by the BIOS.  My diff leaves those disabled.  We probably
should consider enabling them ourselves (which is what Linux seems to
do), but I'd like to leave that until later.

This diff will effectively enable MSI for many systems with AMD CPUs,
without an easy way to turn it off.  I think that is ok, since the
systems where this happens clearly indicate that they support MSI.
Nevertheless I'm working on a diff that gives us a global on-off
switch since that is needed when ACPI indicates that MSI is not
supposed to be used.

Tested by dlg@ on his R815.

ok?


Index: pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/pci.c,v
retrieving revision 1.91
diff -u -p -r1.91 pci.c
--- pci.c       19 May 2011 20:14:55 -0000      1.91
+++ pci.c       29 May 2011 17:28:23 -0000
@@ -370,8 +370,10 @@ pci_probe_device(struct pci_softc *sc, p
        struct pci_attach_args pa;
        struct pci_dev *pd;
        struct device *dev;
-       pcireg_t id, class, intr, bhlcr;
-       int ret = 0, pin, bus, device, function;
+       pcireg_t id, class, intr, bhlcr, cap;
+       int pin, bus, device, function;
+       int off, ret = 0;
+       uint64_t addr;
 
        pci_decompose_tag(pc, tag, &bus, &device, &function);
 
@@ -437,6 +439,29 @@ pci_probe_device(struct pci_softc *sc, p
        }
        pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
 
+       if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSI, &off, &cap)) {
+               /*
+                * XXX Should we enable MSI mapping ourselves on
+                * systems that have it disabled?
+                */
+               if (cap & PCI_HT_MSI_ENABLED) {
+                       if ((cap & PCI_HT_MSI_FIXED) == 0) {
+                               addr = pci_conf_read(pc, tag,
+                                   off + PCI_HT_MSI_ADDR);
+                               addr |= (uint64_t)pci_conf_read(pc, tag,
+                                   off + PCI_HT_MSI_ADDR_HI32) << 32;
+                       } else
+                               addr = PCI_HT_MSI_FIXED_ADDR;
+
+                       /* 
+                        * XXX This will fail to enable MSI on systems
+                        * that don't use the canonical address.
+                        */
+                       if (addr == PCI_HT_MSI_FIXED_ADDR)
+                               pa.pa_flags |= PCI_FLAGS_MSI_ENABLED;
+               }
+       }
+
        if (match != NULL) {
                ret = (*match)(&pa);
                if (ret != 0 && pap != NULL)
@@ -545,6 +570,35 @@ pci_get_capability(pci_chipset_tag_t pc,
 #endif
                reg = pci_conf_read(pc, tag, ofs);
                if (PCI_CAPLIST_CAP(reg) == capid) {
+                       if (offset)
+                               *offset = ofs;
+                       if (value)
+                               *value = reg;
+                       return (1);
+               }
+               ofs = PCI_CAPLIST_NEXT(reg);
+       }
+
+       return (0);
+}
+
+int
+pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
+    int *offset, pcireg_t *value)
+{
+       pcireg_t reg;
+       unsigned int ofs;
+
+       if (pci_get_capability(pc, tag, PCI_CAP_HT, &ofs, NULL) == 0)
+               return (0);
+
+       while (ofs != 0) {
+#ifdef DIAGNOSTIC
+               if ((ofs & 3) || (ofs < 0x40))
+                       panic("pci_get_ht_capability");
+#endif
+               reg = pci_conf_read(pc, tag, ofs);
+               if (PCI_HT_CAP(reg) == capid) {
                        if (offset)
                                *offset = ofs;
                        if (value)
Index: pcireg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/pcireg.h,v
retrieving revision 1.40
diff -u -p -r1.40 pcireg.h
--- pcireg.h    14 May 2011 09:57:56 -0000      1.40
+++ pcireg.h    29 May 2011 17:28:23 -0000
@@ -479,7 +479,7 @@ typedef u_int8_t pci_revision_t;
 #define PCI_CAP_MSI            0x05
 #define PCI_CAP_CPCI_HOTSWAP   0x06
 #define PCI_CAP_PCIX           0x07
-#define PCI_CAP_LDT            0x08
+#define PCI_CAP_HT             0x08
 #define PCI_CAP_VENDSPEC       0x09
 #define PCI_CAP_DEBUGPORT      0x0a
 #define PCI_CAP_CPCI_RSRCCTL   0x0b
@@ -521,6 +521,25 @@ typedef u_int8_t pci_revision_t;
 #define PCI_PMCSR_STATE_D1     0x01
 #define PCI_PMCSR_STATE_D2     0x02
 #define PCI_PMCSR_STATE_D3     0x03
+
+/*
+ * HyperTransport; access via capability pointer.
+ */
+#define PCI_HT_CAP(cr) ((((cr) >> 27) < 0x08) ? \
+    (((cr) >> 27) & 0x1c) : (((cr) >> 27) & 0x1f))
+
+#define PCI_HT_CAP_SLAVE       0x00
+#define PCI_HT_CAP_HOST                0x04
+#define PCI_HT_CAP_INTERRUPT   0x10
+#define PCI_HT_CAP_MSI         0x15
+
+#define PCI_HT_MSI_ENABLED     0x00010000
+#define PCI_HT_MSI_FIXED       0x00020000
+
+#define PCI_HT_MSI_FIXED_ADDR  0xffe00000UL
+
+#define PCI_HT_MSI_ADDR                0x04
+#define PCI_HT_MSI_ADDR_HI32   0x08
 
 /*
  * PCI Express; access via capability pointer.
Index: pcivar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/pcivar.h,v
retrieving revision 1.65
diff -u -p -r1.65 pcivar.h
--- pcivar.h    21 May 2011 10:34:53 -0000      1.65
+++ pcivar.h    29 May 2011 17:28:23 -0000
@@ -231,8 +231,10 @@ int        pci_io_find(pci_chipset_tag_t, pcita
 int    pci_mem_find(pci_chipset_tag_t, pcitag_t, int, bus_addr_t *,
            bus_size_t *, int *);
 
-int pci_get_capability(pci_chipset_tag_t, pcitag_t, int,
-                           int *, pcireg_t *);
+int    pci_get_capability(pci_chipset_tag_t, pcitag_t, int,
+           int *, pcireg_t *);
+int    pci_get_ht_capability(pci_chipset_tag_t, pcitag_t, int,
+           int *, pcireg_t *);
 
 struct pci_matchid {
        pci_vendor_id_t         pm_vid;

Reply via email to