Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 127405
b: refs/heads/master
c: 46bbdfa
h: refs/heads/master
i:
  127403: f56f507
v: v3
  • Loading branch information
Shaohua Li authored and Jesse Barnes committed Jan 7, 2009
1 parent 1565b34 commit c5c20e0
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 20 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2b8c2efe44ed897fc958131d70addc89876d806b
refs/heads/master: 46bbdfa44cfc0d352148a0dc33ba9f6db02ccdf0
125 changes: 106 additions & 19 deletions trunk/drivers/pci/pcie/aspm.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ struct endpoint_state {
struct pcie_link_state {
struct list_head sibiling;
struct pci_dev *pdev;
bool downstream_has_switch;

struct pcie_link_state *parent;
struct list_head children;
struct list_head link;

/* ASPM state */
unsigned int support_state;
Expand Down Expand Up @@ -125,7 +130,7 @@ static void pcie_set_clock_pm(struct pci_dev *pdev, int enable)
link_state->clk_pm_enabled = !!enable;
}

static void pcie_check_clock_pm(struct pci_dev *pdev)
static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist)
{
int pos;
u32 reg32;
Expand All @@ -149,10 +154,26 @@ static void pcie_check_clock_pm(struct pci_dev *pdev)
if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
enabled = 0;
}
link_state->clk_pm_capable = capable;
link_state->clk_pm_enabled = enabled;
link_state->bios_clk_state = enabled;
pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
if (!blacklist) {
link_state->clk_pm_capable = capable;
pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev));
} else {
link_state->clk_pm_capable = 0;
pcie_set_clock_pm(pdev, 0);
}
}

static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev)
{
struct pci_dev *child_dev;

list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM)
return true;
}
return false;
}

/*
Expand Down Expand Up @@ -419,9 +440,9 @@ static unsigned int pcie_aspm_check_state(struct pci_dev *pdev,
{
struct pci_dev *child_dev;

/* If no child, disable the link */
/* If no child, ignore the link */
if (list_empty(&pdev->subordinate->devices))
return 0;
return state;
list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) {
if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
/*
Expand Down Expand Up @@ -462,6 +483,9 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
int valid = 1;
struct pcie_link_state *link_state = pdev->link_state;

/* If no child, disable the link */
if (list_empty(&pdev->subordinate->devices))
state = 0;
/*
* if the downstream component has pci bridge function, don't do ASPM
* now
Expand Down Expand Up @@ -493,20 +517,52 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state)
link_state->enabled_state = state;
}

static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link)
{
struct pcie_link_state *root_port_link = link;
while (root_port_link->parent)
root_port_link = root_port_link->parent;
return root_port_link;
}

/* check the whole hierarchy, and configure each link in the hierarchy */
static void __pcie_aspm_configure_link_state(struct pci_dev *pdev,
unsigned int state)
{
struct pcie_link_state *link_state = pdev->link_state;
struct pcie_link_state *root_port_link = get_root_port_link(link_state);
struct pcie_link_state *leaf;

if (link_state->support_state == 0)
return;
state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;

/* state 0 means disabling aspm */
state = pcie_aspm_check_state(pdev, state);
/* check all links who have specific root port link */
list_for_each_entry(leaf, &link_list, sibiling) {
if (!list_empty(&leaf->children) ||
get_root_port_link(leaf) != root_port_link)
continue;
state = pcie_aspm_check_state(leaf->pdev, state);
}
/* check root port link too in case it hasn't children */
state = pcie_aspm_check_state(root_port_link->pdev, state);

if (link_state->enabled_state == state)
return;
__pcie_aspm_config_link(pdev, state);

/*
* we must change the hierarchy. See comments in
* __pcie_aspm_config_link for the order
**/
if (state & PCIE_LINK_STATE_L1) {
list_for_each_entry(leaf, &link_list, sibiling) {
if (get_root_port_link(leaf) == root_port_link)
__pcie_aspm_config_link(leaf->pdev, state);
}
} else {
list_for_each_entry_reverse(leaf, &link_list, sibiling) {
if (get_root_port_link(leaf) == root_port_link)
__pcie_aspm_config_link(leaf->pdev, state);
}
}
}

/*
Expand Down Expand Up @@ -570,6 +626,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
unsigned int state;
struct pcie_link_state *link_state;
int error = 0;
int blacklist;

if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
return;
Expand All @@ -580,29 +637,58 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
if (list_empty(&pdev->subordinate->devices))
goto out;

if (pcie_aspm_sanity_check(pdev))
goto out;
blacklist = !!pcie_aspm_sanity_check(pdev);

mutex_lock(&aspm_lock);

link_state = kzalloc(sizeof(*link_state), GFP_KERNEL);
if (!link_state)
goto unlock_out;
pdev->link_state = link_state;

pcie_aspm_configure_common_clock(pdev);
link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev);
INIT_LIST_HEAD(&link_state->children);
INIT_LIST_HEAD(&link_state->link);
if (pdev->bus->self) {/* this is a switch */
struct pcie_link_state *parent_link_state;

pcie_aspm_cap_init(pdev);
parent_link_state = pdev->bus->parent->self->link_state;
if (!parent_link_state) {
kfree(link_state);
goto unlock_out;
}
list_add(&link_state->link, &parent_link_state->children);
link_state->parent = parent_link_state;
}

/* config link state to avoid BIOS error */
state = pcie_aspm_check_state(pdev, policy_to_aspm_state(pdev));
__pcie_aspm_config_link(pdev, state);
pdev->link_state = link_state;

pcie_check_clock_pm(pdev);
if (!blacklist) {
pcie_aspm_configure_common_clock(pdev);
pcie_aspm_cap_init(pdev);
} else {
link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1;
link_state->bios_aspm_state = 0;
/* Set support state to 0, so we will disable ASPM later */
link_state->support_state = 0;
}

link_state->pdev = pdev;
list_add(&link_state->sibiling, &link_list);

if (link_state->downstream_has_switch) {
/*
* If link has switch, delay the link config. The leaf link
* initialization will config the whole hierarchy. but we must
* make sure BIOS doesn't set unsupported link state
**/
state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state);
__pcie_aspm_config_link(pdev, state);
} else
__pcie_aspm_configure_link_state(pdev,
policy_to_aspm_state(pdev));

pcie_check_clock_pm(pdev, blacklist);

unlock_out:
if (error)
free_link_state(pdev);
Expand Down Expand Up @@ -635,6 +721,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
/* All functions are removed, so just disable ASPM for the link */
__pcie_aspm_config_one_dev(parent, 0);
list_del(&link_state->sibiling);
list_del(&link_state->link);
/* Clock PM is for endpoint device */

free_link_state(parent);
Expand Down

0 comments on commit c5c20e0

Please sign in to comment.