aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2011-11-10 16:38:33 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2012-01-06 15:10:26 -0500
commit10f6dc7eede9a8895626e9c1b4f2c3b75fbf2850 (patch)
treef99d7357cad3d11aad7870d5d528d8602ab0c65d
parentcfa4d8cc56853ec945956d182ecb4c99102b110a (diff)
PCI: Rework ASPM disable code
Right now we forcibly clear ASPM state on all devices if the BIOS indicates that the feature isn't supported. Based on the Microsoft presentation "PCI Express In Depth for Windows Vista and Beyond", I'm starting to think that this may be an error. The implication is that unless the platform grants full control via _OSC, Windows will not touch any PCIe features - including ASPM. In that case clearing ASPM state would be an error unless the platform has granted us that control. This patch reworks the ASPM disabling code such that the actual clearing of state is triggered by a successful handoff of PCIe control to the OS. The general ASPM code undergoes some changes in order to ensure that the ability to clear the bits isn't overridden by ASPM having already been disabled. Further, this theoretically now allows for situations where only a subset of PCIe roots hand over control, leaving the others in the BIOS state. It's difficult to know for sure that this is the right thing to do - there's zero public documentation on the interaction between all of these components. But enough vendors enable ASPM on platforms and then set this bit that it seems likely that they're expecting the OS to leave them alone. Measured to save around 5W on an idle Thinkpad X220. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/acpi/pci_root.c7
-rw-r--r--drivers/pci/pci-acpi.c1
-rw-r--r--drivers/pci/pcie/aspm.c58
-rw-r--r--include/linux/pci-aspm.h4
4 files changed, 46 insertions, 24 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 2672c798272f..7aff6312ce7c 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -596,6 +596,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
596 if (ACPI_SUCCESS(status)) { 596 if (ACPI_SUCCESS(status)) {
597 dev_info(root->bus->bridge, 597 dev_info(root->bus->bridge,
598 "ACPI _OSC control (0x%02x) granted\n", flags); 598 "ACPI _OSC control (0x%02x) granted\n", flags);
599 if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
600 /*
601 * We have ASPM control, but the FADT indicates
602 * that it's unsupported. Clear it.
603 */
604 pcie_clear_aspm(root->bus);
605 }
599 } else { 606 } else {
600 dev_info(root->bus->bridge, 607 dev_info(root->bus->bridge,
601 "ACPI _OSC request failed (%s), " 608 "ACPI _OSC request failed (%s), "
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 4ecb6408b0d6..c8e75851a314 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -395,7 +395,6 @@ static int __init acpi_pci_init(void)
395 395
396 if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { 396 if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
397 printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); 397 printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n");
398 pcie_clear_aspm();
399 pcie_no_aspm(); 398 pcie_no_aspm();
400 } 399 }
401 400
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index cbfbab18be91..1cfbf228fbb1 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -68,7 +68,7 @@ struct pcie_link_state {
68 struct aspm_latency acceptable[8]; 68 struct aspm_latency acceptable[8];
69}; 69};
70 70
71static int aspm_disabled, aspm_force, aspm_clear_state; 71static int aspm_disabled, aspm_force;
72static bool aspm_support_enabled = true; 72static bool aspm_support_enabled = true;
73static DEFINE_MUTEX(aspm_lock); 73static DEFINE_MUTEX(aspm_lock);
74static LIST_HEAD(link_list); 74static LIST_HEAD(link_list);
@@ -500,9 +500,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
500 int pos; 500 int pos;
501 u32 reg32; 501 u32 reg32;
502 502
503 if (aspm_clear_state)
504 return -EINVAL;
505
506 /* 503 /*
507 * Some functions in a slot might not all be PCIe functions, 504 * Some functions in a slot might not all be PCIe functions,
508 * very strange. Disable ASPM for the whole slot 505 * very strange. Disable ASPM for the whole slot
@@ -574,9 +571,6 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
574 pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) 571 pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
575 return; 572 return;
576 573
577 if (aspm_disabled && !aspm_clear_state)
578 return;
579
580 /* VIA has a strange chipset, root port is under a bridge */ 574 /* VIA has a strange chipset, root port is under a bridge */
581 if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && 575 if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT &&
582 pdev->bus->self) 576 pdev->bus->self)
@@ -608,7 +602,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
608 * the BIOS's expectation, we'll do so once pci_enable_device() is 602 * the BIOS's expectation, we'll do so once pci_enable_device() is
609 * called. 603 * called.
610 */ 604 */
611 if (aspm_policy != POLICY_POWERSAVE || aspm_clear_state) { 605 if (aspm_policy != POLICY_POWERSAVE) {
612 pcie_config_aspm_path(link); 606 pcie_config_aspm_path(link);
613 pcie_set_clkpm(link, policy_to_clkpm_state(link)); 607 pcie_set_clkpm(link, policy_to_clkpm_state(link));
614 } 608 }
@@ -649,8 +643,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
649 struct pci_dev *parent = pdev->bus->self; 643 struct pci_dev *parent = pdev->bus->self;
650 struct pcie_link_state *link, *root, *parent_link; 644 struct pcie_link_state *link, *root, *parent_link;
651 645
652 if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) || 646 if (!pci_is_pcie(pdev) || !parent || !parent->link_state)
653 !parent || !parent->link_state)
654 return; 647 return;
655 if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && 648 if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
656 (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) 649 (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
@@ -734,13 +727,18 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
734 * pci_disable_link_state - disable pci device's link state, so the link will 727 * pci_disable_link_state - disable pci device's link state, so the link will
735 * never enter specific states 728 * never enter specific states
736 */ 729 */
737static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) 730static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
731 bool force)
738{ 732{
739 struct pci_dev *parent = pdev->bus->self; 733 struct pci_dev *parent = pdev->bus->self;
740 struct pcie_link_state *link; 734 struct pcie_link_state *link;
741 735
742 if (aspm_disabled || !pci_is_pcie(pdev)) 736 if (aspm_disabled && !force)
737 return;
738
739 if (!pci_is_pcie(pdev))
743 return; 740 return;
741
744 if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || 742 if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
745 pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) 743 pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
746 parent = pdev; 744 parent = pdev;
@@ -768,16 +766,31 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
768 766
769void pci_disable_link_state_locked(struct pci_dev *pdev, int state) 767void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
770{ 768{
771 __pci_disable_link_state(pdev, state, false); 769 __pci_disable_link_state(pdev, state, false, false);
772} 770}
773EXPORT_SYMBOL(pci_disable_link_state_locked); 771EXPORT_SYMBOL(pci_disable_link_state_locked);
774 772
775void pci_disable_link_state(struct pci_dev *pdev, int state) 773void pci_disable_link_state(struct pci_dev *pdev, int state)
776{ 774{
777 __pci_disable_link_state(pdev, state, true); 775 __pci_disable_link_state(pdev, state, true, false);
778} 776}
779EXPORT_SYMBOL(pci_disable_link_state); 777EXPORT_SYMBOL(pci_disable_link_state);
780 778
779void pcie_clear_aspm(struct pci_bus *bus)
780{
781 struct pci_dev *child;
782
783 /*
784 * Clear any ASPM setup that the firmware has carried out on this bus
785 */
786 list_for_each_entry(child, &bus->devices, bus_list) {
787 __pci_disable_link_state(child, PCIE_LINK_STATE_L0S |
788 PCIE_LINK_STATE_L1 |
789 PCIE_LINK_STATE_CLKPM,
790 false, true);
791 }
792}
793
781static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) 794static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
782{ 795{
783 int i; 796 int i;
@@ -935,6 +948,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
935static int __init pcie_aspm_disable(char *str) 948static int __init pcie_aspm_disable(char *str)
936{ 949{
937 if (!strcmp(str, "off")) { 950 if (!strcmp(str, "off")) {
951 aspm_policy = POLICY_DEFAULT;
938 aspm_disabled = 1; 952 aspm_disabled = 1;
939 aspm_support_enabled = false; 953 aspm_support_enabled = false;
940 printk(KERN_INFO "PCIe ASPM is disabled\n"); 954 printk(KERN_INFO "PCIe ASPM is disabled\n");
@@ -947,16 +961,18 @@ static int __init pcie_aspm_disable(char *str)
947 961
948__setup("pcie_aspm=", pcie_aspm_disable); 962__setup("pcie_aspm=", pcie_aspm_disable);
949 963
950void pcie_clear_aspm(void)
951{
952 if (!aspm_force)
953 aspm_clear_state = 1;
954}
955
956void pcie_no_aspm(void) 964void pcie_no_aspm(void)
957{ 965{
958 if (!aspm_force) 966 /*
967 * Disabling ASPM is intended to prevent the kernel from modifying
968 * existing hardware state, not to clear existing state. To that end:
969 * (a) set policy to POLICY_DEFAULT in order to avoid changing state
970 * (b) prevent userspace from changing policy
971 */
972 if (!aspm_force) {
973 aspm_policy = POLICY_DEFAULT;
959 aspm_disabled = 1; 974 aspm_disabled = 1;
975 }
960} 976}
961 977
962/** 978/**
diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h
index 7cea7b6c1413..c8320144fe79 100644
--- a/include/linux/pci-aspm.h
+++ b/include/linux/pci-aspm.h
@@ -29,7 +29,7 @@ extern void pcie_aspm_pm_state_change(struct pci_dev *pdev);
29extern void pcie_aspm_powersave_config_link(struct pci_dev *pdev); 29extern void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
30extern void pci_disable_link_state(struct pci_dev *pdev, int state); 30extern void pci_disable_link_state(struct pci_dev *pdev, int state);
31extern void pci_disable_link_state_locked(struct pci_dev *pdev, int state); 31extern void pci_disable_link_state_locked(struct pci_dev *pdev, int state);
32extern void pcie_clear_aspm(void); 32extern void pcie_clear_aspm(struct pci_bus *bus);
33extern void pcie_no_aspm(void); 33extern void pcie_no_aspm(void);
34#else 34#else
35static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) 35static inline void pcie_aspm_init_link_state(struct pci_dev *pdev)
@@ -47,7 +47,7 @@ static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
47static inline void pci_disable_link_state(struct pci_dev *pdev, int state) 47static inline void pci_disable_link_state(struct pci_dev *pdev, int state)
48{ 48{
49} 49}
50static inline void pcie_clear_aspm(void) 50static inline void pcie_clear_aspm(struct pci_bus *bus)
51{ 51{
52} 52}
53static inline void pcie_no_aspm(void) 53static inline void pcie_no_aspm(void)