Where do MSI quirks belong? [patch]
John Polstra
jdp at polstra.com
Sun Dec 10 16:29:17 PST 2006
On Nov 20, 2006, at 9:42 AM, John Baldwin wrote:
> It's going to be a function of the chipset, as something in the
> chipset
> (presumably a Host -> PCI bridge) has to listen for writes to
> 0xfeeXXXXXX and
> convert them into APIC messages. There are two ways I planned on
> doing this:
>
> 1) Allow PCI-PCI bridges to be blacklisted, and the pcib_alloc_msi
> [x]()
> methods would compare the bridge's device id against a blacklist.
> This can
> matter if you have virtual PCI-PCI bridges that really a HT -> PCI
> bridge or
> the like.
>
> 2) Blacklist chipsets in the x86 MD code based on the device ID of
> the first
> Host -> PCI bridge at device 0.0.0.
I have implemented both of these checks, except that I put #2 into
the MI code since I couldn't find any reason to make it x86-
specific. Here's the patch. Does it look OK to you? It works fine
here.
John
Index: pci.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/pci/pci.c,v
retrieving revision 1.324
diff -u -p -u -r1.324 pci.c
--- pci.c 21 Nov 2006 05:46:09 -0000 1.324
+++ pci.c 11 Dec 2006 00:16:59 -0000
@@ -175,6 +175,24 @@ struct pci_quirk pci_quirks[] = {
{ 0 }
};
+/* MSI device quirks. */
+struct msi_device_quirk {
+ uint32_t devid; /* Vendor/device ID */
+ int flags; /* MSI quirk flags */
+};
+
+#define MSI_QUIRK_NO_MSI 0x01
+#define MSI_QUIRK_NO_MSIX 0x02
+
+struct msi_device_quirk msi_device_quirks[] = {
+ /*
+ * MSI doesn't work with the Intel E7501 chipset, at least on
+ * the Tyan 2721 motherboard.
+ */
+ { 0x254c8086, MSI_QUIRK_NO_MSI | MSI_QUIRK_NO_MSIX },
+ { 0 }
+};
+
/* map register information */
#define PCI_MAPMEM 0x01 /* memory map */
#define PCI_MAPMEMP 0x02 /* prefetchable memory map */
@@ -1116,6 +1134,43 @@ pci_resume_msi(device_t dev)
}
/*
+ * Return the MSI quirks associated with a specific device, typically
+ * a host-PCI or PCI-PCI bridge.
+ */
+static int
+pci_get_msi_device_quirks(device_t dev)
+{
+ uint32_t devid;
+ int i;
+
+ devid = pci_get_device(dev) << 16 | pci_get_vendor(dev);
+ for (i = 0; msi_device_quirks[i].devid != 0; i++)
+ if (msi_device_quirks[i].devid == devid)
+ return (msi_device_quirks[i].flags);
+ return (0);
+}
+
+/*
+ * Return the systemwide MSI quirks. Currently, we just check for
+ * blacklisted chipsets as represented by the host-PCI bridge at
+ * device 0:0:0. In the future, it may become necessary to check other
+ * system attributes, such as the kenv values that give the motherboard
+ * manufacturer and model number.
+ */
+static int
+pci_get_msi_global_quirks(void)
+{
+ device_t dev;
+ int quirks;
+
+ quirks = 0;
+ dev = pci_find_bsf(0, 0, 0);
+ if (dev != NULL)
+ quirks |= pci_get_msi_device_quirks(dev);
+ return (quirks);
+}
+
+/*
* Attempt to allocate *count MSI messages. The actual number
allocated is
* returned in *count. After this function returns, each message
will be
* available to the driver as SYS_RES_IRQ resources starting at a
rid 1.
@@ -1126,7 +1181,7 @@ pci_alloc_msi_method(device_t dev, devic
struct pci_devinfo *dinfo = device_get_ivars(child);
pcicfgregs *cfg = &dinfo->cfg;
struct resource_list_entry *rle;
- int actual, error, i, irqs[32];
+ int actual, error, i, irqs[32], quirks;
uint16_t ctrl;
/* Don't let count == 0 get us into trouble. */
@@ -1138,13 +1193,23 @@ pci_alloc_msi_method(device_t dev, devic
if (rle != NULL && rle->res != NULL)
return (ENXIO);
+ /*
+ * Check for MSI quirks that affect the whole system, and those
+ * that are specific to the device's parent bridge.
+ */
+ quirks = pci_get_msi_global_quirks() |
+ pci_get_msi_device_quirks(device_get_parent(dev));
+
/* Try MSI-X first. */
- error = pci_alloc_msix(dev, child, count);
- if (error != ENODEV)
- return (error);
+ if (!(quirks & MSI_QUIRK_NO_MSIX)) {
+ error = pci_alloc_msix(dev, child, count);
+ if (error != ENODEV)
+ return (error);
+ }
/* MSI capability present? */
- if (cfg->msi.msi_location == 0 || !pci_do_msi)
+ if (cfg->msi.msi_location == 0 || !pci_do_msi ||
+ (quirks & MSI_QUIRK_NO_MSI))
return (ENODEV);
/* Already have allocated messages? */
More information about the freebsd-arch
mailing list