|
@@ -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)
|