mtip32xx: Fix ERO and NoSnoop values in PCIe upstream on AMD systems

A hardware quirk in P320h/P420m interfere with PCIe transactions on some
AMD chipsets, making P320h/P420m unusable. This workaround is to disable
ERO and NoSnoop bits in the parent and root complex for normal
functioning of these devices

NOTE: This workaround is specific to AMD chipset with a PCIe upstream
device with device id 0x5aXX

Signed-off-by: Asai Thambi S P <asamymuthupa@micron.com>
Signed-off-by: Sam Bradshaw <sbradshaw@micron.com>
Cc: stable@kernel.org
Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
Asai Thambi S P 2014-03-13 18:45:15 -07:00 committed by Jens Axboe
parent af5ded8ccf
commit d1e714db81

View File

@ -4483,6 +4483,57 @@ static DEFINE_HANDLER(5);
static DEFINE_HANDLER(6);
static DEFINE_HANDLER(7);
static void mtip_disable_link_opts(struct driver_data *dd, struct pci_dev *pdev)
{
int pos;
unsigned short pcie_dev_ctrl;
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (pos) {
pci_read_config_word(pdev,
pos + PCI_EXP_DEVCTL,
&pcie_dev_ctrl);
if (pcie_dev_ctrl & (1 << 11) ||
pcie_dev_ctrl & (1 << 4)) {
dev_info(&dd->pdev->dev,
"Disabling ERO/No-Snoop on bridge device %04x:%04x\n",
pdev->vendor, pdev->device);
pcie_dev_ctrl &= ~(PCI_EXP_DEVCTL_NOSNOOP_EN |
PCI_EXP_DEVCTL_RELAX_EN);
pci_write_config_word(pdev,
pos + PCI_EXP_DEVCTL,
pcie_dev_ctrl);
}
}
}
static void mtip_fix_ero_nosnoop(struct driver_data *dd, struct pci_dev *pdev)
{
/*
* This workaround is specific to AMD/ATI chipset with a PCI upstream
* device with device id 0x5aXX
*/
if (pdev->bus && pdev->bus->self) {
if (pdev->bus->self->vendor == PCI_VENDOR_ID_ATI &&
((pdev->bus->self->device & 0xff00) == 0x5a00)) {
mtip_disable_link_opts(dd, pdev->bus->self);
} else {
/* Check further up the topology */
struct pci_dev *parent_dev = pdev->bus->self;
if (parent_dev->bus &&
parent_dev->bus->parent &&
parent_dev->bus->parent->self &&
parent_dev->bus->parent->self->vendor ==
PCI_VENDOR_ID_ATI &&
(parent_dev->bus->parent->self->device &
0xff00) == 0x5a00) {
mtip_disable_link_opts(dd,
parent_dev->bus->parent->self);
}
}
}
}
/*
* Called for each supported PCI device detected.
*
@ -4634,6 +4685,8 @@ static int mtip_pci_probe(struct pci_dev *pdev,
goto msi_initialize_err;
}
mtip_fix_ero_nosnoop(dd, pdev);
/* Initialize the block layer. */
rv = mtip_block_initialize(dd);
if (rv < 0) {