PCI: rockchip: dw: Save&Restore L1SS in PM

Change-Id: I05c9787faff35e3ca4ea21b35261f4f7c3496fc8
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
This commit is contained in:
Jon Lin
2022-05-05 11:21:06 +08:00
committed by Tao Huang
parent a8d165418a
commit 0a082fd9da

View File

@@ -179,6 +179,7 @@ struct rk_pcie {
int legacy_parent_irq; int legacy_parent_irq;
raw_spinlock_t intx_lock; raw_spinlock_t intx_lock;
u16 aspm; u16 aspm;
u32 l1ss_ctl1;
}; };
struct rk_pcie_of_data { struct rk_pcie_of_data {
@@ -1971,13 +1972,14 @@ static void rk_pcie_downstream_dev_to_d0(struct rk_pcie *rk_pcie, bool enable)
{ {
struct pcie_port *pp = &rk_pcie->pci->pp; struct pcie_port *pp = &rk_pcie->pci->pp;
struct pci_bus *child, *root_bus = NULL; struct pci_bus *child, *root_bus = NULL;
struct pci_dev *pdev; struct pci_dev *pdev, *bridge;
u32 reg, val; u32 val;
list_for_each_entry(child, &pp->bridge->bus->children, node) { list_for_each_entry(child, &pp->bridge->bus->children, node) {
/* Bring downstream devices to D3 if they are not already in */ /* Bring downstream devices to D3 if they are not already in */
if (child->parent == pp->bridge->bus) { if (child->parent == pp->bridge->bus) {
root_bus = child; root_bus = child;
bridge = root_bus->self;
break; break;
} }
} }
@@ -1988,15 +1990,23 @@ static void rk_pcie_downstream_dev_to_d0(struct rk_pcie *rk_pcie, bool enable)
} }
/* Save and restore root bus ASPM */ /* Save and restore root bus ASPM */
reg = dw_pcie_find_capability(rk_pcie->pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(rk_pcie->pci, reg + PCI_EXP_LNKCTL);
if (enable) { if (enable) {
if (rk_pcie->l1ss_ctl1)
dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1, rk_pcie->l1ss_ctl1);
/* rk_pcie->aspm woule be saved in advance when enable is false */ /* rk_pcie->aspm woule be saved in advance when enable is false */
dw_pcie_writel_dbi(rk_pcie->pci, reg + PCI_EXP_LNKCTL, rk_pcie->aspm); dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, rk_pcie->aspm);
} else { } else {
val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1);
if (val & PCI_L1SS_CTL1_L1SS_MASK)
rk_pcie->l1ss_ctl1 = val;
else
rk_pcie->l1ss_ctl1 = 0;
val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL);
rk_pcie->aspm = val & PCI_EXP_LNKCTL_ASPMC; rk_pcie->aspm = val & PCI_EXP_LNKCTL_ASPMC;
val &= ~(PCI_EXP_LNKCAP_ASPM_L1 | PCI_EXP_LNKCAP_ASPM_L0S); val &= ~(PCI_EXP_LNKCAP_ASPM_L1 | PCI_EXP_LNKCAP_ASPM_L0S);
dw_pcie_writel_dbi(rk_pcie->pci, reg + PCI_EXP_LNKCTL, val); dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, val);
} }
list_for_each_entry(pdev, &root_bus->devices, bus_list) { list_for_each_entry(pdev, &root_bus->devices, bus_list) {
@@ -2005,11 +2015,19 @@ static void rk_pcie_downstream_dev_to_d0(struct rk_pcie *rk_pcie, bool enable)
dev_err(rk_pcie->pci->dev, dev_err(rk_pcie->pci->dev,
"Failed to transition %s to D3hot state\n", "Failed to transition %s to D3hot state\n",
dev_name(&pdev->dev)); dev_name(&pdev->dev));
if (enable) if (enable) {
if (rk_pcie->l1ss_ctl1) {
pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, &val);
val &= ~PCI_L1SS_CTL1_L1SS_MASK;
val |= (rk_pcie->l1ss_ctl1 & PCI_L1SS_CTL1_L1SS_MASK);
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, val);
}
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
PCI_EXP_LNKCTL_ASPMC, rk_pcie->aspm); PCI_EXP_LNKCTL_ASPMC, rk_pcie->aspm);
else } else {
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
}
} }
} }
} }