aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/aspm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie/aspm.c')
-rw-r--r--drivers/pci/pcie/aspm.c165
1 files changed, 124 insertions, 41 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 9aad608bcf3f..586b6f75910d 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -17,6 +17,7 @@
17#include <linux/init.h> 17#include <linux/init.h>
18#include <linux/slab.h> 18#include <linux/slab.h>
19#include <linux/jiffies.h> 19#include <linux/jiffies.h>
20#include <linux/delay.h>
20#include <linux/pci-aspm.h> 21#include <linux/pci-aspm.h>
21#include "../pci.h" 22#include "../pci.h"
22 23
@@ -33,6 +34,11 @@ struct endpoint_state {
33struct pcie_link_state { 34struct pcie_link_state {
34 struct list_head sibiling; 35 struct list_head sibiling;
35 struct pci_dev *pdev; 36 struct pci_dev *pdev;
37 bool downstream_has_switch;
38
39 struct pcie_link_state *parent;
40 struct list_head children;
41 struct list_head link;
36 42
37 /* ASPM state */ 43 /* ASPM state */
38 unsigned int support_state; 44 unsigned int support_state;
@@ -70,6 +76,8 @@ static const char *policy_str[] = {
70 [POLICY_POWERSAVE] = "powersave" 76 [POLICY_POWERSAVE] = "powersave"
71}; 77};
72 78
79#define LINK_RETRAIN_TIMEOUT HZ
80
73static int policy_to_aspm_state(struct pci_dev *pdev) 81static int policy_to_aspm_state(struct pci_dev *pdev)
74{ 82{
75 struct pcie_link_state *link_state = pdev->link_state; 83 struct pcie_link_state *link_state = pdev->link_state;
@@ -125,7 +133,7 @@ static void pcie_set_clock_pm(struct pci_dev *pdev, int enable)
125 link_state->clk_pm_enabled = !!enable; 133 link_state->clk_pm_enabled = !!enable;
126} 134}
127 135
128static void pcie_check_clock_pm(struct pci_dev *pdev) 136static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist)
129{ 137{
130 int pos; 138 int pos;
131 u32 reg32; 139 u32 reg32;
@@ -149,10 +157,26 @@ static void pcie_check_clock_pm(struct pci_dev *pdev)
149 if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) 157 if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
150 enabled = 0; 158 enabled = 0;
151 } 159 }
152 link_state->clk_pm_capable = capable;
153 link_state->clk_pm_enabled = enabled; 160 link_state->clk_pm_enabled = enabled;
154 link_state->bios_clk_state = enabled; 161 link_state->bios_clk_state = enabled;
155 pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); 162 if (!blacklist) {
163 link_state->clk_pm_capable = capable;
164 pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
165 } else {
166 link_state->clk_pm_capable = 0;
167 pcie_set_clock_pm(pdev, 0);
168 }
169}
170
171static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev)
172{
173 struct pci_dev *child_dev;
174
175 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
176 if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM)
177 return true;
178 }
179 return false;
156} 180}
157 181
158/* 182/*
@@ -217,16 +241,18 @@ static void pcie_aspm_configure_common_clock(struct pci_dev *pdev)
217 pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); 241 pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
218 242
219 /* Wait for link training end */ 243 /* Wait for link training end */
220 /* break out after waiting for 1 second */ 244 /* break out after waiting for timeout */
221 start_jiffies = jiffies; 245 start_jiffies = jiffies;
222 while ((jiffies - start_jiffies) < HZ) { 246 for (;;) {
223 pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, &reg16); 247 pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, &reg16);
224 if (!(reg16 & PCI_EXP_LNKSTA_LT)) 248 if (!(reg16 & PCI_EXP_LNKSTA_LT))
225 break; 249 break;
226 cpu_relax(); 250 if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
251 break;
252 msleep(1);
227 } 253 }
228 /* training failed -> recover */ 254 /* training failed -> recover */
229 if ((jiffies - start_jiffies) >= HZ) { 255 if (reg16 & PCI_EXP_LNKSTA_LT) {
230 dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure" 256 dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure"
231 " common clock\n"); 257 " common clock\n");
232 i = 0; 258 i = 0;
@@ -419,9 +445,9 @@ static unsigned int pcie_aspm_check_state(struct pci_dev *pdev,
419{ 445{
420 struct pci_dev *child_dev; 446 struct pci_dev *child_dev;
421 447
422 /* If no child, disable the link */ 448 /* If no child, ignore the link */
423 if (list_empty(&pdev->subordinate->devices)) 449 if (list_empty(&pdev->subordinate->devices))
424 return 0; 450 return state;
425 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { 451 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
426 if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { 452 if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
427 /* 453 /*
@@ -462,6 +488,9 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
462 int valid = 1; 488 int valid = 1;
463 struct pcie_link_state *link_state = pdev->link_state; 489 struct pcie_link_state *link_state = pdev->link_state;
464 490
491 /* If no child, disable the link */
492 if (list_empty(&pdev->subordinate->devices))
493 state = 0;
465 /* 494 /*
466 * if the downstream component has pci bridge function, don't do ASPM 495 * if the downstream component has pci bridge function, don't do ASPM
467 * now 496 * now
@@ -493,20 +522,52 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
493 link_state->enabled_state = state; 522 link_state->enabled_state = state;
494} 523}
495 524
525static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link)
526{
527 struct pcie_link_state *root_port_link = link;
528 while (root_port_link->parent)
529 root_port_link = root_port_link->parent;
530 return root_port_link;
531}
532
533/* check the whole hierarchy, and configure each link in the hierarchy */
496static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, 534static void __pcie_aspm_configure_link_state(struct pci_dev *pdev,
497 unsigned int state) 535 unsigned int state)
498{ 536{
499 struct pcie_link_state *link_state = pdev->link_state; 537 struct pcie_link_state *link_state = pdev->link_state;
538 struct pcie_link_state *root_port_link = get_root_port_link(link_state);
539 struct pcie_link_state *leaf;
500 540
501 if (link_state->support_state == 0)
502 return;
503 state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; 541 state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
504 542
505 /* state 0 means disabling aspm */ 543 /* check all links who have specific root port link */
506 state = pcie_aspm_check_state(pdev, state); 544 list_for_each_entry(leaf, &link_list, sibiling) {
545 if (!list_empty(&leaf->children) ||
546 get_root_port_link(leaf) != root_port_link)
547 continue;
548 state = pcie_aspm_check_state(leaf->pdev, state);
549 }
550 /* check root port link too in case it hasn't children */
551 state = pcie_aspm_check_state(root_port_link->pdev, state);
552
507 if (link_state->enabled_state == state) 553 if (link_state->enabled_state == state)
508 return; 554 return;
509 __pcie_aspm_config_link(pdev, state); 555
556 /*
557 * we must change the hierarchy. See comments in
558 * __pcie_aspm_config_link for the order
559 **/
560 if (state & PCIE_LINK_STATE_L1) {
561 list_for_each_entry(leaf, &link_list, sibiling) {
562 if (get_root_port_link(leaf) == root_port_link)
563 __pcie_aspm_config_link(leaf->pdev, state);
564 }
565 } else {
566 list_for_each_entry_reverse(leaf, &link_list, sibiling) {
567 if (get_root_port_link(leaf) == root_port_link)
568 __pcie_aspm_config_link(leaf->pdev, state);
569 }
570 }
510} 571}
511 572
512/* 573/*
@@ -570,6 +631,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
570 unsigned int state; 631 unsigned int state;
571 struct pcie_link_state *link_state; 632 struct pcie_link_state *link_state;
572 int error = 0; 633 int error = 0;
634 int blacklist;
573 635
574 if (aspm_disabled || !pdev->is_pcie || pdev->link_state) 636 if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
575 return; 637 return;
@@ -580,29 +642,58 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
580 if (list_empty(&pdev->subordinate->devices)) 642 if (list_empty(&pdev->subordinate->devices))
581 goto out; 643 goto out;
582 644
583 if (pcie_aspm_sanity_check(pdev)) 645 blacklist = !!pcie_aspm_sanity_check(pdev);
584 goto out;
585 646
586 mutex_lock(&aspm_lock); 647 mutex_lock(&aspm_lock);
587 648
588 link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); 649 link_state = kzalloc(sizeof(*link_state), GFP_KERNEL);
589 if (!link_state) 650 if (!link_state)
590 goto unlock_out; 651 goto unlock_out;
591 pdev->link_state = link_state;
592 652
593 pcie_aspm_configure_common_clock(pdev); 653 link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev);
654 INIT_LIST_HEAD(&link_state->children);
655 INIT_LIST_HEAD(&link_state->link);
656 if (pdev->bus->self) {/* this is a switch */
657 struct pcie_link_state *parent_link_state;
594 658
595 pcie_aspm_cap_init(pdev); 659 parent_link_state = pdev->bus->parent->self->link_state;
660 if (!parent_link_state) {
661 kfree(link_state);
662 goto unlock_out;
663 }
664 list_add(&link_state->link, &parent_link_state->children);
665 link_state->parent = parent_link_state;
666 }
596 667
597 /* config link state to avoid BIOS error */ 668 pdev->link_state = link_state;
598 state = pcie_aspm_check_state(pdev, policy_to_aspm_state(pdev));
599 __pcie_aspm_config_link(pdev, state);
600 669
601 pcie_check_clock_pm(pdev); 670 if (!blacklist) {
671 pcie_aspm_configure_common_clock(pdev);
672 pcie_aspm_cap_init(pdev);
673 } else {
674 link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
675 link_state->bios_aspm_state = 0;
676 /* Set support state to 0, so we will disable ASPM later */
677 link_state->support_state = 0;
678 }
602 679
603 link_state->pdev = pdev; 680 link_state->pdev = pdev;
604 list_add(&link_state->sibiling, &link_list); 681 list_add(&link_state->sibiling, &link_list);
605 682
683 if (link_state->downstream_has_switch) {
684 /*
685 * If link has switch, delay the link config. The leaf link
686 * initialization will config the whole hierarchy. but we must
687 * make sure BIOS doesn't set unsupported link state
688 **/
689 state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state);
690 __pcie_aspm_config_link(pdev, state);
691 } else
692 __pcie_aspm_configure_link_state(pdev,
693 policy_to_aspm_state(pdev));
694
695 pcie_check_clock_pm(pdev, blacklist);
696
606unlock_out: 697unlock_out:
607 if (error) 698 if (error)
608 free_link_state(pdev); 699 free_link_state(pdev);
@@ -635,6 +726,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
635 /* All functions are removed, so just disable ASPM for the link */ 726 /* All functions are removed, so just disable ASPM for the link */
636 __pcie_aspm_config_one_dev(parent, 0); 727 __pcie_aspm_config_one_dev(parent, 0);
637 list_del(&link_state->sibiling); 728 list_del(&link_state->sibiling);
729 list_del(&link_state->link);
638 /* Clock PM is for endpoint device */ 730 /* Clock PM is for endpoint device */
639 731
640 free_link_state(parent); 732 free_link_state(parent);
@@ -857,24 +949,15 @@ void pcie_no_aspm(void)
857 aspm_disabled = 1; 949 aspm_disabled = 1;
858} 950}
859 951
860#ifdef CONFIG_ACPI 952/**
861#include <acpi/acpi_bus.h> 953 * pcie_aspm_enabled - is PCIe ASPM enabled?
862#include <linux/pci-acpi.h> 954 *
863static void pcie_aspm_platform_init(void) 955 * Returns true if ASPM has not been disabled by the command-line option
864{ 956 * pcie_aspm=off.
865 pcie_osc_support_set(OSC_ACTIVE_STATE_PWR_SUPPORT| 957 **/
866 OSC_CLOCK_PWR_CAPABILITY_SUPPORT); 958int pcie_aspm_enabled(void)
867}
868#else
869static inline void pcie_aspm_platform_init(void) { }
870#endif
871
872static int __init pcie_aspm_init(void)
873{ 959{
874 if (aspm_disabled) 960 return !aspm_disabled;
875 return 0;
876 pcie_aspm_platform_init();
877 return 0;
878} 961}
962EXPORT_SYMBOL(pcie_aspm_enabled);
879 963
880fs_initcall(pcie_aspm_init);