diff options
author | Jordan_Hargrave@Dell.com <Jordan_Hargrave@Dell.com> | 2011-05-09 15:24:55 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2011-05-10 15:43:41 -0700 |
commit | e522a7126c7c144a1dd14c6f217ac31e71082b1d (patch) | |
tree | 254995a97ed165bc009812969ecafb8b5ef063bc /drivers/pci | |
parent | a246670ddee3132fa71f8993d3989ad8ac04d965 (diff) | |
download | linux-e522a7126c7c144a1dd14c6f217ac31e71082b1d.tar.gz linux-e522a7126c7c144a1dd14c6f217ac31e71082b1d.tar.bz2 linux-e522a7126c7c144a1dd14c6f217ac31e71082b1d.zip |
PCI: Set PCIE maxpayload for card during hotplug insertion
The following patch sets the MaxPayload setting to match the parent
reading when inserting a PCIE card into a hotplug slot. On our system,
the upstream bridge is set to 256, but when inserting a card, the card
setting defaults to 128. As soon as I/O is performed to the card it
starts receiving errors since the payload size is too small.
Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jordan Hargrave <jordan_hargrave@dell.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/pcihp_slot.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c index 80b461c98557..749fdf070319 100644 --- a/drivers/pci/hotplug/pcihp_slot.c +++ b/drivers/pci/hotplug/pcihp_slot.c @@ -158,6 +158,47 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) */ } +/* Program PCIE MaxPayload setting on device: ensure parent maxpayload <= device */ +static int pci_set_payload(struct pci_dev *dev) +{ + int pos, ppos; + u16 pctl, psz; + u16 dctl, dsz, dcap, dmax; + struct pci_dev *parent; + + parent = dev->bus->self; + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return 0; + + /* Read Device MaxPayload capability and setting */ + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl); + pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap); + dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5; + dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD); + + /* Read Parent MaxPayload setting */ + ppos = pci_find_capability(parent, PCI_CAP_ID_EXP); + if (!ppos) + return 0; + pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl); + psz = (pctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5; + + /* If parent payload > device max payload -> error + * If parent payload > device payload -> set speed + * If parent payload <= device payload -> do nothing + */ + if (psz > dmax) + return -1; + else if (psz > dsz) { + dev_info(&dev->dev, "Setting MaxPayload to %d\n", 128 << psz); + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, + (dctl & ~PCI_EXP_DEVCTL_PAYLOAD) + + (psz << 5)); + } + return 0; +} + void pci_configure_slot(struct pci_dev *dev) { struct pci_dev *cdev; @@ -169,6 +210,10 @@ void pci_configure_slot(struct pci_dev *dev) (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) return; + ret = pci_set_payload(dev); + if (ret) + dev_warn(&dev->dev, "could not set device max payload\n"); + memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); if (ret) |