ls1043a add suspend/resume support. Signed-off-by: Frank Li <Frank.Li@xxxxxxx> --- drivers/pci/controller/dwc/pci-layerscape.c | 91 ++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index bc5a8ff1a26ce..debabb9bb41f4 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -41,10 +41,20 @@ #define SCFG_PEXSFTRSTCR 0x190 #define PEXSR(idx) BIT(idx) +/* LS1043A PEX PME control register */ +#define SCFG_PEXPMECR 0x144 +#define PEXPME(idx) BIT(31 - (idx) * 4) + +/* LS1043A PEX LUT debug register */ +#define LS_PCIE_LDBG 0x7fc +#define LDBG_SR BIT(30) +#define LDBG_WE BIT(31) + #define PCIE_IATU_NUM 6 struct ls_pcie_drvdata { const u32 pf_off; + const u32 lut_off; const struct dw_pcie_host_ops *ops; void (*exit_from_l2)(struct dw_pcie_rp *pp); bool pm_support; @@ -54,6 +64,7 @@ struct ls_pcie { struct dw_pcie *pci; const struct ls_pcie_drvdata *drvdata; void __iomem *pf_base; + void __iomem *lut_base; struct regmap *scfg; int index; bool big_endian; @@ -116,6 +127,23 @@ static void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val) iowrite32(val, pcie->pf_base + off); } +static u32 ls_pcie_lut_readl(struct ls_pcie *pcie, u32 off) +{ + if (pcie->big_endian) + return ioread32be(pcie->lut_base + off); + + return ioread32(pcie->lut_base + off); +} + +static void ls_pcie_lut_writel(struct ls_pcie *pcie, u32 off, u32 val) +{ + if (pcie->big_endian) + iowrite32be(val, pcie->lut_base + off); + else + iowrite32(val, pcie->lut_base + off); +} + + static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -249,6 +277,54 @@ static int ls1021a_pcie_host_init(struct dw_pcie_rp *pp) return ret; } +static void ls1043a_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + + if (!pcie->scfg) { + dev_dbg(pcie->pci->dev, "SYSCFG is NULL\n"); + return; + } + + /* Send Turn_off message */ + regmap_read(pcie->scfg, SCFG_PEXPMECR, &val); + val |= PEXPME(pcie->index); + regmap_write(pcie->scfg, SCFG_PEXPMECR, val); + + /* There are not register to check ACK, so wait PCIE_PME_TO_L2_TIMEOUT_US */ + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000); + + /* Clear Turn_off message */ + regmap_read(pcie->scfg, SCFG_PEXPMECR, &val); + val &= ~PEXPME(pcie->index); + regmap_write(pcie->scfg, SCFG_PEXPMECR, val); +} + +static void ls1043a_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + + val = ls_pcie_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_WE; + ls_pcie_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_SR; + ls_pcie_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_SR; + ls_pcie_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_WE; + ls_pcie_lut_writel(pcie, LS_PCIE_LDBG, val); +} + static const struct dw_pcie_host_ops ls_pcie_host_ops = { .host_init = ls_pcie_host_init, .pme_turn_off = ls_pcie_send_turnoff_msg, @@ -265,6 +341,18 @@ static const struct ls_pcie_drvdata ls1021a_drvdata = { .exit_from_l2 = ls1021a_pcie_exit_from_l2, }; +static const struct dw_pcie_host_ops ls1043a_pcie_host_ops = { + .host_init = ls1021a_pcie_host_init, /* the same as ls1021 */ + .pme_turn_off = ls1043a_pcie_send_turnoff_msg, +}; + +static const struct ls_pcie_drvdata ls1043a_drvdata = { + .lut_off = 0x10000, + .pm_support = true, + .ops = &ls1043a_pcie_host_ops, + .exit_from_l2 = ls1043a_pcie_exit_from_l2, +}; + static const struct ls_pcie_drvdata layerscape_drvdata = { .pf_off = 0xc0000, .pm_support = true, @@ -275,7 +363,7 @@ static const struct of_device_id ls_pcie_of_match[] = { { .compatible = "fsl,ls1012a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls1021a-pcie", .data = &ls1021a_drvdata }, { .compatible = "fsl,ls1028a-pcie", .data = &layerscape_drvdata }, - { .compatible = "fsl,ls1043a-pcie", .data = &ls1021a_drvdata }, + { .compatible = "fsl,ls1043a-pcie", .data = &ls1043a_drvdata }, { .compatible = "fsl,ls1046a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls2080a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls2085a-pcie", .data = &layerscape_drvdata }, @@ -314,6 +402,7 @@ static int ls_pcie_probe(struct platform_device *pdev) pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off; + pcie->lut_base = pci->dbi_base + pcie->drvdata->lut_off; if (!ls_pcie_is_bridge(pcie)) return -ENODEV; -- 2.34.1