aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShaohua Li <shaohua.li@intel.com>2008-12-18 20:27:42 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-01-07 14:13:20 -0500
commit46bbdfa44cfc0d352148a0dc33ba9f6db02ccdf0 (patch)
treeb5b7d00f66ea4b5865b64d97592f126fb9f12f22
parent2b8c2efe44ed897fc958131d70addc89876d806b (diff)
PCI: keep ASPM link state consistent throughout PCIe hierarchy
In a PCIe hierarchy with a switch present, if the link state of an endpoint device is changed, we must check the whole hierarchy from the endpoint device to root port, and for each link in the hierarchy, the new link state should be configured. Previously, the implementation checked the state but forgot to configure the links between root port to switch. Fixes Novell bz #448987. Signed-off-by: Shaohua Li <shaohua.li@intel.com> Tested-by: Andrew Patterson <andrew.patterson@hp.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/pci/pcie/aspm.c125
1 files changed, 106 insertions, 19 deletions
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index e361c7dc726f..4d8e2c7b2ad1 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -33,6 +33,11 @@ struct endpoint_state {
33struct pcie_link_state { 33struct pcie_link_state {
34 struct list_head sibiling; 34 struct list_head sibiling;
35 struct pci_dev *pdev; 35 struct pci_dev *pdev;
36 bool downstream_has_switch;
37
38 struct pcie_link_state *parent;
39 struct list_head children;
40 struct list_head link;
36 41
37 /* ASPM state */ 42 /* ASPM state */
38 unsigned int support_state; 43 unsigned int support_state;
@@ -125,7 +130,7 @@ static void pcie_set_clock_pm(struct pci_dev *pdev, int enable)
125 link_state->clk_pm_enabled = !!enable; 130 link_state->clk_pm_enabled = !!enable;
126} 131}
127 132
128static void pcie_check_clock_pm(struct pci_dev *pdev) 133static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist)
129{ 134{
130 int pos; 135 int pos;
131 u32 reg32; 136 u32 reg32;
@@ -149,10 +154,26 @@ static void pcie_check_clock_pm(struct pci_dev *pdev)
149 if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) 154 if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
150 enabled = 0; 155 enabled = 0;
151 } 156 }
152 link_state->clk_pm_capable = capable;
153 link_state->clk_pm_enabled = enabled; 157 link_state->clk_pm_enabled = enabled;
154 link_state->bios_clk_state = enabled; 158 link_state->bios_clk_state = enabled;
155 pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); 159 if (!blacklist) {
160 link_state->clk_pm_capable = capable;
161 pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
162 } else {
163 link_state->clk_pm_capable = 0;
164 pcie_set_clock_pm(pdev, 0);
165 }
166}
167
168static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev)
169{
170 struct pci_dev *child_dev;
171
172 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
173 if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM)
174 return true;
175 }
176 return false;
156} 177}
157 178
158/* 179/*
@@ -419,9 +440,9 @@ static unsigned int pcie_aspm_check_state(struct pci_dev *pdev,
419{ 440{
420 struct pci_dev *child_dev; 441 struct pci_dev *child_dev;
421 442
422 /* If no child, disable the link */ 443 /* If no child, ignore the link */
423 if (list_empty(&pdev->subordinate->devices)) 444 if (list_empty(&pdev->subordinate->devices))
424 return 0; 445 return state;
425 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { 446 list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
426 if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { 447 if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
427 /* 448 /*
@@ -462,6 +483,9 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
462 int valid = 1; 483 int valid = 1;
463 struct pcie_link_state *link_state = pdev->link_state; 484 struct pcie_link_state *link_state = pdev->link_state;
464 485
486 /* If no child, disable the link */
487 if (list_empty(&pdev->subordinate->devices))
488 state = 0;
465 /* 489 /*
466 * if the downstream component has pci bridge function, don't do ASPM 490 * if the downstream component has pci bridge function, don't do ASPM
467 * now 491 * now
@@ -493,20 +517,52 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
493 link_state->enabled_state = state; 517 link_state->enabled_state = state;
494} 518}
495 519
520static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link)
521{
522 struct pcie_link_state *root_port_link = link;
523 while (root_port_link->parent)
524 root_port_link = root_port_link->parent;
525 return root_port_link;
526}
527
528/* check the whole hierarchy, and configure each link in the hierarchy */
496static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, 529static void __pcie_aspm_configure_link_state(struct pci_dev *pdev,
497 unsigned int state) 530 unsigned int state)
498{ 531{
499 struct pcie_link_state *link_state = pdev->link_state; 532 struct pcie_link_state *link_state = pdev->link_state;
533 struct pcie_link_state *root_port_link = get_root_port_link(link_state);
534 struct pcie_link_state *leaf;
500 535
501 if (link_state->support_state == 0)
502 return;
503 state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; 536 state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
504 537
505 /* state 0 means disabling aspm */ 538 /* check all links who have specific root port link */
506 state = pcie_aspm_check_state(pdev, state); 539 list_for_each_entry(leaf, &link_list, sibiling) {
540 if (!list_empty(&leaf->children) ||
541 get_root_port_link(leaf) != root_port_link)
542 continue;
543 state = pcie_aspm_check_state(leaf->pdev, state);
544 }
545 /* check root port link too in case it hasn't children */
546 state = pcie_aspm_check_state(root_port_link->pdev, state);
547
507 if (link_state->enabled_state == state) 548 if (link_state->enabled_state == state)
508 return; 549 return;
509 __pcie_aspm_config_link(pdev, state); 550
551 /*
552 * we must change the hierarchy. See comments in
553 * __pcie_aspm_config_link for the order
554 **/
555 if (state & PCIE_LINK_STATE_L1) {
556 list_for_each_entry(leaf, &link_list, sibiling) {
557 if (get_root_port_link(leaf) == root_port_link)
558 __pcie_aspm_config_link(leaf->pdev, state);
559 }
560 } else {
561 list_for_each_entry_reverse(leaf, &link_list, sibiling) {
562 if (get_root_port_link(leaf) == root_port_link)
563 __pcie_aspm_config_link(leaf->pdev, state);
564 }
565 }
510} 566}
511 567
512/* 568/*
@@ -570,6 +626,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
570 unsigned int state; 626 unsigned int state;
571 struct pcie_link_state *link_state; 627 struct pcie_link_state *link_state;
572 int error = 0; 628 int error = 0;
629 int blacklist;
573 630
574 if (aspm_disabled || !pdev->is_pcie || pdev->link_state) 631 if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
575 return; 632 return;
@@ -580,29 +637,58 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
580 if (list_empty(&pdev->subordinate->devices)) 637 if (list_empty(&pdev->subordinate->devices))
581 goto out; 638 goto out;
582 639
583 if (pcie_aspm_sanity_check(pdev)) 640 blacklist = !!pcie_aspm_sanity_check(pdev);
584 goto out;
585 641
586 mutex_lock(&aspm_lock); 642 mutex_lock(&aspm_lock);
587 643
588 link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); 644 link_state = kzalloc(sizeof(*link_state), GFP_KERNEL);
589 if (!link_state) 645 if (!link_state)
590 goto unlock_out; 646 goto unlock_out;
591 pdev->link_state = link_state;
592 647
593 pcie_aspm_configure_common_clock(pdev); 648 link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev);
649 INIT_LIST_HEAD(&link_state->children);
650 INIT_LIST_HEAD(&link_state->link);
651 if (pdev->bus->self) {/* this is a switch */
652 struct pcie_link_state *parent_link_state;
594 653
595 pcie_aspm_cap_init(pdev); 654 parent_link_state = pdev->bus->parent->self->link_state;
655 if (!parent_link_state) {
656 kfree(link_state);
657 goto unlock_out;
658 }
659 list_add(&link_state->link, &parent_link_state->children);
660 link_state->parent = parent_link_state;
661 }
596 662
597 /* config link state to avoid BIOS error */ 663 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 664
601 pcie_check_clock_pm(pdev); 665 if (!blacklist) {
666 pcie_aspm_configure_common_clock(pdev);
667 pcie_aspm_cap_init(pdev);
668 } else {
669 link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
670 link_state->bios_aspm_state = 0;
671 /* Set support state to 0, so we will disable ASPM later */
672 link_state->support_state = 0;
673 }
602 674
603 link_state->pdev = pdev; 675 link_state->pdev = pdev;
604 list_add(&link_state->sibiling, &link_list); 676 list_add(&link_state->sibiling, &link_list);
605 677
678 if (link_state->downstream_has_switch) {
679 /*
680 * If link has switch, delay the link config. The leaf link
681 * initialization will config the whole hierarchy. but we must
682 * make sure BIOS doesn't set unsupported link state
683 **/
684 state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state);
685 __pcie_aspm_config_link(pdev, state);
686 } else
687 __pcie_aspm_configure_link_state(pdev,
688 policy_to_aspm_state(pdev));
689
690 pcie_check_clock_pm(pdev, blacklist);
691
606unlock_out: 692unlock_out:
607 if (error) 693 if (error)
608 free_link_state(pdev); 694 free_link_state(pdev);
@@ -635,6 +721,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
635 /* All functions are removed, so just disable ASPM for the link */ 721 /* All functions are removed, so just disable ASPM for the link */
636 __pcie_aspm_config_one_dev(parent, 0); 722 __pcie_aspm_config_one_dev(parent, 0);
637 list_del(&link_state->sibiling); 723 list_del(&link_state->sibiling);
724 list_del(&link_state->link);
638 /* Clock PM is for endpoint device */ 725 /* Clock PM is for endpoint device */
639 726
640 free_link_state(parent); 727 free_link_state(parent);