diff options
-rw-r--r-- | drivers/acpi/pci_root.c | 11 | ||||
-rw-r--r-- | drivers/pci/pci.c | 6 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 33 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 5 | ||||
-rw-r--r-- | include/linux/pci-aspm.h | 4 | ||||
-rw-r--r-- | include/linux/pci.h | 7 |
6 files changed, 55 insertions, 11 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 85249395623b..f911a2f8cc34 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -32,6 +32,7 @@ #include <linux/pm_runtime.h> #include <linux/pci.h> #include <linux/pci-acpi.h> +#include <linux/pci-aspm.h> #include <linux/acpi.h> #include <linux/slab.h> #include <acpi/acpi_bus.h> @@ -564,7 +565,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; - if (pcie_aspm_enabled()) + if (pcie_aspm_support_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (pci_msi_enabled()) @@ -591,12 +592,16 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) status = acpi_pci_osc_control_set(device->handle, &flags, OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { dev_info(root->bus->bridge, "ACPI _OSC control (0x%02x) granted\n", flags); - else + } else { dev_dbg(root->bus->bridge, "ACPI _OSC request failed (code %d)\n", status); + printk(KERN_INFO "Unable to assume _OSC PCIe control. " + "Disabling ASPM\n"); + pcie_no_aspm(); + } } pci_acpi_add_bus_pm_notifier(device, root->bus); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b714d787bddd..2472e7177b4b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -740,6 +740,12 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!__pci_complete_power_transition(dev, state)) error = 0; + /* + * When aspm_policy is "powersave" this call ensures + * that ASPM is configured. + */ + if (!error && dev->bus->self) + pcie_aspm_powersave_config_link(dev->bus->self); return error; } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3188cd96b338..eee09f756ec9 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -69,6 +69,7 @@ struct pcie_link_state { }; static int aspm_disabled, aspm_force, aspm_clear_state; +static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -707,6 +708,28 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) 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 ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (pdev->pcie_type != 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 @@ -747,6 +770,8 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) int i; 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; @@ -801,6 +826,8 @@ static ssize_t link_state_store(struct device *dev, struct pcie_link_state *link, *root = pdev->link_state->root; u32 val = buf[0] - '0', state = 0; + if (aspm_disabled) + return -EPERM; if (n < 1 || val > 3) return -EINVAL; @@ -896,6 +923,7 @@ static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { aspm_disabled = 1; + aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; @@ -930,3 +958,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); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 5130d0d22390..595654a1a6a6 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/pcieport_if.h> #include <linux/aer.h> -#include <linux/pci-aspm.h> #include "../pci.h" #include "portdrv.h" @@ -356,10 +355,8 @@ int pcie_port_device_register(struct pci_dev *dev) /* Get and check PCI Express port services */ capabilities = get_port_device_capability(dev); - if (!capabilities) { - pcie_no_aspm(); + if (!capabilities) return 0; - } pci_set_master(dev); /* diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h index ce6810512c66..67cb3ae38016 100644 --- a/include/linux/pci-aspm.h +++ b/include/linux/pci-aspm.h @@ -26,6 +26,7 @@ extern void pcie_aspm_init_link_state(struct pci_dev *pdev); extern void pcie_aspm_exit_link_state(struct pci_dev *pdev); extern void pcie_aspm_pm_state_change(struct pci_dev *pdev); +extern void pcie_aspm_powersave_config_link(struct pci_dev *pdev); extern void pci_disable_link_state(struct pci_dev *pdev, int state); extern void pcie_clear_aspm(void); extern void pcie_no_aspm(void); @@ -39,6 +40,9 @@ static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { } +static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) +{ +} static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { } diff --git a/include/linux/pci.h b/include/linux/pci.h index 16c9f2e61977..96f70d7e058d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1002,12 +1002,11 @@ extern bool pcie_ports_auto; #endif #ifndef CONFIG_PCIEASPM -static inline int pcie_aspm_enabled(void) -{ - return 0; -} +static inline int pcie_aspm_enabled(void) { return 0; } +static inline bool pcie_aspm_support_enabled(void) { return false; } #else extern int pcie_aspm_enabled(void); +extern bool pcie_aspm_support_enabled(void); #endif #ifdef CONFIG_PCIEAER |