summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2006-12-14 19:57:06 +0000
committerjhb <jhb@FreeBSD.org>2006-12-14 19:57:06 +0000
commita33da981f55b2454aab9ef03532f0a6453126fa2 (patch)
treec5b250fc397348ea29fa02e850b78a53c320cfa2 /sys/dev/pci
parentc18cdc85cac392ad941d4145ebde21bf85d03a7d (diff)
downloadFreeBSD-src-a33da981f55b2454aab9ef03532f0a6453126fa2.zip
FreeBSD-src-a33da981f55b2454aab9ef03532f0a6453126fa2.tar.gz
Add a first pass at a way to blacklist MSI on systems where it doesn't
work: - A new PCI quirk (PCI_QUIRK_DISABLE_MSI) is added to the quirk table. - A new pci_msi_device_blacklisted() determines if a passed in device matches an MSI quirk in the quirk table. This can be overridden (all quirks ignored) by setting the hw.pci.honor_msi_blacklist to 0. - A global blacklist check is performed in the MI PCI bus code by checking to see if the device at 0:0:0 is blacklisted. Tested by: jdp
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/pci.c51
-rw-r--r--sys/dev/pci/pcivar.h1
2 files changed, 52 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index c6d16e3..44247b45 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -165,6 +165,7 @@ struct pci_quirk {
uint32_t devid; /* Vendor/device of the card */
int type;
#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */
+#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */
int arg1;
int arg2;
};
@@ -224,6 +225,11 @@ TUNABLE_INT("hw.pci.enable_msix", &pci_do_msix);
SYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1,
"Enable support for MSI-X interrupts");
+static int pci_honor_msi_blacklist = 1;
+TUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist);
+SYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD,
+ &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI");
+
/* Find a device_t by bus/slot/function */
device_t
@@ -1195,6 +1201,47 @@ pci_resume_msi(device_t dev)
}
/*
+ * Returns true if the specified device is blacklisted because MSI
+ * doesn't work.
+ */
+int
+pci_msi_device_blacklisted(device_t dev)
+{
+ struct pci_quirk *q;
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ for (q = &pci_quirks[0]; q->devid; q++) {
+ if (q->devid == pci_get_devid(dev) &&
+ q->type == PCI_QUIRK_DISABLE_MSI)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Determine if MSI is blacklisted globally on this sytem. 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_msi_blacklisted(void)
+{
+ device_t dev;
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ dev = pci_find_bsf(0, 0, 0);
+ if (dev != NULL)
+ return (pci_msi_device_blacklisted(dev));
+ return (0);
+}
+
+/*
* 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.
@@ -1217,6 +1264,10 @@ pci_alloc_msi_method(device_t dev, device_t child, int *count)
if (rle != NULL && rle->res != NULL)
return (ENXIO);
+ /* If MSI is blacklisted for this system, fail. */
+ if (pci_msi_blacklisted())
+ return (ENXIO);
+
/* Try MSI-X first. */
error = pci_alloc_msix(dev, child, count);
if (error != ENODEV)
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index 846f99a..eca13ef 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -417,6 +417,7 @@ void pci_enable_msix(device_t dev, u_int index, uint64_t address,
void pci_mask_msix(device_t dev, u_int index);
int pci_pending_msix(device_t dev, u_int index);
void pci_unmask_msix(device_t dev, u_int index);
+int pci_msi_device_blacklisted(device_t dev);
#endif /* _SYS_BUS_H_ */
OpenPOWER on IntegriCloud