2023-12-01 16:18:30

by Frank Li

[permalink] [raw]
Subject: [PATCH v5 0/4] PCI: layerscape: Add suspend/resume support for ls1043 and ls1021

Add suspend/resume support for ls1043 and ls1021.

Change log see each patch

Frank Li (4):
PCI: layerscape: Add function pointer for exit_from_l2()
PCI: layerscape: Add suspend/resume for ls1021a
PCI: layerscape(ep): Rename pf_* as pf_lut_*
PCI: layerscape: Add suspend/resume for ls1043a

.../pci/controller/dwc/pci-layerscape-ep.c | 16 +-
drivers/pci/controller/dwc/pci-layerscape.c | 189 ++++++++++++++++--
2 files changed, 176 insertions(+), 29 deletions(-)

--
2.34.1


2023-12-01 16:18:38

by Frank Li

[permalink] [raw]
Subject: [PATCH v5 1/4] PCI: layerscape: Add function pointer for exit_from_l2()

Since difference SoCs require different sequence for exiting L2, let's add
a separate "exit_from_l2()" callback. This callback can be used to execute
SoC specific sequence.

Change ls_pcie_exit_from_l2() return value from void to int. Return error
if exit_from_l2() failure at exit resume flow.

Reviewed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Frank Li <[email protected]>
---

Notes:
Change from v4 to v5
- none
Change from v3 to v4
- update commit message
Add mani's review by tag
Change from v2 to v3
- fixed according to mani's feedback
1. update commit message
2. move dw_pcie_host_ops to next patch
3. check return value from exit_from_l2()
Change from v1 to v2
- change subject 'a' to 'A'

Change from v1 to v2
- change subject 'a' to 'A'

drivers/pci/controller/dwc/pci-layerscape.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
index 37956e09c65bd..aea89926bcc4f 100644
--- a/drivers/pci/controller/dwc/pci-layerscape.c
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -39,6 +39,7 @@

struct ls_pcie_drvdata {
const u32 pf_off;
+ int (*exit_from_l2)(struct dw_pcie_rp *pp);
bool pm_support;
};

@@ -125,7 +126,7 @@ static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
dev_err(pcie->pci->dev, "PME_Turn_off timeout\n");
}

-static void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp)
+static int ls_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);
@@ -150,6 +151,8 @@ static void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp)
10000);
if (ret)
dev_err(pcie->pci->dev, "L2 exit timeout\n");
+
+ return ret;
}

static int ls_pcie_host_init(struct dw_pcie_rp *pp)
@@ -180,6 +183,7 @@ static const struct ls_pcie_drvdata ls1021a_drvdata = {
static const struct ls_pcie_drvdata layerscape_drvdata = {
.pf_off = 0xc0000,
.pm_support = true,
+ .exit_from_l2 = ls_pcie_exit_from_l2,
};

static const struct of_device_id ls_pcie_of_match[] = {
@@ -247,11 +251,14 @@ static int ls_pcie_suspend_noirq(struct device *dev)
static int ls_pcie_resume_noirq(struct device *dev)
{
struct ls_pcie *pcie = dev_get_drvdata(dev);
+ int ret;

if (!pcie->drvdata->pm_support)
return 0;

- ls_pcie_exit_from_l2(&pcie->pci->pp);
+ ret = pcie->drvdata->exit_from_l2(&pcie->pci->pp);
+ if (ret)
+ return ret;

return dw_pcie_resume_noirq(pcie->pci);
}
--
2.34.1

2023-12-01 16:18:59

by Frank Li

[permalink] [raw]
Subject: [PATCH v5 2/4] PCI: layerscape: Add suspend/resume for ls1021a

Add suspend/resume support for Layerscape LS1021a.

In the suspend path, PME_Turn_Off message is sent to the endpoint to
transition the link to L2/L3_Ready state. In this SoC, there is no way to
check if the controller has received the PME_To_Ack from the endpoint or
not. So to be on the safer side, the driver just waits for
PCIE_PME_TO_L2_TIMEOUT_US before asserting the SoC specific PMXMTTURNOFF
bit to complete the PME_Turn_Off handshake. Then the link would enter L2/L3
state depending on the VAUX supply.

In the resume path, the link is brought back from L2 to L0 by doing a
software reset.

Signed-off-by: Frank Li <[email protected]>
---

Notes:
Change from v4 to v5
- update comit message
- remove a empty line
- use comments
/* Reset the PEX wrapper to bring the link out of L2 */
- pci->pp.ops = pcie->drvdata->ops,
ls_pcie_host_ops to the "ops" member of layerscape_drvdata.
- don't set pcie->scfg = NULL at error path

Change from v3 to v4
- update commit message.
- it is reset a glue logic part for PCI controller.
- use regmap_write_bits() to reduce code change.

Change from v2 to v3
- update according to mani's feedback
change from v1 to v2
- change subject 'a' to 'A'

drivers/pci/controller/dwc/pci-layerscape.c | 81 ++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
index aea89926bcc4f..8bdaae9be7d56 100644
--- a/drivers/pci/controller/dwc/pci-layerscape.c
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -35,11 +35,19 @@
#define PF_MCR_PTOMR BIT(0)
#define PF_MCR_EXL2S BIT(1)

+/* LS1021A PEXn PM Write Control Register */
+#define SCFG_PEXPMWRCR(idx) (0x5c + (idx) * 0x64)
+#define PMXMTTURNOFF BIT(31)
+#define SCFG_PEXSFTRSTCR 0x190
+#define PEXSR(idx) BIT(idx)
+
#define PCIE_IATU_NUM 6

struct ls_pcie_drvdata {
const u32 pf_off;
+ const struct dw_pcie_host_ops *ops;
int (*exit_from_l2)(struct dw_pcie_rp *pp);
+ bool scfg_support;
bool pm_support;
};

@@ -47,6 +55,8 @@ struct ls_pcie {
struct dw_pcie *pci;
const struct ls_pcie_drvdata *drvdata;
void __iomem *pf_base;
+ struct regmap *scfg;
+ int index;
bool big_endian;
};

@@ -171,18 +181,70 @@ static int ls_pcie_host_init(struct dw_pcie_rp *pp)
return 0;
}

+static void scfg_pcie_send_turnoff_msg(struct regmap *scfg, u32 reg, u32 mask)
+{
+ /* Send PME_Turn_Off message */
+ regmap_write_bits(scfg, reg, mask, mask);
+
+ /*
+ * There is no specific register to check for PME_To_Ack from endpoint.
+ * So on the safe side, wait for PCIE_PME_TO_L2_TIMEOUT_US.
+ */
+ mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000);
+
+ /*
+ * Layerscape hardware reference manual recommends clearing the PMXMTTURNOFF bit
+ * to complete the PME_Turn_Off handshake.
+ */
+ regmap_write_bits(scfg, reg, mask, 0);
+}
+
+static void ls1021a_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);
+
+ scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), PMXMTTURNOFF);
+}
+
+static int scfg_pcie_exit_from_l2(struct regmap *scfg, u32 reg, u32 mask)
+{
+ /* Reset the PEX wrapper to bring the link out of L2 */
+ regmap_write_bits(scfg, reg, mask, mask);
+ regmap_write_bits(scfg, reg, mask, 0);
+
+ return 0;
+}
+
+static int ls1021a_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);
+
+ return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index));
+}
+
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,
};

+static const struct dw_pcie_host_ops ls1021a_pcie_host_ops = {
+ .host_init = ls_pcie_host_init,
+ .pme_turn_off = ls1021a_pcie_send_turnoff_msg,
+};
+
static const struct ls_pcie_drvdata ls1021a_drvdata = {
- .pm_support = false,
+ .pm_support = true,
+ .scfg_support = true,
+ .ops = &ls1021a_pcie_host_ops,
+ .exit_from_l2 = ls1021a_pcie_exit_from_l2,
};

static const struct ls_pcie_drvdata layerscape_drvdata = {
.pf_off = 0xc0000,
.pm_support = true,
+ .ops = &ls_pcie_host_ops;
.exit_from_l2 = ls_pcie_exit_from_l2,
};

@@ -205,6 +267,8 @@ static int ls_pcie_probe(struct platform_device *pdev)
struct dw_pcie *pci;
struct ls_pcie *pcie;
struct resource *dbi_base;
+ u32 index[2];
+ int ret;

pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
@@ -220,6 +284,7 @@ static int ls_pcie_probe(struct platform_device *pdev)
pci->pp.ops = &ls_pcie_host_ops;

pcie->pci = pci;
+ pci->pp.ops = pcie->drvdata->ops;

dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
@@ -230,6 +295,20 @@ static int ls_pcie_probe(struct platform_device *pdev)

pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off;

+ if (pcie->drvdata->scfg_support) {
+ pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg");
+ if (IS_ERR(pcie->scfg)) {
+ dev_err(dev, "No syscfg phandle specified\n");
+ return PTR_ERR(pcie->scfg);
+ }
+
+ ret = of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2);
+ if (ret)
+ return ret;
+
+ pcie->index = index[1];
+ }
+
if (!ls_pcie_is_bridge(pcie))
return -ENODEV;

--
2.34.1

2023-12-01 16:19:05

by Frank Li

[permalink] [raw]
Subject: [PATCH v5 3/4] PCI: layerscape(ep): Rename pf_* as pf_lut_*

'pf' and 'lut' is just difference name in difference chips, but basic it is
a MMIO base address plus an offset.

Rename it to avoid duplicate pf_* and lut_* in driver.

Signed-off-by: Frank Li <[email protected]>
---

Notes:
pf_lut is better than pf_* or lut* because some chip use 'pf', some chip
use 'lut'.

Change from v4 to v5
- rename layerscape-ep code also
change from v1 to v4
- new patch at v3

.../pci/controller/dwc/pci-layerscape-ep.c | 16 ++++-----
drivers/pci/controller/dwc/pci-layerscape.c | 36 +++++++++----------
2 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index 3d3c50ef4b6ff..2ca339f938a86 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -49,7 +49,7 @@ struct ls_pcie_ep {
bool big_endian;
};

-static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
+static u32 ls_pcie_pf_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
{
struct dw_pcie *pci = pcie->pci;

@@ -59,7 +59,7 @@ static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
return ioread32(pci->dbi_base + offset);
}

-static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
+static void ls_pcie_pf_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
{
struct dw_pcie *pci = pcie->pci;

@@ -76,8 +76,8 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
u32 val, cfg;
u8 offset;

- val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR);
- ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
+ val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR);
+ ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);

if (!val)
return IRQ_NONE;
@@ -96,9 +96,9 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
dw_pcie_dbi_ro_wr_dis(pci);

- cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG);
+ cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG);
cfg |= PEX_PF0_CFG_READY;
- ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
+ ls_pcie_pf_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
dw_pcie_ep_linkup(&pci->ep);

dev_dbg(pci->dev, "Link up\n");
@@ -130,10 +130,10 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
}

/* Enable interrupts */
- val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER);
+ val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_IER);
val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE |
PEX_PF0_PME_MES_IER_LUDIE;
- ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);
+ ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);

return 0;
}
diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
index 8bdaae9be7d56..a9151e98fde6f 100644
--- a/drivers/pci/controller/dwc/pci-layerscape.c
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -44,7 +44,7 @@
#define PCIE_IATU_NUM 6

struct ls_pcie_drvdata {
- const u32 pf_off;
+ const u32 pf_lut_off;
const struct dw_pcie_host_ops *ops;
int (*exit_from_l2)(struct dw_pcie_rp *pp);
bool scfg_support;
@@ -54,13 +54,13 @@ struct ls_pcie_drvdata {
struct ls_pcie {
struct dw_pcie *pci;
const struct ls_pcie_drvdata *drvdata;
- void __iomem *pf_base;
+ void __iomem *pf_lut_base;
struct regmap *scfg;
int index;
bool big_endian;
};

-#define ls_pcie_pf_readl_addr(addr) ls_pcie_pf_readl(pcie, addr)
+#define ls_pcie_pf_lut_readl_addr(addr) ls_pcie_pf_lut_readl(pcie, addr)
#define to_ls_pcie(x) dev_get_drvdata((x)->dev)

static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
@@ -101,20 +101,20 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR);
}

-static u32 ls_pcie_pf_readl(struct ls_pcie *pcie, u32 off)
+static u32 ls_pcie_pf_lut_readl(struct ls_pcie *pcie, u32 off)
{
if (pcie->big_endian)
- return ioread32be(pcie->pf_base + off);
+ return ioread32be(pcie->pf_lut_base + off);

- return ioread32(pcie->pf_base + off);
+ return ioread32(pcie->pf_lut_base + off);
}

-static void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val)
+static void ls_pcie_pf_lut_writel(struct ls_pcie *pcie, u32 off, u32 val)
{
if (pcie->big_endian)
- iowrite32be(val, pcie->pf_base + off);
+ iowrite32be(val, pcie->pf_lut_base + off);
else
- iowrite32(val, pcie->pf_base + off);
+ iowrite32(val, pcie->pf_lut_base + off);
}

static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
@@ -124,11 +124,11 @@ static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
u32 val;
int ret;

- val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR);
val |= PF_MCR_PTOMR;
- ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val);

- ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
+ ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR,
val, !(val & PF_MCR_PTOMR),
PCIE_PME_TO_L2_TIMEOUT_US/10,
PCIE_PME_TO_L2_TIMEOUT_US);
@@ -147,15 +147,15 @@ static int ls_pcie_exit_from_l2(struct dw_pcie_rp *pp)
* Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link
* to exit L2 state.
*/
- val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR);
val |= PF_MCR_EXL2S;
- ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val);

/*
* L2 exit timeout of 10ms is not defined in the specifications,
* it was chosen based on empirical observations.
*/
- ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
+ ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR,
val, !(val & PF_MCR_EXL2S),
1000,
10000);
@@ -242,9 +242,9 @@ static const struct ls_pcie_drvdata ls1021a_drvdata = {
};

static const struct ls_pcie_drvdata layerscape_drvdata = {
- .pf_off = 0xc0000,
+ .pf_lut_off = 0xc0000,
.pm_support = true,
- .ops = &ls_pcie_host_ops;
+ .ops = &ls_pcie_host_ops,
.exit_from_l2 = ls_pcie_exit_from_l2,
};

@@ -293,7 +293,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->pf_lut_base = pci->dbi_base + pcie->drvdata->pf_lut_off;

if (pcie->drvdata->scfg_support) {
pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg");
--
2.34.1

2023-12-01 16:19:18

by Frank Li

[permalink] [raw]
Subject: [PATCH v5 4/4] PCI: layerscape: Add suspend/resume for ls1043a

Add suspend/resume support for Layerscape LS1043a.

In the suspend path, PME_Turn_Off message is sent to the endpoint to
transition the link to L2/L3_Ready state. In this SoC, there is no way to
check if the controller has received the PME_To_Ack from the endpoint or
not. So to be on the safer side, the driver just waits for
PCIE_PME_TO_L2_TIMEOUT_US before asserting the SoC specific PMXMTTURNOFF
bit to complete the PME_Turn_Off handshake. Then the link would enter L2/L3
state depending on the VAUX supply.

In the resume path, the link is brought back from L2 to L0 by doing a
software reset.

Signed-off-by: Frank Li <[email protected]>
---

Notes:
Change from v4 to v5
- update commit message
- use comments
/* Reset the PEX wrapper to bring the link out of L2 */

Change from v3 to v4
- Call scfg_pcie_send_turnoff_msg() shared with ls1021a
- update commit message

Change from v2 to v3
- Remove ls_pcie_lut_readl(writel) function

Change from v1 to v2
- Update subject 'a' to 'A'

drivers/pci/controller/dwc/pci-layerscape.c | 63 ++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
index a9151e98fde6f..715365e91f8ef 100644
--- a/drivers/pci/controller/dwc/pci-layerscape.c
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -41,6 +41,15 @@
#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 {
@@ -224,6 +233,45 @@ static int ls1021a_pcie_exit_from_l2(struct dw_pcie_rp *pp)
return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index));
}

+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);
+
+ scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMECR, PEXPME(pcie->index));
+}
+
+static int 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;
+
+ /*
+ * Reset the PEX wrapper to bring the link out of L2.
+ * LDBG_WE: allows the user to have write access to the PEXDBG[SR] for both setting and
+ * clearing the soft reset on the PEX module.
+ * LDBG_SR: When SR is set to 1, the PEX module enters soft reset.
+ */
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
+ val |= LDBG_WE;
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
+
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
+ val |= LDBG_SR;
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
+
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
+ val &= ~LDBG_SR;
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
+
+ val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
+ val &= ~LDBG_WE;
+ ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
+
+ return 0;
+}
+
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,
@@ -241,6 +289,19 @@ 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 = ls_pcie_host_init,
+ .pme_turn_off = ls1043a_pcie_send_turnoff_msg,
+};
+
+static const struct ls_pcie_drvdata ls1043a_drvdata = {
+ .pf_lut_off = 0x10000,
+ .pm_support = true,
+ .scfg_support = true,
+ .ops = &ls1043a_pcie_host_ops,
+ .exit_from_l2 = ls1043a_pcie_exit_from_l2,
+};
+
static const struct ls_pcie_drvdata layerscape_drvdata = {
.pf_lut_off = 0xc0000,
.pm_support = true,
@@ -252,7 +313,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 },
--
2.34.1

2023-12-01 16:56:18

by Roy Zang

[permalink] [raw]
Subject: RE: [PATCH v5 1/4] PCI: layerscape: Add function pointer for exit_from_l2()

> From: Frank Li <[email protected]>
> Subject: [PATCH v5 1/4] PCI: layerscape: Add function pointer for exit_from_l2()
>
> Since difference SoCs require different sequence for exiting L2, let's add a
> separate "exit_from_l2()" callback. This callback can be used to execute SoC
> specific sequence.
>
> Change ls_pcie_exit_from_l2() return value from void to int. Return error if
> exit_from_l2() failure at exit resume flow.
>
> Reviewed-by: Manivannan Sadhasivam <[email protected]>
> Signed-off-by: Frank Li <[email protected]>
Acked-by: Roy Zang <[email protected]>
Roy

2023-12-01 16:58:05

by Roy Zang

[permalink] [raw]
Subject: RE: [PATCH v5 2/4] PCI: layerscape: Add suspend/resume for ls1021a

> From: Frank Li <[email protected]>
> Subject: [PATCH v5 2/4] PCI: layerscape: Add suspend/resume for ls1021a
>
> Add suspend/resume support for Layerscape LS1021a.
>
> In the suspend path, PME_Turn_Off message is sent to the endpoint to
> transition the link to L2/L3_Ready state. In this SoC, there is no way to check if
> the controller has received the PME_To_Ack from the endpoint or not. So to be
> on the safer side, the driver just waits for PCIE_PME_TO_L2_TIMEOUT_US
> before asserting the SoC specific PMXMTTURNOFF bit to complete the
> PME_Turn_Off handshake. Then the link would enter L2/L3 state depending on
> the VAUX supply.
>
> In the resume path, the link is brought back from L2 to L0 by doing a software
> reset.
>
> Signed-off-by: Frank Li <[email protected]>
Acked-by: Roy Zang <[email protected]>
Roy

2023-12-01 16:58:47

by Roy Zang

[permalink] [raw]
Subject: RE: [PATCH v5 3/4] PCI: layerscape(ep): Rename pf_* as pf_lut_*

> From: Frank Li <[email protected]>
> Subject: [PATCH v5 3/4] PCI: layerscape(ep): Rename pf_* as pf_lut_*
>
> 'pf' and 'lut' is just difference name in difference chips, but basic it is a MMIO
> base address plus an offset.
>
> Rename it to avoid duplicate pf_* and lut_* in driver.
>
> Signed-off-by: Frank Li <[email protected]>
Acked-by: Roy Zang <[email protected]>
Roy

2023-12-01 17:01:03

by Roy Zang

[permalink] [raw]
Subject: RE: [PATCH v5 4/4] PCI: layerscape: Add suspend/resume for ls1043a

> From: Frank Li <[email protected]>
> Subject: [PATCH v5 4/4] PCI: layerscape: Add suspend/resume for ls1043a
>
> Add suspend/resume support for Layerscape LS1043a.
>
> In the suspend path, PME_Turn_Off message is sent to the endpoint to
> transition the link to L2/L3_Ready state. In this SoC, there is no way to check if
> the controller has received the PME_To_Ack from the endpoint or not. So to be
> on the safer side, the driver just waits for PCIE_PME_TO_L2_TIMEOUT_US
> before asserting the SoC specific PMXMTTURNOFF bit to complete the
> PME_Turn_Off handshake. Then the link would enter L2/L3 state depending on
> the VAUX supply.
>
> In the resume path, the link is brought back from L2 to L0 by doing a software
> reset.
>
> Signed-off-by: Frank Li <[email protected]>
Acked-by: Roy Zang <[email protected]>
Roy

2023-12-04 10:59:57

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v5 2/4] PCI: layerscape: Add suspend/resume for ls1021a

On Fri, Dec 01, 2023 at 11:17:10AM -0500, Frank Li wrote:
> Add suspend/resume support for Layerscape LS1021a.
>
> In the suspend path, PME_Turn_Off message is sent to the endpoint to
> transition the link to L2/L3_Ready state. In this SoC, there is no way to
> check if the controller has received the PME_To_Ack from the endpoint or
> not. So to be on the safer side, the driver just waits for
> PCIE_PME_TO_L2_TIMEOUT_US before asserting the SoC specific PMXMTTURNOFF
> bit to complete the PME_Turn_Off handshake. Then the link would enter L2/L3
> state depending on the VAUX supply.
>
> In the resume path, the link is brought back from L2 to L0 by doing a
> software reset.
>
> Signed-off-by: Frank Li <[email protected]>

One comment below. With that addressed,

Reviewed-by: Manivannan Sadhasivam <[email protected]>

> ---
>
> Notes:
> Change from v4 to v5
> - update comit message
> - remove a empty line
> - use comments
> /* Reset the PEX wrapper to bring the link out of L2 */
> - pci->pp.ops = pcie->drvdata->ops,
> ls_pcie_host_ops to the "ops" member of layerscape_drvdata.
> - don't set pcie->scfg = NULL at error path
>
> Change from v3 to v4
> - update commit message.
> - it is reset a glue logic part for PCI controller.
> - use regmap_write_bits() to reduce code change.
>
> Change from v2 to v3
> - update according to mani's feedback
> change from v1 to v2
> - change subject 'a' to 'A'
>
> drivers/pci/controller/dwc/pci-layerscape.c | 81 ++++++++++++++++++++-
> 1 file changed, 80 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
> index aea89926bcc4f..8bdaae9be7d56 100644
> --- a/drivers/pci/controller/dwc/pci-layerscape.c
> +++ b/drivers/pci/controller/dwc/pci-layerscape.c
> @@ -35,11 +35,19 @@
> #define PF_MCR_PTOMR BIT(0)
> #define PF_MCR_EXL2S BIT(1)
>
> +/* LS1021A PEXn PM Write Control Register */
> +#define SCFG_PEXPMWRCR(idx) (0x5c + (idx) * 0x64)
> +#define PMXMTTURNOFF BIT(31)
> +#define SCFG_PEXSFTRSTCR 0x190
> +#define PEXSR(idx) BIT(idx)
> +
> #define PCIE_IATU_NUM 6
>
> struct ls_pcie_drvdata {
> const u32 pf_off;
> + const struct dw_pcie_host_ops *ops;
> int (*exit_from_l2)(struct dw_pcie_rp *pp);
> + bool scfg_support;
> bool pm_support;
> };
>
> @@ -47,6 +55,8 @@ struct ls_pcie {
> struct dw_pcie *pci;
> const struct ls_pcie_drvdata *drvdata;
> void __iomem *pf_base;
> + struct regmap *scfg;
> + int index;
> bool big_endian;
> };
>
> @@ -171,18 +181,70 @@ static int ls_pcie_host_init(struct dw_pcie_rp *pp)
> return 0;
> }
>
> +static void scfg_pcie_send_turnoff_msg(struct regmap *scfg, u32 reg, u32 mask)
> +{
> + /* Send PME_Turn_Off message */
> + regmap_write_bits(scfg, reg, mask, mask);
> +
> + /*
> + * There is no specific register to check for PME_To_Ack from endpoint.
> + * So on the safe side, wait for PCIE_PME_TO_L2_TIMEOUT_US.
> + */
> + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000);
> +
> + /*
> + * Layerscape hardware reference manual recommends clearing the PMXMTTURNOFF bit
> + * to complete the PME_Turn_Off handshake.
> + */
> + regmap_write_bits(scfg, reg, mask, 0);
> +}
> +
> +static void ls1021a_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);
> +
> + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), PMXMTTURNOFF);
> +}
> +
> +static int scfg_pcie_exit_from_l2(struct regmap *scfg, u32 reg, u32 mask)
> +{
> + /* Reset the PEX wrapper to bring the link out of L2 */
> + regmap_write_bits(scfg, reg, mask, mask);
> + regmap_write_bits(scfg, reg, mask, 0);
> +
> + return 0;
> +}
> +
> +static int ls1021a_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);
> +
> + return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index));
> +}
> +
> 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,
> };
>
> +static const struct dw_pcie_host_ops ls1021a_pcie_host_ops = {
> + .host_init = ls_pcie_host_init,
> + .pme_turn_off = ls1021a_pcie_send_turnoff_msg,
> +};
> +
> static const struct ls_pcie_drvdata ls1021a_drvdata = {
> - .pm_support = false,
> + .pm_support = true,
> + .scfg_support = true,
> + .ops = &ls1021a_pcie_host_ops,
> + .exit_from_l2 = ls1021a_pcie_exit_from_l2,
> };
>
> static const struct ls_pcie_drvdata layerscape_drvdata = {
> .pf_off = 0xc0000,
> .pm_support = true,
> + .ops = &ls_pcie_host_ops;
> .exit_from_l2 = ls_pcie_exit_from_l2,
> };
>
> @@ -205,6 +267,8 @@ static int ls_pcie_probe(struct platform_device *pdev)
> struct dw_pcie *pci;
> struct ls_pcie *pcie;
> struct resource *dbi_base;
> + u32 index[2];
> + int ret;
>
> pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> if (!pcie)
> @@ -220,6 +284,7 @@ static int ls_pcie_probe(struct platform_device *pdev)
> pci->pp.ops = &ls_pcie_host_ops;

This should be removed now.

- Mani

>
> pcie->pci = pci;
> + pci->pp.ops = pcie->drvdata->ops;
>
> dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
> @@ -230,6 +295,20 @@ static int ls_pcie_probe(struct platform_device *pdev)
>
> pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off;
>
> + if (pcie->drvdata->scfg_support) {
> + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg");
> + if (IS_ERR(pcie->scfg)) {
> + dev_err(dev, "No syscfg phandle specified\n");
> + return PTR_ERR(pcie->scfg);
> + }
> +
> + ret = of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2);
> + if (ret)
> + return ret;
> +
> + pcie->index = index[1];
> + }
> +
> if (!ls_pcie_is_bridge(pcie))
> return -ENODEV;
>
> --
> 2.34.1
>

--
மணிவண்ணன் சதாசிவம்

2023-12-04 11:02:50

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v5 3/4] PCI: layerscape(ep): Rename pf_* as pf_lut_*

On Fri, Dec 01, 2023 at 11:17:11AM -0500, Frank Li wrote:
> 'pf' and 'lut' is just difference name in difference chips, but basic it is
> a MMIO base address plus an offset.
>
> Rename it to avoid duplicate pf_* and lut_* in driver.
>
> Signed-off-by: Frank Li <[email protected]>

One comment below. With that addressed,

Reviewed-by: Manivannan Sadhasivam <[email protected]>

> ---
>
> Notes:
> pf_lut is better than pf_* or lut* because some chip use 'pf', some chip
> use 'lut'.
>
> Change from v4 to v5
> - rename layerscape-ep code also
> change from v1 to v4
> - new patch at v3
>
> .../pci/controller/dwc/pci-layerscape-ep.c | 16 ++++-----
> drivers/pci/controller/dwc/pci-layerscape.c | 36 +++++++++----------
> 2 files changed, 26 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> index 3d3c50ef4b6ff..2ca339f938a86 100644
> --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> @@ -49,7 +49,7 @@ struct ls_pcie_ep {
> bool big_endian;
> };
>
> -static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
> +static u32 ls_pcie_pf_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
> {
> struct dw_pcie *pci = pcie->pci;
>
> @@ -59,7 +59,7 @@ static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
> return ioread32(pci->dbi_base + offset);
> }
>
> -static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
> +static void ls_pcie_pf_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
> {
> struct dw_pcie *pci = pcie->pci;
>
> @@ -76,8 +76,8 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
> u32 val, cfg;
> u8 offset;
>
> - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR);
> - ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
> + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR);
> + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
>
> if (!val)
> return IRQ_NONE;
> @@ -96,9 +96,9 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
> dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
> dw_pcie_dbi_ro_wr_dis(pci);
>
> - cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG);
> + cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG);
> cfg |= PEX_PF0_CFG_READY;
> - ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
> + ls_pcie_pf_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
> dw_pcie_ep_linkup(&pci->ep);
>
> dev_dbg(pci->dev, "Link up\n");
> @@ -130,10 +130,10 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> }
>
> /* Enable interrupts */
> - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER);
> + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_IER);
> val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE |
> PEX_PF0_PME_MES_IER_LUDIE;
> - ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);
> + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);
>
> return 0;
> }
> diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
> index 8bdaae9be7d56..a9151e98fde6f 100644
> --- a/drivers/pci/controller/dwc/pci-layerscape.c
> +++ b/drivers/pci/controller/dwc/pci-layerscape.c
> @@ -44,7 +44,7 @@
> #define PCIE_IATU_NUM 6
>
> struct ls_pcie_drvdata {
> - const u32 pf_off;
> + const u32 pf_lut_off;
> const struct dw_pcie_host_ops *ops;
> int (*exit_from_l2)(struct dw_pcie_rp *pp);
> bool scfg_support;
> @@ -54,13 +54,13 @@ struct ls_pcie_drvdata {
> struct ls_pcie {
> struct dw_pcie *pci;
> const struct ls_pcie_drvdata *drvdata;
> - void __iomem *pf_base;
> + void __iomem *pf_lut_base;
> struct regmap *scfg;
> int index;
> bool big_endian;
> };
>
> -#define ls_pcie_pf_readl_addr(addr) ls_pcie_pf_readl(pcie, addr)
> +#define ls_pcie_pf_lut_readl_addr(addr) ls_pcie_pf_lut_readl(pcie, addr)
> #define to_ls_pcie(x) dev_get_drvdata((x)->dev)
>
> static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
> @@ -101,20 +101,20 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
> iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR);
> }
>
> -static u32 ls_pcie_pf_readl(struct ls_pcie *pcie, u32 off)
> +static u32 ls_pcie_pf_lut_readl(struct ls_pcie *pcie, u32 off)
> {
> if (pcie->big_endian)
> - return ioread32be(pcie->pf_base + off);
> + return ioread32be(pcie->pf_lut_base + off);
>
> - return ioread32(pcie->pf_base + off);
> + return ioread32(pcie->pf_lut_base + off);
> }
>
> -static void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val)
> +static void ls_pcie_pf_lut_writel(struct ls_pcie *pcie, u32 off, u32 val)
> {
> if (pcie->big_endian)
> - iowrite32be(val, pcie->pf_base + off);
> + iowrite32be(val, pcie->pf_lut_base + off);
> else
> - iowrite32(val, pcie->pf_base + off);
> + iowrite32(val, pcie->pf_lut_base + off);
> }
>
> static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
> @@ -124,11 +124,11 @@ static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
> u32 val;
> int ret;
>
> - val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR);
> val |= PF_MCR_PTOMR;
> - ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val);
>
> - ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
> + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR,
> val, !(val & PF_MCR_PTOMR),
> PCIE_PME_TO_L2_TIMEOUT_US/10,
> PCIE_PME_TO_L2_TIMEOUT_US);
> @@ -147,15 +147,15 @@ static int ls_pcie_exit_from_l2(struct dw_pcie_rp *pp)
> * Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link
> * to exit L2 state.
> */
> - val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR);
> val |= PF_MCR_EXL2S;
> - ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val);
>
> /*
> * L2 exit timeout of 10ms is not defined in the specifications,
> * it was chosen based on empirical observations.
> */
> - ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
> + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR,
> val, !(val & PF_MCR_EXL2S),
> 1000,
> 10000);
> @@ -242,9 +242,9 @@ static const struct ls_pcie_drvdata ls1021a_drvdata = {
> };
>
> static const struct ls_pcie_drvdata layerscape_drvdata = {
> - .pf_off = 0xc0000,
> + .pf_lut_off = 0xc0000,
> .pm_support = true,
> - .ops = &ls_pcie_host_ops;
> + .ops = &ls_pcie_host_ops,

This change should be part of the previous patch.

- Mani

> .exit_from_l2 = ls_pcie_exit_from_l2,
> };
>
> @@ -293,7 +293,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->pf_lut_base = pci->dbi_base + pcie->drvdata->pf_lut_off;
>
> if (pcie->drvdata->scfg_support) {
> pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg");
> --
> 2.34.1
>

--
மணிவண்ணன் சதாசிவம்

2023-12-04 11:04:09

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v5 4/4] PCI: layerscape: Add suspend/resume for ls1043a

On Fri, Dec 01, 2023 at 11:17:12AM -0500, Frank Li wrote:
> Add suspend/resume support for Layerscape LS1043a.
>
> In the suspend path, PME_Turn_Off message is sent to the endpoint to
> transition the link to L2/L3_Ready state. In this SoC, there is no way to
> check if the controller has received the PME_To_Ack from the endpoint or
> not. So to be on the safer side, the driver just waits for
> PCIE_PME_TO_L2_TIMEOUT_US before asserting the SoC specific PMXMTTURNOFF
> bit to complete the PME_Turn_Off handshake. Then the link would enter L2/L3
> state depending on the VAUX supply.
>
> In the resume path, the link is brought back from L2 to L0 by doing a
> software reset.
>
> Signed-off-by: Frank Li <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

- Mani

> ---
>
> Notes:
> Change from v4 to v5
> - update commit message
> - use comments
> /* Reset the PEX wrapper to bring the link out of L2 */
>
> Change from v3 to v4
> - Call scfg_pcie_send_turnoff_msg() shared with ls1021a
> - update commit message
>
> Change from v2 to v3
> - Remove ls_pcie_lut_readl(writel) function
>
> Change from v1 to v2
> - Update subject 'a' to 'A'
>
> drivers/pci/controller/dwc/pci-layerscape.c | 63 ++++++++++++++++++++-
> 1 file changed, 62 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
> index a9151e98fde6f..715365e91f8ef 100644
> --- a/drivers/pci/controller/dwc/pci-layerscape.c
> +++ b/drivers/pci/controller/dwc/pci-layerscape.c
> @@ -41,6 +41,15 @@
> #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 {
> @@ -224,6 +233,45 @@ static int ls1021a_pcie_exit_from_l2(struct dw_pcie_rp *pp)
> return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index));
> }
>
> +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);
> +
> + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMECR, PEXPME(pcie->index));
> +}
> +
> +static int 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;
> +
> + /*
> + * Reset the PEX wrapper to bring the link out of L2.
> + * LDBG_WE: allows the user to have write access to the PEXDBG[SR] for both setting and
> + * clearing the soft reset on the PEX module.
> + * LDBG_SR: When SR is set to 1, the PEX module enters soft reset.
> + */
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
> + val |= LDBG_WE;
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
> +
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
> + val |= LDBG_SR;
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
> +
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
> + val &= ~LDBG_SR;
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
> +
> + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG);
> + val &= ~LDBG_WE;
> + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val);
> +
> + return 0;
> +}
> +
> 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,
> @@ -241,6 +289,19 @@ 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 = ls_pcie_host_init,
> + .pme_turn_off = ls1043a_pcie_send_turnoff_msg,
> +};
> +
> +static const struct ls_pcie_drvdata ls1043a_drvdata = {
> + .pf_lut_off = 0x10000,
> + .pm_support = true,
> + .scfg_support = true,
> + .ops = &ls1043a_pcie_host_ops,
> + .exit_from_l2 = ls1043a_pcie_exit_from_l2,
> +};
> +
> static const struct ls_pcie_drvdata layerscape_drvdata = {
> .pf_lut_off = 0xc0000,
> .pm_support = true,
> @@ -252,7 +313,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 },
> --
> 2.34.1
>

--
மணிவண்ணன் சதாசிவம்