@@ -5109,25 +5109,48 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
* @speed: storage for minimum speed
* @width: storage for minimum width
*
- * This function will walk up the PCI device chain and determine the minimum
- * link width and speed of the device.
+ * This function use pcie_bandwidth_available() for determining the minimum
+ * link width and speed of the device. Legacy code is kept for compatibility.
*/
int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
enum pcie_link_width *width)
{
- int ret;
+ int bw;
+
+ return pcie_bandwidth_available(dev, speed, width, &bw, NULL);
+}
+EXPORT_SYMBOL(pcie_get_minimum_link);
+
+/**
+ * pcie_bandwidth_available - determine minimum link settings of a PCIe
+ device and its bandwidth limitation
+ * @dev: PCI device to query
+ * @speed: storage for minimum speed
+ * @width: storage for minimum width
+ * @bw: storage for link bandwidth
+ * @limiting_dev: storage for device causing the bandwidth limitation
+ *
+ * This function walks up the PCI device chain and determines the minimum width,
+ * minimum speed and available bandwidth of the device.
+ */
+int pcie_bandwidth_available(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width, int *bw,
+ struct pci_dev **limiting_dev)
+{
+ int err;
*speed = PCI_SPEED_UNKNOWN;
*width = PCIE_LNK_WIDTH_UNKNOWN;
+ *bw = 0;
while (dev) {
u16 lnksta;
enum pci_bus_speed next_speed;
enum pcie_link_width next_width;
- ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
- if (ret)
- return ret;
+ err = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+ if (err)
+ return err;
next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
@@ -5139,12 +5162,20 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
if (next_width < *width)
*width = next_width;
+ /* Check if current device limits the total bandwidth */
+ if (!(*bw) ||
+ (*bw > next_width * PCIE_SPEED2MBS_ENC(next_speed))) {
+ if (limiting_dev)
+ *limiting_dev = dev;
+ *bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+ }
+
dev = dev->bus->self;
}
return 0;
}
-EXPORT_SYMBOL(pcie_get_minimum_link);
+EXPORT_SYMBOL(pcie_bandwidth_available);
/**
* pcie_get_speed_cap - queries for the PCI device's link speed capability
@@ -1098,6 +1098,9 @@ static inline int pci_is_managed(struct pci_dev *pdev)
int pcie_set_mps(struct pci_dev *dev, int mps);
int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
enum pcie_link_width *width);
+int pcie_bandwidth_available(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width, int *bw,
+ struct pci_dev **limiting_dev);
int pcie_get_speed_cap(struct pci_dev *dev, enum pci_bus_speed *speed);
int pcie_get_width_cap(struct pci_dev *dev, enum pcie_link_width *width);
int pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,