Merge tag 'pci-v3.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[linux-3.10.git] / drivers / pci / pcie / aspm.c
index 23fabb3..d320df6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * File:       drivers/pci/pcie/aspm.c
- * Enabling PCIE link L0s/L1 state and Clock Power Management
+ * Enabling PCIe link L0s/L1 state and Clock Power Management
  *
  * Copyright (C) 2007 Intel
  * Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com)
 #endif
 #define MODULE_PARAM_PREFIX "pcie_aspm."
 
+/* Note: those are not register definitions */
+#define ASPM_STATE_L0S_UP      (1)     /* Upstream direction L0s state */
+#define ASPM_STATE_L0S_DW      (2)     /* Downstream direction L0s state */
+#define ASPM_STATE_L1          (4)     /* L1 state */
+#define ASPM_STATE_L0S         (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
+#define ASPM_STATE_ALL         (ASPM_STATE_L0S | ASPM_STATE_L1)
+
 struct aspm_latency {
        u32 l0s;                        /* L0s latency (nsec) */
        u32 l1;                         /* L1 latency (nsec) */
@@ -33,25 +40,27 @@ struct aspm_latency {
 
 struct pcie_link_state {
        struct pci_dev *pdev;           /* Upstream component of the Link */
+       struct pcie_link_state *root;   /* pointer to the root port link */
        struct pcie_link_state *parent; /* pointer to the parent Link state */
        struct list_head sibling;       /* node in link_list */
        struct list_head children;      /* list of child link states */
        struct list_head link;          /* node in parent's children list */
 
        /* ASPM state */
-       u32 aspm_support:2;             /* Supported ASPM state */
-       u32 aspm_enabled:2;             /* Enabled ASPM state */
-       u32 aspm_default:2;             /* Default ASPM state by BIOS */
+       u32 aspm_support:3;             /* Supported ASPM state */
+       u32 aspm_enabled:3;             /* Enabled ASPM state */
+       u32 aspm_capable:3;             /* Capable ASPM state with latency */
+       u32 aspm_default:3;             /* Default ASPM state by BIOS */
+       u32 aspm_disable:3;             /* Disabled ASPM state */
 
        /* Clock PM state */
        u32 clkpm_capable:1;            /* Clock PM capable? */
        u32 clkpm_enabled:1;            /* Current Clock PM state */
        u32 clkpm_default:1;            /* Default Clock PM state by BIOS */
 
-       u32 has_switch:1;               /* Downstream has switches? */
-
-       /* Latencies */
-       struct aspm_latency latency;    /* Exit latency */
+       /* Exit latencies */
+       struct aspm_latency latency_up; /* Upstream direction exit latency */
+       struct aspm_latency latency_dw; /* Downstream direction exit latency */
        /*
         * Endpoint acceptable latencies. A pcie downstream port only
         * has one slot under it, so at most there are 8 functions.
@@ -60,13 +69,22 @@ struct pcie_link_state {
 };
 
 static int aspm_disabled, aspm_force;
+static bool aspm_support_enabled = true;
 static DEFINE_MUTEX(aspm_lock);
 static LIST_HEAD(link_list);
 
 #define POLICY_DEFAULT 0       /* BIOS default setting */
 #define POLICY_PERFORMANCE 1   /* high performance */
 #define POLICY_POWERSAVE 2     /* high power saving */
+
+#ifdef CONFIG_PCIEASPM_PERFORMANCE
+static int aspm_policy = POLICY_PERFORMANCE;
+#elif defined CONFIG_PCIEASPM_POWERSAVE
+static int aspm_policy = POLICY_POWERSAVE;
+#else
 static int aspm_policy;
+#endif
+
 static const char *policy_str[] = {
        [POLICY_DEFAULT] = "default",
        [POLICY_PERFORMANCE] = "performance",
@@ -83,7 +101,7 @@ static int policy_to_aspm_state(struct pcie_link_state *link)
                return 0;
        case POLICY_POWERSAVE:
                /* Enable ASPM L0s/L1 */
-               return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
+               return ASPM_STATE_ALL;
        case POLICY_DEFAULT:
                return link->aspm_default;
        }
@@ -107,21 +125,16 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
 
 static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
 {
-       int pos;
-       u16 reg16;
        struct pci_dev *child;
        struct pci_bus *linkbus = link->pdev->subordinate;
 
        list_for_each_entry(child, &linkbus->devices, bus_list) {
-               pos = pci_find_capability(child, PCI_CAP_ID_EXP);
-               if (!pos)
-                       return;
-               pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
                if (enable)
-                       reg16 |= PCI_EXP_LNKCTL_CLKREQ_EN;
+                       pcie_capability_set_word(child, PCI_EXP_LNKCTL,
+                                                PCI_EXP_LNKCTL_CLKREQ_EN);
                else
-                       reg16 &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
-               pci_write_config_word(child, pos + PCI_EXP_LNKCTL, reg16);
+                       pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
+                                                  PCI_EXP_LNKCTL_CLKREQ_EN);
        }
        link->clkpm_enabled = !!enable;
 }
@@ -130,7 +143,7 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
 {
        /* Don't enable Clock PM if the link is not Clock PM capable */
        if (!link->clkpm_capable && enable)
-               return;
+               enable = 0;
        /* Need nothing if the specified equals to current state */
        if (link->clkpm_enabled == enable)
                return;
@@ -139,7 +152,7 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
 
 static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
 {
-       int pos, capable = 1, enabled = 1;
+       int capable = 1, enabled = 1;
        u32 reg32;
        u16 reg16;
        struct pci_dev *child;
@@ -147,16 +160,13 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
 
        /* All functions should have the same cap and state, take the worst */
        list_for_each_entry(child, &linkbus->devices, bus_list) {
-               pos = pci_find_capability(child, PCI_CAP_ID_EXP);
-               if (!pos)
-                       return;
-               pci_read_config_dword(child, pos + PCI_EXP_LNKCAP, &reg32);
+               pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &reg32);
                if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) {
                        capable = 0;
                        enabled = 0;
                        break;
                }
-               pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
+               pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
                if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
                        enabled = 0;
        }
@@ -165,18 +175,6 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
        link->clkpm_capable = (blacklist) ? 0 : capable;
 }
 
-static bool pcie_aspm_downstream_has_switch(struct pcie_link_state *link)
-{
-       struct pci_dev *child;
-       struct pci_bus *linkbus = link->pdev->subordinate;
-
-       list_for_each_entry(child, &linkbus->devices, bus_list) {
-               if (child->pcie_type == PCI_EXP_TYPE_UPSTREAM)
-                       return true;
-       }
-       return false;
-}
-
 /*
  * pcie_aspm_configure_common_clock: check if the 2 ends of a link
  *   could use common clock. If they are, configure them to use the
@@ -184,7 +182,7 @@ static bool pcie_aspm_downstream_has_switch(struct pcie_link_state *link)
  */
 static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
 {
-       int ppos, cpos, same_clock = 1;
+       int same_clock = 1;
        u16 reg16, parent_reg, child_reg[8];
        unsigned long start_jiffies;
        struct pci_dev *child, *parent = link->pdev;
@@ -194,49 +192,46 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
         * Configuration, so just check one function
         */
        child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
-       BUG_ON(!child->is_pcie);
+       BUG_ON(!pci_is_pcie(child));
 
        /* Check downstream component if bit Slot Clock Configuration is 1 */
-       cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
-       pci_read_config_word(child, cpos + PCI_EXP_LNKSTA, &reg16);
+       pcie_capability_read_word(child, PCI_EXP_LNKSTA, &reg16);
        if (!(reg16 & PCI_EXP_LNKSTA_SLC))
                same_clock = 0;
 
        /* Check upstream component if bit Slot Clock Configuration is 1 */
-       ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
-       pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+       pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
        if (!(reg16 & PCI_EXP_LNKSTA_SLC))
                same_clock = 0;
 
        /* Configure downstream component, all functions */
        list_for_each_entry(child, &linkbus->devices, bus_list) {
-               cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
-               pci_read_config_word(child, cpos + PCI_EXP_LNKCTL, &reg16);
+               pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
                child_reg[PCI_FUNC(child->devfn)] = reg16;
                if (same_clock)
                        reg16 |= PCI_EXP_LNKCTL_CCC;
                else
                        reg16 &= ~PCI_EXP_LNKCTL_CCC;
-               pci_write_config_word(child, cpos + PCI_EXP_LNKCTL, reg16);
+               pcie_capability_write_word(child, PCI_EXP_LNKCTL, reg16);
        }
 
        /* Configure upstream component */
-       pci_read_config_word(parent, ppos + PCI_EXP_LNKCTL, &reg16);
+       pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
        parent_reg = reg16;
        if (same_clock)
                reg16 |= PCI_EXP_LNKCTL_CCC;
        else
                reg16 &= ~PCI_EXP_LNKCTL_CCC;
-       pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+       pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
 
        /* Retrain link */
        reg16 |= PCI_EXP_LNKCTL_RL;
-       pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+       pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
 
        /* Wait for link training end. Break out after waiting for timeout */
        start_jiffies = jiffies;
        for (;;) {
-               pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+               pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
                if (!(reg16 & PCI_EXP_LNKSTA_LT))
                        break;
                if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
@@ -247,14 +242,11 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
                return;
 
        /* Training failed. Restore common clock configurations */
-       dev_printk(KERN_ERR, &parent->dev,
-                  "ASPM: Could not configure common clock\n");
-       list_for_each_entry(child, &linkbus->devices, bus_list) {
-               cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
-               pci_write_config_word(child, cpos + PCI_EXP_LNKCTL,
-                                     child_reg[PCI_FUNC(child->devfn)]);
-       }
-       pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, parent_reg);
+       dev_err(&parent->dev, "ASPM: Could not configure common clock\n");
+       list_for_each_entry(child, &linkbus->devices, bus_list)
+               pcie_capability_write_word(child, PCI_EXP_LNKCTL,
+                                          child_reg[PCI_FUNC(child->devfn)]);
+       pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg);
 }
 
 /* Convert L0s latency encoding to ns */
@@ -289,186 +281,173 @@ static u32 calc_l1_acceptable(u32 encoding)
        return (1000 << encoding);
 }
 
-static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state,
-                                    u32 *l0s, u32 *l1, u32 *enabled)
+struct aspm_register_info {
+       u32 support:2;
+       u32 enabled:2;
+       u32 latency_encoding_l0s;
+       u32 latency_encoding_l1;
+};
+
+static void pcie_get_aspm_reg(struct pci_dev *pdev,
+                             struct aspm_register_info *info)
 {
-       int pos;
        u16 reg16;
-       u32 reg32, encoding;
-
-       *l0s = *l1 = *enabled = 0;
-       pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
-       pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32);
-       *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
-       if (*state != PCIE_LINK_STATE_L0S &&
-           *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S))
-               *state = 0;
-       if (*state == 0)
+       u32 reg32;
+
+       pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &reg32);
+       info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
+       info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
+       info->latency_encoding_l1  = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
+       pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
+       info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
+}
+
+static void pcie_aspm_check_latency(struct pci_dev *endpoint)
+{
+       u32 latency, l1_switch_latency = 0;
+       struct aspm_latency *acceptable;
+       struct pcie_link_state *link;
+
+       /* Device not in D0 doesn't need latency check */
+       if ((endpoint->current_state != PCI_D0) &&
+           (endpoint->current_state != PCI_UNKNOWN))
                return;
 
-       encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
-       *l0s = calc_l0s_latency(encoding);
-       if (*state & PCIE_LINK_STATE_L1) {
-               encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
-               *l1 = calc_l1_latency(encoding);
+       link = endpoint->bus->self->link_state;
+       acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
+
+       while (link) {
+               /* Check upstream direction L0s latency */
+               if ((link->aspm_capable & ASPM_STATE_L0S_UP) &&
+                   (link->latency_up.l0s > acceptable->l0s))
+                       link->aspm_capable &= ~ASPM_STATE_L0S_UP;
+
+               /* Check downstream direction L0s latency */
+               if ((link->aspm_capable & ASPM_STATE_L0S_DW) &&
+                   (link->latency_dw.l0s > acceptable->l0s))
+                       link->aspm_capable &= ~ASPM_STATE_L0S_DW;
+               /*
+                * Check L1 latency.
+                * Every switch on the path to root complex need 1
+                * more microsecond for L1. Spec doesn't mention L0s.
+                */
+               latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
+               if ((link->aspm_capable & ASPM_STATE_L1) &&
+                   (latency + l1_switch_latency > acceptable->l1))
+                       link->aspm_capable &= ~ASPM_STATE_L1;
+               l1_switch_latency += 1000;
+
+               link = link->parent;
        }
-       pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
-       *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
 }
 
 static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 {
-       u32 support, l0s, l1, enabled;
        struct pci_dev *child, *parent = link->pdev;
        struct pci_bus *linkbus = parent->subordinate;
+       struct aspm_register_info upreg, dwreg;
 
        if (blacklist) {
-               /* Set support state to 0, so we will disable ASPM later */
-               link->aspm_support = 0;
-               link->aspm_default = 0;
-               link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
+               /* Set enabled/disable so that we will disable ASPM later */
+               link->aspm_enabled = ASPM_STATE_ALL;
+               link->aspm_disable = ASPM_STATE_ALL;
                return;
        }
 
        /* Configure common clock before checking latencies */
        pcie_aspm_configure_common_clock(link);
 
-       /* upstream component states */
-       pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled);
-       link->aspm_support = support;
-       link->latency.l0s = l0s;
-       link->latency.l1 = l1;
-       link->aspm_enabled = enabled;
-
-       /* downstream component states, all functions have the same setting */
+       /* Get upstream/downstream components' register state */
+       pcie_get_aspm_reg(parent, &upreg);
        child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
-       pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled);
-       link->aspm_support &= support;
-       link->latency.l0s = max_t(u32, link->latency.l0s, l0s);
-       link->latency.l1 = max_t(u32, link->latency.l1, l1);
+       pcie_get_aspm_reg(child, &dwreg);
 
-       if (!link->aspm_support)
-               return;
-
-       link->aspm_enabled &= link->aspm_support;
+       /*
+        * Setup L0s state
+        *
+        * Note that we must not enable L0s in either direction on a
+        * given link unless components on both sides of the link each
+        * support L0s.
+        */
+       if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S)
+               link->aspm_support |= ASPM_STATE_L0S;
+       if (dwreg.enabled & PCIE_LINK_STATE_L0S)
+               link->aspm_enabled |= ASPM_STATE_L0S_UP;
+       if (upreg.enabled & PCIE_LINK_STATE_L0S)
+               link->aspm_enabled |= ASPM_STATE_L0S_DW;
+       link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s);
+       link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s);
+
+       /* Setup L1 state */
+       if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1)
+               link->aspm_support |= ASPM_STATE_L1;
+       if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1)
+               link->aspm_enabled |= ASPM_STATE_L1;
+       link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
+       link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
+
+       /* Save default state */
        link->aspm_default = link->aspm_enabled;
 
-       /* ENDPOINT states*/
+       /* Setup initial capable state. Will be updated later */
+       link->aspm_capable = link->aspm_support;
+       /*
+        * If the downstream component has pci bridge function, don't
+        * do ASPM for now.
+        */
+       list_for_each_entry(child, &linkbus->devices, bus_list) {
+               if (pci_pcie_type(child) == PCI_EXP_TYPE_PCI_BRIDGE) {
+                       link->aspm_disable = ASPM_STATE_ALL;
+                       break;
+               }
+       }
+
+       /* Get and check endpoint acceptable latencies */
        list_for_each_entry(child, &linkbus->devices, bus_list) {
-               int pos;
                u32 reg32, encoding;
                struct aspm_latency *acceptable =
                        &link->acceptable[PCI_FUNC(child->devfn)];
 
-               if (child->pcie_type != PCI_EXP_TYPE_ENDPOINT &&
-                   child->pcie_type != PCI_EXP_TYPE_LEG_END)
+               if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
+                   pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
                        continue;
 
-               pos = pci_find_capability(child, PCI_CAP_ID_EXP);
-               pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
+               pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
+               /* Calculate endpoint L0s acceptable latency */
                encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
                acceptable->l0s = calc_l0s_acceptable(encoding);
-               if (link->aspm_support & PCIE_LINK_STATE_L1) {
-                       encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9;
-                       acceptable->l1 = calc_l1_acceptable(encoding);
-               }
-       }
-}
+               /* Calculate endpoint L1 acceptable latency */
+               encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9;
+               acceptable->l1 = calc_l1_acceptable(encoding);
 
-/**
- * __pcie_aspm_check_state_one - check latency for endpoint device.
- * @endpoint: pointer to the struct pci_dev of endpoint device
- *
- * TBD: The latency from the endpoint to root complex vary per switch's
- * upstream link state above the device. Here we just do a simple check
- * which assumes all links above the device can be in L1 state, that
- * is we just consider the worst case. If switch's upstream link can't
- * be put into L0S/L1, then our check is too strictly.
- */
-static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state)
-{
-       u32 l1_switch_latency = 0;
-       struct aspm_latency *acceptable;
-       struct pcie_link_state *link;
-
-       link = endpoint->bus->self->link_state;
-       state &= link->aspm_support;
-       acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
-
-       while (link && state) {
-               if ((state & PCIE_LINK_STATE_L0S) &&
-                   (link->latency.l0s > acceptable->l0s))
-                       state &= ~PCIE_LINK_STATE_L0S;
-               if ((state & PCIE_LINK_STATE_L1) &&
-                   (link->latency.l1 + l1_switch_latency > acceptable->l1))
-                       state &= ~PCIE_LINK_STATE_L1;
-               link = link->parent;
-               /*
-                * Every switch on the path to root complex need 1
-                * more microsecond for L1. Spec doesn't mention L0s.
-                */
-               l1_switch_latency += 1000;
-       }
-       return state;
-}
-
-static u32 pcie_aspm_check_state(struct pcie_link_state *link, u32 state)
-{
-       pci_power_t power_state;
-       struct pci_dev *child;
-       struct pci_bus *linkbus = link->pdev->subordinate;
-
-       /* If no child, ignore the link */
-       if (list_empty(&linkbus->devices))
-               return state;
-
-       list_for_each_entry(child, &linkbus->devices, bus_list) {
-               /*
-                * If downstream component of a link is pci bridge, we
-                * disable ASPM for now for the link
-                */
-               if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
-                       return 0;
-
-               if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT &&
-                    child->pcie_type != PCI_EXP_TYPE_LEG_END))
-                       continue;
-               /* Device not in D0 doesn't need check latency */
-               power_state = child->current_state;
-               if (power_state == PCI_D1 || power_state == PCI_D2 ||
-                   power_state == PCI_D3hot || power_state == PCI_D3cold)
-                       continue;
-               state = __pcie_aspm_check_state_one(child, state);
+               pcie_aspm_check_latency(child);
        }
-       return state;
 }
 
-static void __pcie_aspm_config_one_dev(struct pci_dev *pdev, unsigned int state)
+static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
 {
-       u16 reg16;
-       int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
-
-       pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
-       reg16 &= ~0x3;
-       reg16 |= state;
-       pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
+       pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
+                                          PCI_EXP_LNKCTL_ASPMC, val);
 }
 
-static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state)
+static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
 {
+       u32 upstream = 0, dwstream = 0;
        struct pci_dev *child, *parent = link->pdev;
        struct pci_bus *linkbus = parent->subordinate;
 
-       /* If no child, disable the link */
-       if (list_empty(&linkbus->devices))
-               state = 0;
-       /*
-        * If the downstream component has pci bridge function, don't
-        * do ASPM now.
-        */
-       list_for_each_entry(child, &linkbus->devices, bus_list) {
-               if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
-                       return;
+       /* Nothing to do if the link is already in the requested state */
+       state &= (link->aspm_capable & ~link->aspm_disable);
+       if (link->aspm_enabled == state)
+               return;
+       /* Convert ASPM state to upstream/downstream ASPM register state */
+       if (state & ASPM_STATE_L0S_UP)
+               dwstream |= PCI_EXP_LNKCTL_ASPM_L0S;
+       if (state & ASPM_STATE_L0S_DW)
+               upstream |= PCI_EXP_LNKCTL_ASPM_L0S;
+       if (state & ASPM_STATE_L1) {
+               upstream |= PCI_EXP_LNKCTL_ASPM_L1;
+               dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
        }
        /*
         * Spec 2.0 suggests all functions should be configured the
@@ -476,76 +455,24 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state)
         * upstream component first and then downstream, and vice
         * versa for disabling ASPM L1. Spec doesn't mention L0S.
         */
-       if (state & PCIE_LINK_STATE_L1)
-               __pcie_aspm_config_one_dev(parent, state);
-
+       if (state & ASPM_STATE_L1)
+               pcie_config_aspm_dev(parent, upstream);
        list_for_each_entry(child, &linkbus->devices, bus_list)
-               __pcie_aspm_config_one_dev(child, state);
-
-       if (!(state & PCIE_LINK_STATE_L1))
-               __pcie_aspm_config_one_dev(parent, state);
+               pcie_config_aspm_dev(child, dwstream);
+       if (!(state & ASPM_STATE_L1))
+               pcie_config_aspm_dev(parent, upstream);
 
        link->aspm_enabled = 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 pcie_link_state *link,
-                                            u32 state)
+static void pcie_config_aspm_path(struct pcie_link_state *link)
 {
-       struct pcie_link_state *leaf, *root = get_root_port_link(link);
-
-       state &= (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
-
-       /* Check all links who have specific root port link */
-       list_for_each_entry(leaf, &link_list, sibling) {
-               if (!list_empty(&leaf->children) ||
-                   get_root_port_link(leaf) != root)
-                       continue;
-               state = pcie_aspm_check_state(leaf, state);
-       }
-       /* Check root port link too in case it hasn't children */
-       state = pcie_aspm_check_state(root, state);
-       if (link->aspm_enabled == state)
-               return;
-       /*
-        * 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, sibling) {
-                       if (get_root_port_link(leaf) == root)
-                               __pcie_aspm_config_link(leaf, state);
-               }
-       } else {
-               list_for_each_entry_reverse(leaf, &link_list, sibling) {
-                       if (get_root_port_link(leaf) == root)
-                               __pcie_aspm_config_link(leaf, state);
-               }
+       while (link) {
+               pcie_config_aspm_link(link, policy_to_aspm_state(link));
+               link = link->parent;
        }
 }
 
-/*
- * pcie_aspm_configure_link_state: enable/disable PCI express link state
- * @pdev: the root port or switch downstream port
- */
-static void pcie_aspm_configure_link_state(struct pcie_link_state *link,
-                                          u32 state)
-{
-       down_read(&pci_bus_sem);
-       mutex_lock(&aspm_lock);
-       __pcie_aspm_configure_link_state(link, state);
-       mutex_unlock(&aspm_lock);
-       up_read(&pci_bus_sem);
-}
-
 static void free_link_state(struct pcie_link_state *link)
 {
        link->pdev->link_state = NULL;
@@ -554,39 +481,42 @@ static void free_link_state(struct pcie_link_state *link)
 
 static int pcie_aspm_sanity_check(struct pci_dev *pdev)
 {
-       struct pci_dev *child_dev;
-       int child_pos;
+       struct pci_dev *child;
        u32 reg32;
 
        /*
-        * Some functions in a slot might not all be PCIE functions, very
-        * strange. Disable ASPM for the whole slot
+        * Some functions in a slot might not all be PCIe functions,
+        * very strange. Disable ASPM for the whole slot
         */
-       list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
-               child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP);
-               if (!child_pos)
+       list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
+               if (!pci_is_pcie(child))
                        return -EINVAL;
 
                /*
+                * If ASPM is disabled then we're not going to change
+                * the BIOS state. It's safe to continue even if it's a
+                * pre-1.1 device
+                */
+
+               if (aspm_disabled)
+                       continue;
+
+               /*
                 * Disable ASPM for pre-1.1 PCIe device, we follow MS to use
                 * RBER bit to determine if a function is 1.1 version device
                 */
-               pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP,
-                       &reg32);
+               pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
                if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
-                       dev_printk(KERN_INFO, &child_dev->dev, "disabling ASPM"
-                               " on pre-1.1 PCIe device.  You can enable it"
-                               " with 'pcie_aspm=force'\n");
+                       dev_info(&child->dev, "disabling ASPM on pre-1.1 PCIe device.  You can enable it with 'pcie_aspm=force'\n");
                        return -EINVAL;
                }
        }
        return 0;
 }
 
-static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev)
+static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
 {
        struct pcie_link_state *link;
-       int blacklist = !!pcie_aspm_sanity_check(pdev);
 
        link = kzalloc(sizeof(*link), GFP_KERNEL);
        if (!link)
@@ -595,8 +525,7 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev)
        INIT_LIST_HEAD(&link->children);
        INIT_LIST_HEAD(&link->link);
        link->pdev = pdev;
-       link->has_switch = pcie_aspm_downstream_has_switch(link);
-       if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
                struct pcie_link_state *parent;
                parent = pdev->bus->parent->self->link_state;
                if (!parent) {
@@ -606,16 +535,14 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev)
                link->parent = parent;
                list_add(&link->link, &parent->children);
        }
-       list_add(&link->sibling, &link_list);
+       /* Setup a pointer to the root port link */
+       if (!link->parent)
+               link->root = link;
+       else
+               link->root = link->parent->root;
 
+       list_add(&link->sibling, &link_list);
        pdev->link_state = link;
-
-       /* Check ASPM capability */
-       pcie_aspm_cap_init(link, blacklist);
-
-       /* Check Clock PM capability */
-       pcie_clkpm_cap_init(link, blacklist);
-
        return link;
 }
 
@@ -626,17 +553,20 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev)
  */
 void pcie_aspm_init_link_state(struct pci_dev *pdev)
 {
-       u32 state;
        struct pcie_link_state *link;
+       int blacklist = !!pcie_aspm_sanity_check(pdev);
+
+       if (!aspm_support_enabled)
+               return;
 
-       if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
+       if (!pci_is_pcie(pdev) || pdev->link_state)
                return;
-       if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-           pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+       if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+           pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
                return;
 
        /* VIA has a strange chipset, root port is under a bridge */
-       if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT &&
+       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT &&
            pdev->bus->self)
                return;
 
@@ -645,47 +575,73 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
                goto out;
 
        mutex_lock(&aspm_lock);
-       link = pcie_aspm_setup_link_state(pdev);
+       link = alloc_pcie_link_state(pdev);
        if (!link)
                goto unlock;
        /*
-        * Setup initial ASPM state
-        *
-        * 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.
+        * Setup initial ASPM state. Note that we need to configure
+        * upstream links also because capable state of them can be
+        * update through pcie_aspm_cap_init().
         */
-       if (link->has_switch) {
-               state = pcie_aspm_check_state(link, link->aspm_default);
-               __pcie_aspm_config_link(link, state);
-       } else {
-               state = policy_to_aspm_state(link);
-               __pcie_aspm_configure_link_state(link, state);
-       }
+       pcie_aspm_cap_init(link, blacklist);
 
        /* Setup initial Clock PM state */
-       state = (link->clkpm_capable) ? policy_to_clkpm_state(link) : 0;
-       pcie_set_clkpm(link, state);
+       pcie_clkpm_cap_init(link, blacklist);
+
+       /*
+        * At this stage drivers haven't had an opportunity to change the
+        * link policy setting. Enabling ASPM on broken hardware can cripple
+        * it even before the driver has had a chance to disable ASPM, so
+        * default to a safe level right now. If we're enabling ASPM beyond
+        * the BIOS's expectation, we'll do so once pci_enable_device() is
+        * called.
+        */
+       if (aspm_policy != POLICY_POWERSAVE) {
+               pcie_config_aspm_path(link);
+               pcie_set_clkpm(link, policy_to_clkpm_state(link));
+       }
+
 unlock:
        mutex_unlock(&aspm_lock);
 out:
        up_read(&pci_bus_sem);
 }
 
+/* Recheck latencies and update aspm_capable for links under the root */
+static void pcie_update_aspm_capable(struct pcie_link_state *root)
+{
+       struct pcie_link_state *link;
+       BUG_ON(root->parent);
+       list_for_each_entry(link, &link_list, sibling) {
+               if (link->root != 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)
+                       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))
+                               continue;
+                       pcie_aspm_check_latency(child);
+               }
+       }
+}
+
 /* @pdev: the endpoint device */
 void pcie_aspm_exit_link_state(struct pci_dev *pdev)
 {
        struct pci_dev *parent = pdev->bus->self;
-       struct pcie_link_state *link_state = parent->link_state;
+       struct pcie_link_state *link, *root, *parent_link;
 
-       if (aspm_disabled || !pdev->is_pcie || !parent || !link_state)
-               return;
-       if (parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-               parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+       if (!parent || !parent->link_state)
                return;
+
        down_read(&pci_bus_sem);
        mutex_lock(&aspm_lock);
-
        /*
         * All PCIe functions are in one slot, remove one function will remove
         * the whole slot, so just wait until we are the last function left.
@@ -693,13 +649,22 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
        if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices))
                goto out;
 
+       link = parent->link_state;
+       root = link->root;
+       parent_link = link->parent;
+
        /* All functions are removed, so just disable ASPM for the link */
-       __pcie_aspm_config_one_dev(parent, 0);
-       list_del(&link_state->sibling);
-       list_del(&link_state->link);
+       pcie_config_aspm_link(link, 0);
+       list_del(&link->sibling);
+       list_del(&link->link);
        /* Clock PM is for endpoint device */
+       free_link_state(link);
 
-       free_link_state(link_state);
+       /* Recheck latencies and configure upstream links */
+       if (parent_link) {
+               pcie_update_aspm_capable(root);
+               pcie_config_aspm_path(parent_link);
+       }
 out:
        mutex_unlock(&aspm_lock);
        up_read(&pci_bus_sem);
@@ -708,56 +673,125 @@ out:
 /* @pdev: the root port or switch downstream port */
 void pcie_aspm_pm_state_change(struct pci_dev *pdev)
 {
-       struct pcie_link_state *link_state = pdev->link_state;
+       struct pcie_link_state *link = pdev->link_state;
 
-       if (aspm_disabled || !pdev->is_pcie || !pdev->link_state)
+       if (aspm_disabled || !pci_is_pcie(pdev) || !link)
                return;
-       if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-               pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+       if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+           (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
                return;
        /*
-        * devices changed PM state, we should recheck if latency meets all
-        * functions' requirement
+        * Devices changed PM state, we should recheck if latency
+        * meets all functions' requirement
         */
-       pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled);
+       down_read(&pci_bus_sem);
+       mutex_lock(&aspm_lock);
+       pcie_update_aspm_capable(link->root);
+       pcie_config_aspm_path(link);
+       mutex_unlock(&aspm_lock);
+       up_read(&pci_bus_sem);
+}
+
+void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
+{
+       struct pcie_link_state *link = pdev->link_state;
+
+       if (aspm_disabled || !pci_is_pcie(pdev) || !link)
+               return;
+
+       if (aspm_policy != POLICY_POWERSAVE)
+               return;
+
+       if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+           (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+               return;
+
+       down_read(&pci_bus_sem);
+       mutex_lock(&aspm_lock);
+       pcie_config_aspm_path(link);
+       pcie_set_clkpm(link, policy_to_clkpm_state(link));
+       mutex_unlock(&aspm_lock);
+       up_read(&pci_bus_sem);
 }
 
 /*
  * pci_disable_link_state - disable pci device's link state, so the link will
  * never enter specific states
  */
-void pci_disable_link_state(struct pci_dev *pdev, int state)
+static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
+                                    bool force)
 {
        struct pci_dev *parent = pdev->bus->self;
-       struct pcie_link_state *link_state;
+       struct pcie_link_state *link;
 
-       if (aspm_disabled || !pdev->is_pcie)
+       if (aspm_disabled && !force)
                return;
-       if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
-           pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
+
+       if (!pci_is_pcie(pdev))
+               return;
+
+       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+           pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
                parent = pdev;
        if (!parent || !parent->link_state)
                return;
 
-       down_read(&pci_bus_sem);
+       if (sem)
+               down_read(&pci_bus_sem);
        mutex_lock(&aspm_lock);
-       link_state = parent->link_state;
-       link_state->aspm_support &= ~state;
-       __pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled);
+       link = parent->link_state;
+       if (state & PCIE_LINK_STATE_L0S)
+               link->aspm_disable |= ASPM_STATE_L0S;
+       if (state & PCIE_LINK_STATE_L1)
+               link->aspm_disable |= ASPM_STATE_L1;
+       pcie_config_aspm_link(link, policy_to_aspm_state(link));
+
        if (state & PCIE_LINK_STATE_CLKPM) {
-               link_state->clkpm_capable = 0;
-               pcie_set_clkpm(link_state, 0);
+               link->clkpm_capable = 0;
+               pcie_set_clkpm(link, 0);
        }
        mutex_unlock(&aspm_lock);
-       up_read(&pci_bus_sem);
+       if (sem)
+               up_read(&pci_bus_sem);
+}
+
+void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
+{
+       __pci_disable_link_state(pdev, state, false, false);
+}
+EXPORT_SYMBOL(pci_disable_link_state_locked);
+
+void pci_disable_link_state(struct pci_dev *pdev, int state)
+{
+       __pci_disable_link_state(pdev, state, true, false);
 }
 EXPORT_SYMBOL(pci_disable_link_state);
 
+void pcie_clear_aspm(struct pci_bus *bus)
+{
+       struct pci_dev *child;
+
+       if (aspm_force)
+               return;
+
+       /*
+        * Clear any ASPM setup that the firmware has carried out on this bus
+        */
+       list_for_each_entry(child, &bus->devices, bus_list) {
+               __pci_disable_link_state(child, PCIE_LINK_STATE_L0S |
+                                        PCIE_LINK_STATE_L1 |
+                                        PCIE_LINK_STATE_CLKPM,
+                                        false, true);
+       }
+}
+
 static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
 {
        int i;
-       struct pcie_link_state *link_state;
+       struct pcie_link_state *link;
 
+       if (aspm_disabled)
+               return -EPERM;
        for (i = 0; i < ARRAY_SIZE(policy_str); i++)
                if (!strncmp(val, policy_str[i], strlen(policy_str[i])))
                        break;
@@ -769,10 +803,9 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
        down_read(&pci_bus_sem);
        mutex_lock(&aspm_lock);
        aspm_policy = i;
-       list_for_each_entry(link_state, &link_list, sibling) {
-               __pcie_aspm_configure_link_state(link_state,
-                       policy_to_aspm_state(link_state));
-               pcie_set_clkpm(link_state, policy_to_clkpm_state(link_state));
+       list_for_each_entry(link, &link_list, sibling) {
+               pcie_config_aspm_link(link, policy_to_aspm_state(link));
+               pcie_set_clkpm(link, policy_to_clkpm_state(link));
        }
        mutex_unlock(&aspm_lock);
        up_read(&pci_bus_sem);
@@ -810,18 +843,30 @@ static ssize_t link_state_store(struct device *dev,
                size_t n)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
-       int state;
+       struct pcie_link_state *link, *root = pdev->link_state->root;
+       u32 val = buf[0] - '0', state = 0;
 
-       if (n < 1)
+       if (aspm_disabled)
+               return -EPERM;
+       if (n < 1 || val > 3)
                return -EINVAL;
-       state = buf[0]-'0';
-       if (state >= 0 && state <= 3) {
-               /* setup link aspm state */
-               pcie_aspm_configure_link_state(pdev->link_state, state);
-               return n;
-       }
 
-       return -EINVAL;
+       /* Convert requested state to ASPM state */
+       if (val & PCIE_LINK_STATE_L0S)
+               state |= ASPM_STATE_L0S;
+       if (val & PCIE_LINK_STATE_L1)
+               state |= ASPM_STATE_L1;
+
+       down_read(&pci_bus_sem);
+       mutex_lock(&aspm_lock);
+       list_for_each_entry(link, &link_list, sibling) {
+               if (link->root != root)
+                       continue;
+               pcie_config_aspm_link(link, state);
+       }
+       mutex_unlock(&aspm_lock);
+       up_read(&pci_bus_sem);
+       return n;
 }
 
 static ssize_t clk_ctl_show(struct device *dev,
@@ -863,8 +908,9 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
 {
        struct pcie_link_state *link_state = pdev->link_state;
 
-       if (!pdev->is_pcie || (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-               pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+       if (!pci_is_pcie(pdev) ||
+           (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+            pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
                return;
 
        if (link_state->aspm_support)
@@ -879,8 +925,9 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
 {
        struct pcie_link_state *link_state = pdev->link_state;
 
-       if (!pdev->is_pcie || (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-               pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+       if (!pci_is_pcie(pdev) ||
+           (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+            pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
                return;
 
        if (link_state->aspm_support)
@@ -895,11 +942,13 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
 static int __init pcie_aspm_disable(char *str)
 {
        if (!strcmp(str, "off")) {
+               aspm_policy = POLICY_DEFAULT;
                aspm_disabled = 1;
+               aspm_support_enabled = false;
                printk(KERN_INFO "PCIe ASPM is disabled\n");
        } else if (!strcmp(str, "force")) {
                aspm_force = 1;
-               printk(KERN_INFO "PCIe ASPM is forcedly enabled\n");
+               printk(KERN_INFO "PCIe ASPM is forcibly enabled\n");
        }
        return 1;
 }
@@ -908,8 +957,16 @@ __setup("pcie_aspm=", pcie_aspm_disable);
 
 void pcie_no_aspm(void)
 {
-       if (!aspm_force)
+       /*
+        * Disabling ASPM is intended to prevent the kernel from modifying
+        * existing hardware state, not to clear existing state. To that end:
+        * (a) set policy to POLICY_DEFAULT in order to avoid changing state
+        * (b) prevent userspace from changing policy
+        */
+       if (!aspm_force) {
+               aspm_policy = POLICY_DEFAULT;
                aspm_disabled = 1;
+       }
 }
 
 /**
@@ -924,3 +981,8 @@ int pcie_aspm_enabled(void)
 }
 EXPORT_SYMBOL(pcie_aspm_enabled);
 
+bool pcie_aspm_support_enabled(void)
+{
+       return aspm_support_enabled;
+}
+EXPORT_SYMBOL(pcie_aspm_support_enabled);