Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: Disable ASPM if BIOS asks us to

We currently refuse to touch the ASPM registers if the BIOS tells us that
ASPM isn't supported. This can cause problems if the BIOS has (for any
reason) enabled ASPM on some devices anyway. Change the code such that we
explicitly clear ASPM if the FADT indicates that ASPM isn't supported,
and make sure we tidy up appropriately on device removal in order to deal
with the hotplug case. If ASPM is disabled because the BIOS doesn't hand
over control then we won't touch the registers.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by

Matthew Garrett and committed by
Jesse Barnes
2f671e2d 8d805286

+22 -5
+1
drivers/pci/pci-acpi.c
··· 399 399 400 400 if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { 401 401 printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); 402 + pcie_clear_aspm(); 402 403 pcie_no_aspm(); 403 404 } 404 405
+17 -4
drivers/pci/pcie/aspm.c
··· 68 68 struct aspm_latency acceptable[8]; 69 69 }; 70 70 71 - static int aspm_disabled, aspm_force; 71 + static int aspm_disabled, aspm_force, aspm_clear_state; 72 72 static DEFINE_MUTEX(aspm_lock); 73 73 static LIST_HEAD(link_list); 74 74 ··· 139 139 { 140 140 /* Don't enable Clock PM if the link is not Clock PM capable */ 141 141 if (!link->clkpm_capable && enable) 142 - return; 142 + enable = 0; 143 143 /* Need nothing if the specified equals to current state */ 144 144 if (link->clkpm_enabled == enable) 145 145 return; ··· 498 498 struct pci_dev *child; 499 499 int pos; 500 500 u32 reg32; 501 + 502 + if (aspm_clear_state) 503 + return -EINVAL; 504 + 501 505 /* 502 506 * Some functions in a slot might not all be PCIe functions, 503 507 * very strange. Disable ASPM for the whole slot ··· 567 563 struct pcie_link_state *link; 568 564 int blacklist = !!pcie_aspm_sanity_check(pdev); 569 565 570 - if (aspm_disabled || !pci_is_pcie(pdev) || pdev->link_state) 566 + if (!pci_is_pcie(pdev) || pdev->link_state) 571 567 return; 572 568 if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && 573 569 pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) 570 + return; 571 + 572 + if (aspm_disabled && !aspm_clear_state) 574 573 return; 575 574 576 575 /* VIA has a strange chipset, root port is under a bridge */ ··· 648 641 struct pci_dev *parent = pdev->bus->self; 649 642 struct pcie_link_state *link, *root, *parent_link; 650 643 651 - if (aspm_disabled || !pci_is_pcie(pdev) || 644 + if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) || 652 645 !parent || !parent->link_state) 653 646 return; 654 647 if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && ··· 905 898 } 906 899 907 900 __setup("pcie_aspm=", pcie_aspm_disable); 901 + 902 + void pcie_clear_aspm(void) 903 + { 904 + if (!aspm_force) 905 + aspm_clear_state = 1; 906 + } 908 907 909 908 void pcie_no_aspm(void) 910 909 {
+4 -1
include/linux/pci-aspm.h
··· 27 27 extern void pcie_aspm_exit_link_state(struct pci_dev *pdev); 28 28 extern void pcie_aspm_pm_state_change(struct pci_dev *pdev); 29 29 extern void pci_disable_link_state(struct pci_dev *pdev, int state); 30 + extern void pcie_clear_aspm(void); 30 31 extern void pcie_no_aspm(void); 31 32 #else 32 33 static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) ··· 42 41 static inline void pci_disable_link_state(struct pci_dev *pdev, int state) 43 42 { 44 43 } 45 - 44 + static inline void pcie_clear_aspm(void) 45 + { 46 + } 46 47 static inline void pcie_no_aspm(void) 47 48 { 48 49 }