diff options
author | jhb <jhb@FreeBSD.org> | 2006-12-14 19:57:06 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2006-12-14 19:57:06 +0000 |
commit | a33da981f55b2454aab9ef03532f0a6453126fa2 (patch) | |
tree | c5b250fc397348ea29fa02e850b78a53c320cfa2 /sys/dev/pci | |
parent | c18cdc85cac392ad941d4145ebde21bf85d03a7d (diff) | |
download | FreeBSD-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.c | 51 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 1 |
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_ */ |