diff options
Diffstat (limited to 'drivers/pci/quirks.c')
-rw-r--r-- | drivers/pci/quirks.c | 135 |
1 files changed, 122 insertions, 13 deletions
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d54a182a09cf..6b635022f2fe 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -461,6 +461,7 @@ static void quirk_nfp6000(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP4000, quirk_nfp6000); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000, quirk_nfp6000); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP5000, quirk_nfp6000); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000_VF, quirk_nfp6000); /* On IBM Crocodile ipr SAS adapters, expand BAR to system page size */ @@ -2106,6 +2107,7 @@ static void quirk_netmos(struct pci_dev *dev) if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return; + /* else: fall through */ case PCI_DEVICE_ID_NETMOS_9735: case PCI_DEVICE_ID_NETMOS_9745: case PCI_DEVICE_ID_NETMOS_9845: @@ -4554,27 +4556,79 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev) return 0; } -static const struct pci_dev_enable_acs { +static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev) +{ + int pos; + u32 cap, ctrl; + + if (!pci_quirk_intel_spt_pch_acs_match(dev)) + return -ENOTTY; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return -ENOTTY; + + pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap); + pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl); + + ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC); + + pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl); + + pci_info(dev, "Intel SPT PCH root port workaround: disabled ACS redirect\n"); + + return 0; +} + +static const struct pci_dev_acs_ops { u16 vendor; u16 device; int (*enable_acs)(struct pci_dev *dev); -} pci_dev_enable_acs[] = { - { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs }, - { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_spt_pch_acs }, - { 0 } + int (*disable_acs_redir)(struct pci_dev *dev); +} pci_dev_acs_ops[] = { + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + .enable_acs = pci_quirk_enable_intel_pch_acs, + }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + .enable_acs = pci_quirk_enable_intel_spt_pch_acs, + .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir, + }, }; int pci_dev_specific_enable_acs(struct pci_dev *dev) { - const struct pci_dev_enable_acs *i; - int ret; + const struct pci_dev_acs_ops *p; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) { + p = &pci_dev_acs_ops[i]; + if ((p->vendor == dev->vendor || + p->vendor == (u16)PCI_ANY_ID) && + (p->device == dev->device || + p->device == (u16)PCI_ANY_ID) && + p->enable_acs) { + ret = p->enable_acs(dev); + if (ret >= 0) + return ret; + } + } - for (i = pci_dev_enable_acs; i->enable_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->enable_acs(dev); + return -ENOTTY; +} + +int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) +{ + const struct pci_dev_acs_ops *p; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) { + p = &pci_dev_acs_ops[i]; + if ((p->vendor == dev->vendor || + p->vendor == (u16)PCI_ANY_ID) && + (p->device == dev->device || + p->device == (u16)PCI_ANY_ID) && + p->disable_acs_redir) { + ret = p->disable_acs_redir(dev); if (ret >= 0) return ret; } @@ -4756,6 +4810,61 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); /* + * Some IDT switches incorrectly flag an ACS Source Validation error on + * completions for config read requests even though PCIe r4.0, sec + * 6.12.1.1, says that completions are never affected by ACS Source + * Validation. Here's the text of IDT 89H32H8G3-YC, erratum #36: + * + * Item #36 - Downstream port applies ACS Source Validation to Completions + * Section 6.12.1.1 of the PCI Express Base Specification 3.1 states that + * completions are never affected by ACS Source Validation. However, + * completions received by a downstream port of the PCIe switch from a + * device that has not yet captured a PCIe bus number are incorrectly + * dropped by ACS Source Validation by the switch downstream port. + * + * The workaround suggested by IDT is to issue a config write to the + * downstream device before issuing the first config read. This allows the + * downstream device to capture its bus and device numbers (see PCIe r4.0, + * sec 2.2.9), thus avoiding the ACS error on the completion. + * + * However, we don't know when the device is ready to accept the config + * write, so we do config reads until we receive a non-Config Request Retry + * Status, then do the config write. + * + * To avoid hitting the erratum when doing the config reads, we disable ACS + * SV around this process. + */ +int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout) +{ + int pos; + u16 ctrl = 0; + bool found; + struct pci_dev *bridge = bus->self; + + pos = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ACS); + + /* Disable ACS SV before initial config reads */ + if (pos) { + pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl); + if (ctrl & PCI_ACS_SV) + pci_write_config_word(bridge, pos + PCI_ACS_CTRL, + ctrl & ~PCI_ACS_SV); + } + + found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout); + + /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */ + if (found) + pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0); + + /* Re-enable ACS_SV if it was previously enabled */ + if (ctrl & PCI_ACS_SV) + pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl); + + return found; +} + +/* * Microsemi Switchtec NTB uses devfn proxy IDs to move TLPs between * NT endpoints via the internal switch fabric. These IDs replace the * originating requestor ID TLPs which access host memory on peer NTB |