From d5ef8cf951dd135e76deb32d1c315fe95d1f4c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 11 Jul 2025 16:28:26 +0300 Subject: [PATCH 3/3] PCI/ASPM: Add pci_enable_link_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_disable_link_state() lacks a symmetric pair. Some drivers want to disable ASPM during certain phases of their operation but then re-enable it later on. If pci_disable_link_state() is made for the device, there is currently no way to re-enable the states that were disabled. Add pci_enable_link_state() to remove ASPM states from the state disable mask. Signed-off-by: Ilpo Järvinen --- drivers/pci/pcie/aspm.c | 43 +++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 44 insertions(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 5721ebfdea71..348bd79f049f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1432,6 +1432,49 @@ int pci_disable_link_state(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state); +/** + * pci_enable_link_state - Re-enable device's link state + * @pdev: PCI device + * @state: ASPM link states to re-enable + * + * Enable device's link state that were previously disable so the link is + * allowed to enter the specific states. Note that if the BIOS didn't grant + * ASPM control to the OS, this does nothing because we can't touch the + * LNKCTL register. + * + * Return: 0 or a negative errno. + */ +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; + /* + * A driver requested that ASPM be enabled on this device, but + * if we don't have permission to manage ASPM (e.g., on ACPI + * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and + * the _OSC method), we can't honor that request. + */ + if (aspm_disabled) { + pci_warn(pdev, "can't enable ASPM; OS doesn't have ASPM control\n"); + return -EPERM; + } + + mutex_lock(&aspm_lock); + /* Use the disable mask variant because it relates to aspm_disable */ + link->aspm_disable &= ~pci_calc_aspm_disable_mask(state); + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + + if (state & PCIE_LINK_STATE_CLKPM) + link->clkpm_disable = 0; + pcie_set_clkpm(link, policy_to_clkpm_state(link)); + mutex_unlock(&aspm_lock); + + return 0; +} +EXPORT_SYMBOL(pci_enable_link_state); + static int __pci_set_default_link_state(struct pci_dev *pdev, int state, bool locked) { struct pcie_link_state *link = pcie_aspm_get_link(pdev); diff --git a/include/linux/pci.h b/include/linux/pci.h index b8f60864ef81..d566d86e6368 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1826,6 +1826,7 @@ static inline int pcie_set_target_speed(struct pci_dev *port, #ifdef CONFIG_PCIEASPM int pci_disable_link_state(struct pci_dev *pdev, int state); int pci_disable_link_state_locked(struct pci_dev *pdev, int state); +int pci_enable_link_state(struct pci_dev *pdev, int state); int pci_set_default_link_state(struct pci_dev *pdev, int state); int pci_set_default_link_state_locked(struct pci_dev *pdev, int state); void pcie_no_aspm(void); -- 2.39.5