Commit b5a0a9b5 authored by Rajat Jain's avatar Rajat Jain Committed by Bjorn Helgaas

PCI/ASPM: Read and set up L1 substate capabilities

The PCIe spec (r3.1, sec 7.33) says the L1 PM Substates Capability may be
implemented only in function 0.

Read the L1 substate capability structures of upstream and downstream
components of the link and set it up in the device structure.

[bhelgaas: add specific spec reference]
Signed-off-by: default avatarRajat Jain <rajatja@google.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent b2103ccb
...@@ -49,6 +49,7 @@ struct aspm_latency { ...@@ -49,6 +49,7 @@ struct aspm_latency {
struct pcie_link_state { struct pcie_link_state {
struct pci_dev *pdev; /* Upstream component of the Link */ struct pci_dev *pdev; /* Upstream component of the Link */
struct pci_dev *downstream; /* Downstream component, function 0 */
struct pcie_link_state *root; /* pointer to the root port link */ struct pcie_link_state *root; /* pointer to the root port link */
struct pcie_link_state *parent; /* pointer to the parent Link state */ struct pcie_link_state *parent; /* pointer to the parent Link state */
struct list_head sibling; /* node in link_list */ struct list_head sibling; /* node in link_list */
...@@ -300,6 +301,12 @@ struct aspm_register_info { ...@@ -300,6 +301,12 @@ struct aspm_register_info {
u32 enabled:2; u32 enabled:2;
u32 latency_encoding_l0s; u32 latency_encoding_l0s;
u32 latency_encoding_l1; u32 latency_encoding_l1;
/* L1 substates */
u32 l1ss_cap_ptr;
u32 l1ss_cap;
u32 l1ss_ctl1;
u32 l1ss_ctl2;
}; };
static void pcie_get_aspm_reg(struct pci_dev *pdev, static void pcie_get_aspm_reg(struct pci_dev *pdev,
...@@ -314,6 +321,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev, ...@@ -314,6 +321,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev,
info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16); pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
/* Read L1 PM substate capabilities */
info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0;
info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
if (!info->l1ss_cap_ptr)
return;
pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP,
&info->l1ss_cap);
if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) {
info->l1ss_cap = 0;
return;
}
pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1,
&info->l1ss_ctl1);
pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2,
&info->l1ss_ctl2);
} }
static void pcie_aspm_check_latency(struct pci_dev *endpoint) static void pcie_aspm_check_latency(struct pci_dev *endpoint)
...@@ -355,6 +378,20 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) ...@@ -355,6 +378,20 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
} }
} }
/*
* The L1 PM substate capability is only implemented in function 0 in a
* multi function device.
*/
static struct pci_dev *pci_function_0(struct pci_bus *linkbus)
{
struct pci_dev *child;
list_for_each_entry(child, &linkbus->devices, bus_list)
if (PCI_FUNC(child->devfn) == 0)
return child;
return NULL;
}
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{ {
struct pci_dev *child, *parent = link->pdev; struct pci_dev *child, *parent = link->pdev;
...@@ -370,8 +407,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) ...@@ -370,8 +407,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
/* Get upstream/downstream components' register state */ /* Get upstream/downstream components' register state */
pcie_get_aspm_reg(parent, &upreg); pcie_get_aspm_reg(parent, &upreg);
child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); child = pci_function_0(linkbus);
pcie_get_aspm_reg(child, &dwreg); pcie_get_aspm_reg(child, &dwreg);
link->downstream = child;
/* /*
* If ASPM not supported, don't mess with the clocks and link, * If ASPM not supported, don't mess with the clocks and link,
...@@ -414,6 +452,25 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) ...@@ -414,6 +452,25 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1); link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1); link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
/* Setup L1 substate */
if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1)
link->aspm_support |= ASPM_STATE_L1_1;
if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2)
link->aspm_support |= ASPM_STATE_L1_2;
if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1)
link->aspm_support |= ASPM_STATE_L1_1_PCIPM;
if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2)
link->aspm_support |= ASPM_STATE_L1_2_PCIPM;
if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1)
link->aspm_enabled |= ASPM_STATE_L1_1;
if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2)
link->aspm_enabled |= ASPM_STATE_L1_2;
if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1)
link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM;
if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;
/* Save default state */ /* Save default state */
link->aspm_default = link->aspm_enabled; link->aspm_default = link->aspm_enabled;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment