diff options
Diffstat (limited to 'drivers/pci/pcie/aspm.c')
-rw-r--r-- | drivers/pci/pcie/aspm.c | 75 |
1 files changed, 67 insertions, 8 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 71222814c1ec..6892601fc76f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c | |||
@@ -68,7 +68,8 @@ struct pcie_link_state { | |||
68 | struct aspm_latency acceptable[8]; | 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 | static bool aspm_support_enabled = true; | ||
72 | static DEFINE_MUTEX(aspm_lock); | 73 | static DEFINE_MUTEX(aspm_lock); |
73 | static LIST_HEAD(link_list); | 74 | static LIST_HEAD(link_list); |
74 | 75 | ||
@@ -139,7 +140,7 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable) | |||
139 | { | 140 | { |
140 | /* Don't enable Clock PM if the link is not Clock PM capable */ | 141 | /* Don't enable Clock PM if the link is not Clock PM capable */ |
141 | if (!link->clkpm_capable && enable) | 142 | if (!link->clkpm_capable && enable) |
142 | return; | 143 | enable = 0; |
143 | /* Need nothing if the specified equals to current state */ | 144 | /* Need nothing if the specified equals to current state */ |
144 | if (link->clkpm_enabled == enable) | 145 | if (link->clkpm_enabled == enable) |
145 | return; | 146 | return; |
@@ -498,6 +499,10 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) | |||
498 | struct pci_dev *child; | 499 | struct pci_dev *child; |
499 | int pos; | 500 | int pos; |
500 | u32 reg32; | 501 | u32 reg32; |
502 | |||
503 | if (aspm_clear_state) | ||
504 | return -EINVAL; | ||
505 | |||
501 | /* | 506 | /* |
502 | * Some functions in a slot might not all be PCIe functions, | 507 | * Some functions in a slot might not all be PCIe functions, |
503 | * very strange. Disable ASPM for the whole slot | 508 | * very strange. Disable ASPM for the whole slot |
@@ -563,12 +568,15 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) | |||
563 | struct pcie_link_state *link; | 568 | struct pcie_link_state *link; |
564 | int blacklist = !!pcie_aspm_sanity_check(pdev); | 569 | int blacklist = !!pcie_aspm_sanity_check(pdev); |
565 | 570 | ||
566 | if (aspm_disabled || !pci_is_pcie(pdev) || pdev->link_state) | 571 | if (!pci_is_pcie(pdev) || pdev->link_state) |
567 | return; | 572 | return; |
568 | if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && | 573 | if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && |
569 | pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) | 574 | pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) |
570 | return; | 575 | return; |
571 | 576 | ||
577 | if (aspm_disabled && !aspm_clear_state) | ||
578 | return; | ||
579 | |||
572 | /* VIA has a strange chipset, root port is under a bridge */ | 580 | /* VIA has a strange chipset, root port is under a bridge */ |
573 | if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && | 581 | if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && |
574 | pdev->bus->self) | 582 | pdev->bus->self) |
@@ -600,7 +608,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) | |||
600 | * the BIOS's expectation, we'll do so once pci_enable_device() is | 608 | * the BIOS's expectation, we'll do so once pci_enable_device() is |
601 | * called. | 609 | * called. |
602 | */ | 610 | */ |
603 | if (aspm_policy != POLICY_POWERSAVE) { | 611 | if (aspm_policy != POLICY_POWERSAVE || aspm_clear_state) { |
604 | pcie_config_aspm_path(link); | 612 | pcie_config_aspm_path(link); |
605 | pcie_set_clkpm(link, policy_to_clkpm_state(link)); | 613 | pcie_set_clkpm(link, policy_to_clkpm_state(link)); |
606 | } | 614 | } |
@@ -641,7 +649,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) | |||
641 | struct pci_dev *parent = pdev->bus->self; | 649 | struct pci_dev *parent = pdev->bus->self; |
642 | struct pcie_link_state *link, *root, *parent_link; | 650 | struct pcie_link_state *link, *root, *parent_link; |
643 | 651 | ||
644 | if (aspm_disabled || !pci_is_pcie(pdev) || | 652 | if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) || |
645 | !parent || !parent->link_state) | 653 | !parent || !parent->link_state) |
646 | return; | 654 | return; |
647 | if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && | 655 | if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && |
@@ -700,11 +708,33 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) | |||
700 | up_read(&pci_bus_sem); | 708 | up_read(&pci_bus_sem); |
701 | } | 709 | } |
702 | 710 | ||
711 | void pcie_aspm_powersave_config_link(struct pci_dev *pdev) | ||
712 | { | ||
713 | struct pcie_link_state *link = pdev->link_state; | ||
714 | |||
715 | if (aspm_disabled || !pci_is_pcie(pdev) || !link) | ||
716 | return; | ||
717 | |||
718 | if (aspm_policy != POLICY_POWERSAVE) | ||
719 | return; | ||
720 | |||
721 | if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && | ||
722 | (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) | ||
723 | return; | ||
724 | |||
725 | down_read(&pci_bus_sem); | ||
726 | mutex_lock(&aspm_lock); | ||
727 | pcie_config_aspm_path(link); | ||
728 | pcie_set_clkpm(link, policy_to_clkpm_state(link)); | ||
729 | mutex_unlock(&aspm_lock); | ||
730 | up_read(&pci_bus_sem); | ||
731 | } | ||
732 | |||
703 | /* | 733 | /* |
704 | * pci_disable_link_state - disable pci device's link state, so the link will | 734 | * pci_disable_link_state - disable pci device's link state, so the link will |
705 | * never enter specific states | 735 | * never enter specific states |
706 | */ | 736 | */ |
707 | void pci_disable_link_state(struct pci_dev *pdev, int state) | 737 | static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) |
708 | { | 738 | { |
709 | struct pci_dev *parent = pdev->bus->self; | 739 | struct pci_dev *parent = pdev->bus->self; |
710 | struct pcie_link_state *link; | 740 | struct pcie_link_state *link; |
@@ -717,7 +747,8 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) | |||
717 | if (!parent || !parent->link_state) | 747 | if (!parent || !parent->link_state) |
718 | return; | 748 | return; |
719 | 749 | ||
720 | down_read(&pci_bus_sem); | 750 | if (sem) |
751 | down_read(&pci_bus_sem); | ||
721 | mutex_lock(&aspm_lock); | 752 | mutex_lock(&aspm_lock); |
722 | link = parent->link_state; | 753 | link = parent->link_state; |
723 | if (state & PCIE_LINK_STATE_L0S) | 754 | if (state & PCIE_LINK_STATE_L0S) |
@@ -731,7 +762,19 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) | |||
731 | pcie_set_clkpm(link, 0); | 762 | pcie_set_clkpm(link, 0); |
732 | } | 763 | } |
733 | mutex_unlock(&aspm_lock); | 764 | mutex_unlock(&aspm_lock); |
734 | up_read(&pci_bus_sem); | 765 | if (sem) |
766 | up_read(&pci_bus_sem); | ||
767 | } | ||
768 | |||
769 | void pci_disable_link_state_locked(struct pci_dev *pdev, int state) | ||
770 | { | ||
771 | __pci_disable_link_state(pdev, state, false); | ||
772 | } | ||
773 | EXPORT_SYMBOL(pci_disable_link_state_locked); | ||
774 | |||
775 | void pci_disable_link_state(struct pci_dev *pdev, int state) | ||
776 | { | ||
777 | __pci_disable_link_state(pdev, state, true); | ||
735 | } | 778 | } |
736 | EXPORT_SYMBOL(pci_disable_link_state); | 779 | EXPORT_SYMBOL(pci_disable_link_state); |
737 | 780 | ||
@@ -740,6 +783,8 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) | |||
740 | int i; | 783 | int i; |
741 | struct pcie_link_state *link; | 784 | struct pcie_link_state *link; |
742 | 785 | ||
786 | if (aspm_disabled) | ||
787 | return -EPERM; | ||
743 | for (i = 0; i < ARRAY_SIZE(policy_str); i++) | 788 | for (i = 0; i < ARRAY_SIZE(policy_str); i++) |
744 | if (!strncmp(val, policy_str[i], strlen(policy_str[i]))) | 789 | if (!strncmp(val, policy_str[i], strlen(policy_str[i]))) |
745 | break; | 790 | break; |
@@ -794,6 +839,8 @@ static ssize_t link_state_store(struct device *dev, | |||
794 | struct pcie_link_state *link, *root = pdev->link_state->root; | 839 | struct pcie_link_state *link, *root = pdev->link_state->root; |
795 | u32 val = buf[0] - '0', state = 0; | 840 | u32 val = buf[0] - '0', state = 0; |
796 | 841 | ||
842 | if (aspm_disabled) | ||
843 | return -EPERM; | ||
797 | if (n < 1 || val > 3) | 844 | if (n < 1 || val > 3) |
798 | return -EINVAL; | 845 | return -EINVAL; |
799 | 846 | ||
@@ -889,6 +936,7 @@ static int __init pcie_aspm_disable(char *str) | |||
889 | { | 936 | { |
890 | if (!strcmp(str, "off")) { | 937 | if (!strcmp(str, "off")) { |
891 | aspm_disabled = 1; | 938 | aspm_disabled = 1; |
939 | aspm_support_enabled = false; | ||
892 | printk(KERN_INFO "PCIe ASPM is disabled\n"); | 940 | printk(KERN_INFO "PCIe ASPM is disabled\n"); |
893 | } else if (!strcmp(str, "force")) { | 941 | } else if (!strcmp(str, "force")) { |
894 | aspm_force = 1; | 942 | aspm_force = 1; |
@@ -899,6 +947,12 @@ static int __init pcie_aspm_disable(char *str) | |||
899 | 947 | ||
900 | __setup("pcie_aspm=", pcie_aspm_disable); | 948 | __setup("pcie_aspm=", pcie_aspm_disable); |
901 | 949 | ||
950 | void pcie_clear_aspm(void) | ||
951 | { | ||
952 | if (!aspm_force) | ||
953 | aspm_clear_state = 1; | ||
954 | } | ||
955 | |||
902 | void pcie_no_aspm(void) | 956 | void pcie_no_aspm(void) |
903 | { | 957 | { |
904 | if (!aspm_force) | 958 | if (!aspm_force) |
@@ -917,3 +971,8 @@ int pcie_aspm_enabled(void) | |||
917 | } | 971 | } |
918 | EXPORT_SYMBOL(pcie_aspm_enabled); | 972 | EXPORT_SYMBOL(pcie_aspm_enabled); |
919 | 973 | ||
974 | bool pcie_aspm_support_enabled(void) | ||
975 | { | ||
976 | return aspm_support_enabled; | ||
977 | } | ||
978 | EXPORT_SYMBOL(pcie_aspm_support_enabled); | ||