PCI/ASPM: Configure L1 substate settings
Configure the L1 substate settings on the upstream and downstream devices, while taking care of the rules dictated by the PCIe spec. [bhelgaas: drop "inline"] Signed-off-by: Rajat Jain <rajatja@google.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
f1f0366dd6
commit
aeda9adeba
|
@ -588,6 +588,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
|
||||||
|
u32 clear, u32 set)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, pos, &val);
|
||||||
|
val &= ~clear;
|
||||||
|
val |= set;
|
||||||
|
pci_write_config_dword(pdev, pos, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure the ASPM L1 substates */
|
||||||
|
static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
|
||||||
|
{
|
||||||
|
u32 val, enable_req;
|
||||||
|
struct pci_dev *child = link->downstream, *parent = link->pdev;
|
||||||
|
u32 up_cap_ptr = link->l1ss.up_cap_ptr;
|
||||||
|
u32 dw_cap_ptr = link->l1ss.dw_cap_ptr;
|
||||||
|
|
||||||
|
enable_req = (link->aspm_enabled ^ state) & state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here are the rules specified in the PCIe spec for enabling L1SS:
|
||||||
|
* - When enabling L1.x, enable bit at parent first, then at child
|
||||||
|
* - When disabling L1.x, disable bit at child first, then at parent
|
||||||
|
* - When enabling ASPM L1.x, need to disable L1
|
||||||
|
* (at child followed by parent).
|
||||||
|
* - The ASPM/PCIPM L1.2 must be disabled while programming timing
|
||||||
|
* parameters
|
||||||
|
*
|
||||||
|
* To keep it simple, disable all L1SS bits first, and later enable
|
||||||
|
* what is needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Disable all L1 substates */
|
||||||
|
pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
PCI_L1SS_CTL1_L1SS_MASK, 0);
|
||||||
|
pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
PCI_L1SS_CTL1_L1SS_MASK, 0);
|
||||||
|
/*
|
||||||
|
* If needed, disable L1, and it gets enabled later
|
||||||
|
* in pcie_config_aspm_link().
|
||||||
|
*/
|
||||||
|
if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) {
|
||||||
|
pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
|
||||||
|
PCI_EXP_LNKCTL_ASPM_L1, 0);
|
||||||
|
pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
|
||||||
|
PCI_EXP_LNKCTL_ASPM_L1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable_req & ASPM_STATE_L1_2_MASK) {
|
||||||
|
|
||||||
|
/* Program T_pwr_on in both ports */
|
||||||
|
pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2,
|
||||||
|
link->l1ss.ctl2);
|
||||||
|
pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2,
|
||||||
|
link->l1ss.ctl2);
|
||||||
|
|
||||||
|
/* Program T_cmn_mode in parent */
|
||||||
|
pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
0xFF00, link->l1ss.ctl1);
|
||||||
|
|
||||||
|
/* Program LTR L1.2 threshold in both ports */
|
||||||
|
pci_clear_and_set_dword(parent, dw_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
0xE3FF0000, link->l1ss.ctl1);
|
||||||
|
pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
0xE3FF0000, link->l1ss.ctl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = 0;
|
||||||
|
if (state & ASPM_STATE_L1_1)
|
||||||
|
val |= PCI_L1SS_CTL1_ASPM_L1_1;
|
||||||
|
if (state & ASPM_STATE_L1_2)
|
||||||
|
val |= PCI_L1SS_CTL1_ASPM_L1_2;
|
||||||
|
if (state & ASPM_STATE_L1_1_PCIPM)
|
||||||
|
val |= PCI_L1SS_CTL1_PCIPM_L1_1;
|
||||||
|
if (state & ASPM_STATE_L1_2_PCIPM)
|
||||||
|
val |= PCI_L1SS_CTL1_PCIPM_L1_2;
|
||||||
|
|
||||||
|
/* Enable what we need to enable */
|
||||||
|
pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
PCI_L1SS_CAP_L1_PM_SS, val);
|
||||||
|
pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
|
||||||
|
PCI_L1SS_CAP_L1_PM_SS, val);
|
||||||
|
}
|
||||||
|
|
||||||
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
|
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
|
||||||
{
|
{
|
||||||
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
|
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
|
||||||
|
@ -597,11 +683,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
|
||||||
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
|
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
|
||||||
{
|
{
|
||||||
u32 upstream = 0, dwstream = 0;
|
u32 upstream = 0, dwstream = 0;
|
||||||
struct pci_dev *child, *parent = link->pdev;
|
struct pci_dev *child = link->downstream, *parent = link->pdev;
|
||||||
struct pci_bus *linkbus = parent->subordinate;
|
struct pci_bus *linkbus = parent->subordinate;
|
||||||
|
|
||||||
/* Nothing to do if the link is already in the requested state */
|
/* Enable only the states that were not explicitly disabled */
|
||||||
state &= (link->aspm_capable & ~link->aspm_disable);
|
state &= (link->aspm_capable & ~link->aspm_disable);
|
||||||
|
|
||||||
|
/* Can't enable any substates if L1 is not enabled */
|
||||||
|
if (!(state & ASPM_STATE_L1))
|
||||||
|
state &= ~ASPM_STATE_L1SS;
|
||||||
|
|
||||||
|
/* Spec says both ports must be in D0 before enabling PCI PM substates*/
|
||||||
|
if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) {
|
||||||
|
state &= ~ASPM_STATE_L1_SS_PCIPM;
|
||||||
|
state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nothing to do if the link is already in the requested state */
|
||||||
if (link->aspm_enabled == state)
|
if (link->aspm_enabled == state)
|
||||||
return;
|
return;
|
||||||
/* Convert ASPM state to upstream/downstream ASPM register state */
|
/* Convert ASPM state to upstream/downstream ASPM register state */
|
||||||
|
@ -613,6 +711,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
|
||||||
upstream |= PCI_EXP_LNKCTL_ASPM_L1;
|
upstream |= PCI_EXP_LNKCTL_ASPM_L1;
|
||||||
dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
|
dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link->aspm_capable & ASPM_STATE_L1SS)
|
||||||
|
pcie_config_aspm_l1ss(link, state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spec 2.0 suggests all functions should be configured the
|
* Spec 2.0 suggests all functions should be configured the
|
||||||
* same setting for ASPM. Enabling ASPM L1 should be done in
|
* same setting for ASPM. Enabling ASPM L1 should be done in
|
||||||
|
|
Loading…
Reference in New Issue