struct pcie_link_state {
        struct list_head sibiling;
        struct pci_dev *pdev;
+       bool downstream_has_switch;
+
+       struct pcie_link_state *parent;
+       struct list_head children;
+       struct list_head link;
 
        /* ASPM state */
        unsigned int support_state;
        link_state->clk_pm_enabled = !!enable;
 }
 
-static void pcie_check_clock_pm(struct pci_dev *pdev)
+static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist)
 {
        int pos;
        u32 reg32;
                if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
                        enabled = 0;
        }
-       link_state->clk_pm_capable = capable;
        link_state->clk_pm_enabled = enabled;
        link_state->bios_clk_state = enabled;
-       pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
+       if (!blacklist) {
+               link_state->clk_pm_capable = capable;
+               pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
+       } else {
+               link_state->clk_pm_capable = 0;
+               pcie_set_clock_pm(pdev, 0);
+       }
+}
+
+static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev)
+{
+       struct pci_dev *child_dev;
+
+       list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
+               if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM)
+                       return true;
+       }
+       return false;
 }
 
 /*
 {
        struct pci_dev *child_dev;
 
-       /* If no child, disable the link */
+       /* If no child, ignore the link */
        if (list_empty(&pdev->subordinate->devices))
-               return 0;
+               return state;
        list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
                if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
                        /*
        int valid = 1;
        struct pcie_link_state *link_state = pdev->link_state;
 
+       /* If no child, disable the link */
+       if (list_empty(&pdev->subordinate->devices))
+               state = 0;
        /*
         * if the downstream component has pci bridge function, don't do ASPM
         * now
        link_state->enabled_state = state;
 }
 
+static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link)
+{
+       struct pcie_link_state *root_port_link = link;
+       while (root_port_link->parent)
+               root_port_link = root_port_link->parent;
+       return root_port_link;
+}
+
+/* check the whole hierarchy, and configure each link in the hierarchy */
 static void __pcie_aspm_configure_link_state(struct pci_dev *pdev,
        unsigned int state)
 {
        struct pcie_link_state *link_state = pdev->link_state;
+       struct pcie_link_state *root_port_link = get_root_port_link(link_state);
+       struct pcie_link_state *leaf;
 
-       if (link_state->support_state == 0)
-               return;
        state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
 
-       /* state 0 means disabling aspm */
-       state = pcie_aspm_check_state(pdev, state);
+       /* check all links who have specific root port link */
+       list_for_each_entry(leaf, &link_list, sibiling) {
+               if (!list_empty(&leaf->children) ||
+                       get_root_port_link(leaf) != root_port_link)
+                       continue;
+               state = pcie_aspm_check_state(leaf->pdev, state);
+       }
+       /* check root port link too in case it hasn't children */
+       state = pcie_aspm_check_state(root_port_link->pdev, state);
+
        if (link_state->enabled_state == state)
                return;
-       __pcie_aspm_config_link(pdev, state);
+
+       /*
+        * we must change the hierarchy. See comments in
+        * __pcie_aspm_config_link for the order
+        **/
+       if (state & PCIE_LINK_STATE_L1) {
+               list_for_each_entry(leaf, &link_list, sibiling) {
+                       if (get_root_port_link(leaf) == root_port_link)
+                               __pcie_aspm_config_link(leaf->pdev, state);
+               }
+       } else {
+               list_for_each_entry_reverse(leaf, &link_list, sibiling) {
+                       if (get_root_port_link(leaf) == root_port_link)
+                               __pcie_aspm_config_link(leaf->pdev, state);
+               }
+       }
 }
 
 /*
        unsigned int state;
        struct pcie_link_state *link_state;
        int error = 0;
+       int blacklist;
 
        if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
                return;
        if (list_empty(&pdev->subordinate->devices))
                goto out;
 
-       if (pcie_aspm_sanity_check(pdev))
-               goto out;
+       blacklist = !!pcie_aspm_sanity_check(pdev);
 
        mutex_lock(&aspm_lock);
 
        link_state = kzalloc(sizeof(*link_state), GFP_KERNEL);
        if (!link_state)
                goto unlock_out;
-       pdev->link_state = link_state;
 
-       pcie_aspm_configure_common_clock(pdev);
+       link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev);
+       INIT_LIST_HEAD(&link_state->children);
+       INIT_LIST_HEAD(&link_state->link);
+       if (pdev->bus->self) {/* this is a switch */
+               struct pcie_link_state *parent_link_state;
 
-       pcie_aspm_cap_init(pdev);
+               parent_link_state = pdev->bus->parent->self->link_state;
+               if (!parent_link_state) {
+                       kfree(link_state);
+                       goto unlock_out;
+               }
+               list_add(&link_state->link, &parent_link_state->children);
+               link_state->parent = parent_link_state;
+       }
 
-       /* config link state to avoid BIOS error */
-       state = pcie_aspm_check_state(pdev, policy_to_aspm_state(pdev));
-       __pcie_aspm_config_link(pdev, state);
+       pdev->link_state = link_state;
 
-       pcie_check_clock_pm(pdev);
+       if (!blacklist) {
+               pcie_aspm_configure_common_clock(pdev);
+               pcie_aspm_cap_init(pdev);
+       } else {
+               link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
+               link_state->bios_aspm_state = 0;
+               /* Set support state to 0, so we will disable ASPM later */
+               link_state->support_state = 0;
+       }
 
        link_state->pdev = pdev;
        list_add(&link_state->sibiling, &link_list);
 
+       if (link_state->downstream_has_switch) {
+               /*
+                * If link has switch, delay the link config. The leaf link
+                * initialization will config the whole hierarchy. but we must
+                * make sure BIOS doesn't set unsupported link state
+                **/
+               state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state);
+               __pcie_aspm_config_link(pdev, state);
+       } else
+               __pcie_aspm_configure_link_state(pdev,
+                       policy_to_aspm_state(pdev));
+
+       pcie_check_clock_pm(pdev, blacklist);
+
 unlock_out:
        if (error)
                free_link_state(pdev);
        /* All functions are removed, so just disable ASPM for the link */
        __pcie_aspm_config_one_dev(parent, 0);
        list_del(&link_state->sibiling);
+       list_del(&link_state->link);
        /* Clock PM is for endpoint device */
 
        free_link_state(parent);