@@ -376,11 +376,18 @@ struct pci_dev_reset_methods {
#ifdef CONFIG_PCI_QUIRKS
int pci_dev_specific_reset(struct pci_dev *dev, int probe);
+int pci_dev_specific_fixup_acs_quirk(struct pci_bus *bus , int devfn,
+ int enable, bool found);
#else
static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
{
return -ENOTTY;
}
+static inline int pci_dev_specific_fixup_acs_quirk(struct pc_bus *bus,
+ int devfn, int enable, bool found)
+{
+ return -ENOTTY;
+}
#endif
#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
@@ -2041,18 +2041,26 @@ static bool pci_bus_wait_crs(struct pci_bus
*bus, int de
vfn, u32 *l,
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
int timeout)
{
+ bool found = false;
+ int enable = -1;
+
+ enable = pci_dev_specific_fixup_acs_quirk(bus, devfn, false, found);
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
- return false;
+ goto out;
/* Some broken boards return 0 or ~0 if a slot is empty: */
if (*l == 0xffffffff || *l == 0x00000000 ||
*l == 0x0000ffff || *l == 0xffff0000)
- return false;
+ goto out;
+ found = true;
if (pci_bus_crs_vendor_id(*l))
- return pci_bus_wait_crs(bus, devfn, l, timeout);
+ found = pci_bus_wait_crs(bus, devfn, l, timeout);
- return true;
+out:
+ if (enable > 0)
+ pci_dev_specific_fixup_acs_quirk(bus, devfn, enable, found);
+ return found;
}
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
@@ -4839,3 +4839,98 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev)
pdev->no_msi = 1;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID,
quirk_fsl_no_msi);
+
+
+
+/*
+ * Some IDT switches flag an ACS violation for config reads even though
the
+ * PCI spec allows for it (PCIe 3.1, 6.1.12.1). It flags it because the
bus
+ * number is not properly set in the completion. The workaround is to do a
+ * dummy write to properly latch number once the device is ready for
config
+ * operations.
+ *
+ * So, the caller would disable ACS source validation, wait for the device
+ * hanging off this switch to be ready for config operations, and then
+ * call this routine again to enable it (if it was eabled to begin with)
+ */
+static int pci_idt_acs_quirk(struct pci_bus *bus, int devfn, int enable,
+ bool found)
+{
+ int pos;
+ u16 cap;
+ u16 ctrl;
+ int retval;
+ struct pci_dev *dev = bus->self;
+
+
+ /* Write 0 to the devfn device under the PCIE switch (bus->self)
+ * as part of forcing the devfn number to latch with the device below
+ */
+ if (found)
+ pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0);
+
+
+ /* Enable/disable ACS SV feature (based on enable flag) */
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+ if (!pos)
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
+
+ if (!(cap & PCI_ACS_SV))
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+
+ retval = !!(ctrl & cap & PCI_ACS_SV);
+ if (enable)
+ ctrl |= (cap & PCI_ACS_SV);
+ else
+ ctrl &= ~(cap & PCI_ACS_SV);
+
+ pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+ return retval;
+}
+
+static const struct pci_dev_acs_quirk{
+ u16 vendor;
+ u16 device;
+ int (*fixup_acs)(struct pci_bus *bus, int devfn, int enable,
bool found
);
+} pci_dev_acs_quirks[] = {
+ { PCI_VENDOR_ID_IDT, 0x80b5, pci_idt_acs_quirk},
+ {0}
+};
+
+int pci_dev_specific_fixup_acs_quirk(struct pci_bus *bus, int devfn,
int enable
,
+ bool found)
+{
+ const struct pci_dev_acs_quirk *i;
+ struct pci_dev *dev;
+ int ret;
+
+ if (!bus || !bus->self)
+ return -ENOTTY;
+
+ dev = bus->self;
+
+ /*
+ * Allow devices that do not expose standard PCIe ACS capabilities
+ * or control to indicate their support here. Multi-function express
+ * devices which do not allow internal peer-to-peer between functions,
+ * but do not implement PCIe ACS may wish to return true here.
+ */
+ for (i = pci_dev_acs_quirks; i->fixup_acs; i++) {
+ if ((i->vendor == dev->vendor ||
+ i->vendor == (u16)PCI_ANY_ID) &&
+ (i->device == dev->device ||
+ i->device == (u16)PCI_ANY_ID)) {
+ ret = i->fixup_acs(bus, devfn, enable, found);
+ if (ret >= 0)