forked from Minki/linux
PCI: Workaround IDT switch ACS Source Validation erratum
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. Workaround: Issue a CfgWr1 to the downstream device before issuing the first CfgRd1 to the device. This allows the downstream device to capture its bus number; ACS Source Validation no longer stops completions from being forwarded by the downstream port. It has been observed that Microsoft Windows implements this workaround already; however, some versions of Linux and other operating systems may not. When doing the first config read to probe for a device, if the device is behind an IDT switch with this erratum: 1. Disable ACS Source Validation if enabled 2. Wait for device to become ready to accept config accesses (by using the Config Request Retry Status mechanism) 3. Do a config write to the endpoint 4. Enable ACS Source Validation (if it was enabled to begin with) The workaround suggested by IDT is basically only step 3, but we don't know when the device is ready to accept config requests. That means we need to do config reads until we receive a non-Config Request Retry Status, which means we need to disable ACS SV temporarily. Signed-off-by: James Puthukattukaran <james.puthukattukaran@oracle.com> [bhelgaas: changelog, clean up whitespace, fold in unused variable fix from Anders Roxell <anders.roxell@linaro.org>] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
parent
ce397d215c
commit
aa667c6408
@ -225,6 +225,10 @@ enum pci_bar_type {
|
||||
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
|
||||
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
|
||||
int crs_timeout);
|
||||
bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
|
||||
int crs_timeout);
|
||||
int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout);
|
||||
|
||||
int pci_setup_device(struct pci_dev *dev);
|
||||
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
struct resource *res, unsigned int reg);
|
||||
|
@ -2156,8 +2156,8 @@ static bool pci_bus_wait_crs(struct pci_bus *bus, int devfn, u32 *l,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
||||
int timeout)
|
||||
bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
||||
int timeout)
|
||||
{
|
||||
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
|
||||
return false;
|
||||
@ -2172,6 +2172,24 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
||||
int timeout)
|
||||
{
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
struct pci_dev *bridge = bus->self;
|
||||
|
||||
/*
|
||||
* Certain IDT switches have an issue where they improperly trigger
|
||||
* ACS Source Validation errors on completions for config reads.
|
||||
*/
|
||||
if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
|
||||
bridge->device == 0x80b5)
|
||||
return pci_idt_bus_quirk(bus, devfn, l, timeout);
|
||||
#endif
|
||||
|
||||
return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
|
||||
|
||||
/*
|
||||
|
@ -4753,3 +4753,58 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID,
|
||||
PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user