@@ -49,7 +49,6 @@ struct aspm_latency {
struct pcie_link_state {
struct pci_dev *pdev; /* Upstream component of the Link */
struct pci_dev *downstream; /* Downstream component, function 0 */
- struct pcie_link_state *root; /* pointer to the root port link */
struct list_head sibling; /* node in link_list */
/* ASPM state */
@@ -851,6 +850,25 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
return 0;
}
+/*
+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
+ * hierarchies. Note that some PCIe host implementations omit
+ * the root ports entirely, in which case a downstream port on
+ * a switch may become the root of the link state chain for all
+ * its subordinate endpoints.
+ */
+static struct pci_dev *pcie_get_root(struct pci_dev *pdev)
+{
+ struct pci_dev *parent = pci_get_parent(pdev);
+
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE || !parent) {
+ return pdev;
+ } else {
+ return pcie_get_root(parent);
+ }
+}
+
static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
{
struct pcie_link_state *link;
@@ -863,29 +881,6 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
link->pdev = pdev;
link->downstream = pci_function_0(pdev->subordinate);
- /*
- * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
- * hierarchies. Note that some PCIe host implementations omit
- * the root ports entirely, in which case a downstream port on
- * a switch may become the root of the link state chain for all
- * its subordinate endpoints.
- */
- if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE ||
- !pdev->bus->parent->self) {
- link->root = link;
- } else {
- struct pci_dev *parent;
-
- parent = pci_get_parent(pdev);
- if (!parent->link_state) {
- kfree(link);
- return NULL;
- }
-
- link->root = parent->link_state->root;
- }
-
list_add(&link->sibling, &link_list);
pdev->link_state = link;
return link;
@@ -972,21 +967,26 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
static void pcie_update_aspm_capable(struct pcie_link_state *root)
{
struct pcie_link_state *link;
- struct pci_dev *parent = pci_get_parent(root->pdev);
+ struct pci_dev *dev, *root_dev;
- if (parent && parent->link_state)
- root = parent->link_state->root;
+ /* Ensure it is the root device */
+ root_dev = pcie_get_root(root->pdev);
+ root = root_dev ? root_dev->link_state : NULL;
list_for_each_entry(link, &link_list, sibling) {
- if (link->root != root)
+ dev = pcie_get_root(link->pdev);
+ if (dev->link_state != root)
continue;
+
link->aspm_capable = link->aspm_support;
}
list_for_each_entry(link, &link_list, sibling) {
struct pci_dev *child;
struct pci_bus *linkbus = link->pdev->subordinate;
- if (link->root != root)
+ dev = pcie_get_root(link->pdev);
+ if (dev->link_state != root)
continue;
+
list_for_each_entry(child, &linkbus->devices, bus_list) {
if ((pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT) &&
(pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END))
@@ -999,9 +999,9 @@ static void pcie_update_aspm_capable(struct pcie_link_state *root)
/* @pdev: the endpoint device */
void pcie_aspm_exit_link_state(struct pci_dev *pdev)
{
- struct pci_dev *parent_dev;
+ struct pci_dev *parent_dev, *root_dev;
struct pci_dev *parent = pdev->bus->self;
- struct pcie_link_state *link, *root, *parent_link;
+ struct pcie_link_state *link, *parent_link;
if (!parent || !parent->link_state)
return;
@@ -1016,7 +1016,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
goto out;
link = parent->link_state;
- root = link->root;
+ root_dev = pcie_get_root(link->pdev);
parent_dev = pci_get_parent(link->pdev);
parent_link = parent_dev ? parent_dev->link_state : NULL;
@@ -1028,7 +1028,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
/* Recheck latencies and configure upstream links */
if (parent_link) {
- pcie_update_aspm_capable(root);
+ pcie_update_aspm_capable(root_dev->link_state);
pcie_config_aspm_path(parent_link);
}
out:
@@ -1040,6 +1040,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
void pcie_aspm_pm_state_change(struct pci_dev *pdev)
{
struct pcie_link_state *link = pdev->link_state;
+ struct pci_dev *root = pcie_get_root(pdev);
if (aspm_disabled || !link)
return;
@@ -1049,7 +1050,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev)
*/
down_read(&pci_bus_sem);
mutex_lock(&aspm_lock);
- pcie_update_aspm_capable(link->root);
+ pcie_update_aspm_capable(root->link_state);
pcie_config_aspm_path(link);
mutex_unlock(&aspm_lock);
up_read(&pci_bus_sem);