Platform firmware may not be able to access config space to program ASPM. For instance, devices behind Intel VMD are not configured by the BIOS. So add a helper to let drivers have an option to enable ASPM. Signed-off-by: Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx> --- drivers/pci/pcie/aspm.c | 73 ++++++++++++++++++++++++++++++----------- include/linux/pci.h | 7 ++++ 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 253c30cc1967..b5ac6a99e016 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1080,6 +1080,58 @@ static struct pcie_link_state *pcie_aspm_get_link(struct pci_dev *pdev) return bridge->link_state; } +static u32 pcie_aspm_convert_state(int state) +{ + u32 aspm = 0; + + if (state & PCIE_LINK_STATE_L0S) + aspm |= ASPM_STATE_L0S; + if (state & PCIE_LINK_STATE_L1) + /* L1 PM substates require L1 */ + aspm |= ASPM_STATE_L1 | ASPM_STATE_L1SS; + if (state & PCIE_LINK_STATE_L1_1) + aspm |= ASPM_STATE_L1_1; + if (state & PCIE_LINK_STATE_L1_2) + aspm |= ASPM_STATE_L1_2; + if (state & PCIE_LINK_STATE_L1_1_PCIPM) + aspm |= ASPM_STATE_L1_1_PCIPM; + if (state & PCIE_LINK_STATE_L1_2_PCIPM) + aspm |= ASPM_STATE_L1_2_PCIPM; + + return aspm; +} + +static void pci_set_link_state(struct pci_dev *pdev, bool enable, int state) +{ + struct pcie_link_state *link = pcie_aspm_get_link(pdev); + + mutex_lock(&aspm_lock); + if (enable) + link->aspm_default = pcie_aspm_convert_state(state); + else + link->aspm_disable = pcie_aspm_convert_state(state); + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + + if (state & PCIE_LINK_STATE_CLKPM) { + link->clkpm_default = enable; + link->clkpm_disable = !enable; + } + pcie_set_clkpm(link, policy_to_clkpm_state(link)); + mutex_unlock(&aspm_lock); +} + +int pci_enable_link_state(struct pci_dev *pdev, int state) +{ + struct pcie_link_state *link = pcie_aspm_get_link(pdev); + + if (!link) + return -EINVAL; + + pci_set_link_state(pdev, true, state); + return 0; +} +EXPORT_SYMBOL(pci_enable_link_state); + static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) { struct pcie_link_state *link = pcie_aspm_get_link(pdev); @@ -1101,26 +1153,7 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) if (sem) down_read(&pci_bus_sem); - mutex_lock(&aspm_lock); - if (state & PCIE_LINK_STATE_L0S) - link->aspm_disable |= ASPM_STATE_L0S; - if (state & PCIE_LINK_STATE_L1) - /* L1 PM substates require L1 */ - link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS; - if (state & PCIE_LINK_STATE_L1_1) - link->aspm_disable |= ASPM_STATE_L1_1; - if (state & PCIE_LINK_STATE_L1_2) - link->aspm_disable |= ASPM_STATE_L1_2; - if (state & PCIE_LINK_STATE_L1_1_PCIPM) - link->aspm_disable |= ASPM_STATE_L1_1_PCIPM; - if (state & PCIE_LINK_STATE_L1_2_PCIPM) - link->aspm_disable |= ASPM_STATE_L1_2_PCIPM; - pcie_config_aspm_link(link, policy_to_aspm_state(link)); - - if (state & PCIE_LINK_STATE_CLKPM) - link->clkpm_disable = 1; - pcie_set_clkpm(link, policy_to_clkpm_state(link)); - mutex_unlock(&aspm_lock); + pci_set_link_state(pdev, false, state); if (sem) up_read(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 835530605c0d..a6a7830ec258 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1568,14 +1568,21 @@ extern bool pcie_ports_native; #define PCIE_LINK_STATE_L1_2 BIT(4) #define PCIE_LINK_STATE_L1_1_PCIPM BIT(5) #define PCIE_LINK_STATE_L1_2_PCIPM BIT(6) +#define PCIE_LINK_STATE_ALL (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |\ + PCIE_LINK_STATE_CLKPM | PCIE_LINK_STATE_L1_1 |\ + PCIE_LINK_STATE_L1_2 | PCIE_LINK_STATE_L1_1_PCIPM |\ + PCIE_LINK_STATE_L1_2_PCIPM) #ifdef CONFIG_PCIEASPM +int pci_enable_link_state(struct pci_dev *pdev, int state); int pci_disable_link_state(struct pci_dev *pdev, int state); int pci_disable_link_state_locked(struct pci_dev *pdev, int state); void pcie_no_aspm(void); bool pcie_aspm_support_enabled(void); bool pcie_aspm_enabled(struct pci_dev *pdev); #else +static inline int pci_enable_link_state(struct pci_dev *pdev, int state) +{ return 0; } static inline int pci_disable_link_state(struct pci_dev *pdev, int state) { return 0; } static inline int pci_disable_link_state_locked(struct pci_dev *pdev, int state) -- 2.17.1