Skip to content

Commit c56d445

Browse files
Hariprasad Shenaibjorn-helgaas
Hariprasad Shenai
authored andcommitted
PCI: Turn off Request Attributes to avoid Chelsio T5 Completion erratum
The Chelsio T5 has a PCIe compliance erratum that causes Malformed TLP or Unexpected Completion errors in some systems, which may cause device access timeouts. Per PCIe r3.0, sec 2.2.9, "Completion headers must supply the same values for the Attribute as were supplied in the header of the corresponding Request, except as explicitly allowed when IDO is used." Instead of copying the Attributes from the Request to the Completion, the T5 always generates Completions with zero Attributes. The receiver of a Completion whose Attributes don't match the Request may accept it (which itself seems non-compliant based on sec 2.3.2), or it may handle it as a Malformed TLP or an Unexpected Completion, which will probably lead to a device access timeout. Work around this by disabling "Relaxed Ordering" and "No Snoop" in the Root Port so it always generate Requests with zero Attributes. This does affect all other devices which are downstream of that Root Port, but these are performance optimizations that should not make a functional difference. Note that Configuration Space accesses are never supposed to have TLP Attributes, so we're safe waiting till after any Configuration Space accesses to do the Root Port "fixup". Based on original work by Casey Leedom <[email protected]> [bhelgaas: changelog, comments, rename to pci_find_pcie_root_port(), rework to use pci_upstream_bridge() and check for Root Port device type, edit diagnostics to clarify intent and devices affected] Signed-off-by: Hariprasad Shenai <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 68d0d97 commit c56d445

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

drivers/pci/pci.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,30 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
457457
}
458458
EXPORT_SYMBOL(pci_find_parent_resource);
459459

460+
/**
461+
* pci_find_pcie_root_port - return PCIe Root Port
462+
* @dev: PCI device to query
463+
*
464+
* Traverse up the parent chain and return the PCIe Root Port PCI Device
465+
* for a given PCI Device.
466+
*/
467+
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev)
468+
{
469+
struct pci_dev *bridge, *highest_pcie_bridge = NULL;
470+
471+
bridge = pci_upstream_bridge(dev);
472+
while (bridge && pci_is_pcie(bridge)) {
473+
highest_pcie_bridge = bridge;
474+
bridge = pci_upstream_bridge(bridge);
475+
}
476+
477+
if (pci_pcie_type(highest_pcie_bridge) != PCI_EXP_TYPE_ROOT_PORT)
478+
return NULL;
479+
480+
return highest_pcie_bridge;
481+
}
482+
EXPORT_SYMBOL(pci_find_pcie_root_port);
483+
460484
/**
461485
* pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
462486
* @dev: the PCI device to operate on

drivers/pci/quirks.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3691,6 +3691,63 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(0x1797, 0x6868, PCI_CLASS_NOT_DEFINED, 8,
36913691
DECLARE_PCI_FIXUP_CLASS_EARLY(0x1797, 0x6869, PCI_CLASS_NOT_DEFINED, 8,
36923692
quirk_tw686x_class);
36933693

3694+
/*
3695+
* Per PCIe r3.0, sec 2.2.9, "Completion headers must supply the same
3696+
* values for the Attribute as were supplied in the header of the
3697+
* corresponding Request, except as explicitly allowed when IDO is used."
3698+
*
3699+
* If a non-compliant device generates a completion with a different
3700+
* attribute than the request, the receiver may accept it (which itself
3701+
* seems non-compliant based on sec 2.3.2), or it may handle it as a
3702+
* Malformed TLP or an Unexpected Completion, which will probably lead to a
3703+
* device access timeout.
3704+
*
3705+
* If the non-compliant device generates completions with zero attributes
3706+
* (instead of copying the attributes from the request), we can work around
3707+
* this by disabling the "Relaxed Ordering" and "No Snoop" attributes in
3708+
* upstream devices so they always generate requests with zero attributes.
3709+
*
3710+
* This affects other devices under the same Root Port, but since these
3711+
* attributes are performance hints, there should be no functional problem.
3712+
*
3713+
* Note that Configuration Space accesses are never supposed to have TLP
3714+
* Attributes, so we're safe waiting till after any Configuration Space
3715+
* accesses to do the Root Port fixup.
3716+
*/
3717+
static void quirk_disable_root_port_attributes(struct pci_dev *pdev)
3718+
{
3719+
struct pci_dev *root_port = pci_find_pcie_root_port(pdev);
3720+
3721+
if (!root_port) {
3722+
dev_warn(&pdev->dev, "PCIe Completion erratum may cause device errors\n");
3723+
return;
3724+
}
3725+
3726+
dev_info(&root_port->dev, "Disabling No Snoop/Relaxed Ordering Attributes to avoid PCIe Completion erratum in %s\n",
3727+
dev_name(&pdev->dev));
3728+
pcie_capability_clear_and_set_word(root_port, PCI_EXP_DEVCTL,
3729+
PCI_EXP_DEVCTL_RELAX_EN |
3730+
PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
3731+
}
3732+
3733+
/*
3734+
* The Chelsio T5 chip fails to copy TLP Attributes from a Request to the
3735+
* Completion it generates.
3736+
*/
3737+
static void quirk_chelsio_T5_disable_root_port_attributes(struct pci_dev *pdev)
3738+
{
3739+
/*
3740+
* This mask/compare operation selects for Physical Function 4 on a
3741+
* T5. We only need to fix up the Root Port once for any of the
3742+
* PFs. PF[0..3] have PCI Device IDs of 0x50xx, but PF4 is uniquely
3743+
* 0x54xx so we use that one,
3744+
*/
3745+
if ((pdev->device & 0xff00) == 0x5400)
3746+
quirk_disable_root_port_attributes(pdev);
3747+
}
3748+
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
3749+
quirk_chelsio_T5_disable_root_port_attributes);
3750+
36943751
/*
36953752
* AMD has indicated that the devices below do not support peer-to-peer
36963753
* in any system where they are found in the southbridge with an AMD

include/linux/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ void pci_bus_add_device(struct pci_dev *dev);
820820
void pci_read_bridge_bases(struct pci_bus *child);
821821
struct resource *pci_find_parent_resource(const struct pci_dev *dev,
822822
struct resource *res);
823+
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev);
823824
u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin);
824825
int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge);
825826
u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);

0 commit comments

Comments
 (0)