Skip to content

Commit 9fda4d0

Browse files
Zhiqiang-HouLorenzo Pieralisi
authored andcommitted
PCI: layerscape: Add power management support for ls1028a
Add PME_Turn_off/PME_TO_Ack handshake sequence for ls1028a platform. Implemented on top of common dwc dw_pcie_suspend(resume)_noirq() functions to handle system enter/exit suspend states. Link: https://lore.kernel.org/r/20230821184815.2167131-4-Frank.Li@nxp.com Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Acked-by: Manivannan Sadhasivam <mani@kernel.org>
1 parent 4774faf commit 9fda4d0

File tree

1 file changed

+131
-9
lines changed

1 file changed

+131
-9
lines changed

drivers/pci/controller/dwc/pci-layerscape.c

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
* Author: Minghuan Lian <Minghuan.Lian@freescale.com>
99
*/
1010

11+
#include <linux/delay.h>
1112
#include <linux/kernel.h>
1213
#include <linux/interrupt.h>
1314
#include <linux/init.h>
15+
#include <linux/iopoll.h>
1416
#include <linux/of_pci.h>
1517
#include <linux/of_platform.h>
1618
#include <linux/of_address.h>
@@ -20,19 +22,34 @@
2022
#include <linux/mfd/syscon.h>
2123
#include <linux/regmap.h>
2224

25+
#include "../../pci.h"
2326
#include "pcie-designware.h"
2427

2528
/* PEX Internal Configuration Registers */
2629
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
2730
#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */
2831
#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */
2932

33+
/* PF Message Command Register */
34+
#define LS_PCIE_PF_MCR 0x2c
35+
#define PF_MCR_PTOMR BIT(0)
36+
#define PF_MCR_EXL2S BIT(1)
37+
3038
#define PCIE_IATU_NUM 6
3139

40+
struct ls_pcie_drvdata {
41+
const u32 pf_off;
42+
bool pm_support;
43+
};
44+
3245
struct ls_pcie {
3346
struct dw_pcie *pci;
47+
const struct ls_pcie_drvdata *drvdata;
48+
void __iomem *pf_base;
49+
bool big_endian;
3450
};
3551

52+
#define ls_pcie_pf_readl_addr(addr) ls_pcie_pf_readl(pcie, addr)
3653
#define to_ls_pcie(x) dev_get_drvdata((x)->dev)
3754

3855
static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
@@ -73,6 +90,68 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
7390
iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR);
7491
}
7592

93+
static u32 ls_pcie_pf_readl(struct ls_pcie *pcie, u32 off)
94+
{
95+
if (pcie->big_endian)
96+
return ioread32be(pcie->pf_base + off);
97+
98+
return ioread32(pcie->pf_base + off);
99+
}
100+
101+
static void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val)
102+
{
103+
if (pcie->big_endian)
104+
iowrite32be(val, pcie->pf_base + off);
105+
else
106+
iowrite32(val, pcie->pf_base + off);
107+
}
108+
109+
static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp)
110+
{
111+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
112+
struct ls_pcie *pcie = to_ls_pcie(pci);
113+
u32 val;
114+
int ret;
115+
116+
val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
117+
val |= PF_MCR_PTOMR;
118+
ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
119+
120+
ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
121+
val, !(val & PF_MCR_PTOMR),
122+
PCIE_PME_TO_L2_TIMEOUT_US/10,
123+
PCIE_PME_TO_L2_TIMEOUT_US);
124+
if (ret)
125+
dev_err(pcie->pci->dev, "PME_Turn_off timeout\n");
126+
}
127+
128+
static void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp)
129+
{
130+
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
131+
struct ls_pcie *pcie = to_ls_pcie(pci);
132+
u32 val;
133+
int ret;
134+
135+
/*
136+
* Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link
137+
* to exit L2 state.
138+
*/
139+
val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR);
140+
val |= PF_MCR_EXL2S;
141+
ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val);
142+
143+
/*
144+
* L2 exit timeout of 10ms is not defined in the specifications,
145+
* it was chosen based on empirical observations.
146+
*/
147+
ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR,
148+
val, !(val & PF_MCR_EXL2S),
149+
1000,
150+
10000);
151+
if (ret)
152+
dev_err(pcie->pci->dev, "L2 exit timeout\n");
153+
}
154+
76155
static int ls_pcie_host_init(struct dw_pcie_rp *pp)
77156
{
78157
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -91,18 +170,28 @@ static int ls_pcie_host_init(struct dw_pcie_rp *pp)
91170

92171
static const struct dw_pcie_host_ops ls_pcie_host_ops = {
93172
.host_init = ls_pcie_host_init,
173+
.pme_turn_off = ls_pcie_send_turnoff_msg,
174+
};
175+
176+
static const struct ls_pcie_drvdata ls1021a_drvdata = {
177+
.pm_support = false,
178+
};
179+
180+
static const struct ls_pcie_drvdata layerscape_drvdata = {
181+
.pf_off = 0xc0000,
182+
.pm_support = true,
94183
};
95184

96185
static const struct of_device_id ls_pcie_of_match[] = {
97-
{ .compatible = "fsl,ls1012a-pcie", },
98-
{ .compatible = "fsl,ls1021a-pcie", },
99-
{ .compatible = "fsl,ls1028a-pcie", },
100-
{ .compatible = "fsl,ls1043a-pcie", },
101-
{ .compatible = "fsl,ls1046a-pcie", },
102-
{ .compatible = "fsl,ls2080a-pcie", },
103-
{ .compatible = "fsl,ls2085a-pcie", },
104-
{ .compatible = "fsl,ls2088a-pcie", },
105-
{ .compatible = "fsl,ls1088a-pcie", },
186+
{ .compatible = "fsl,ls1012a-pcie", .data = &layerscape_drvdata },
187+
{ .compatible = "fsl,ls1021a-pcie", .data = &ls1021a_drvdata },
188+
{ .compatible = "fsl,ls1028a-pcie", .data = &layerscape_drvdata },
189+
{ .compatible = "fsl,ls1043a-pcie", .data = &ls1021a_drvdata },
190+
{ .compatible = "fsl,ls1046a-pcie", .data = &layerscape_drvdata },
191+
{ .compatible = "fsl,ls2080a-pcie", .data = &layerscape_drvdata },
192+
{ .compatible = "fsl,ls2085a-pcie", .data = &layerscape_drvdata },
193+
{ .compatible = "fsl,ls2088a-pcie", .data = &layerscape_drvdata },
194+
{ .compatible = "fsl,ls1088a-pcie", .data = &layerscape_drvdata },
106195
{ },
107196
};
108197

@@ -121,6 +210,8 @@ static int ls_pcie_probe(struct platform_device *pdev)
121210
if (!pci)
122211
return -ENOMEM;
123212

213+
pcie->drvdata = of_device_get_match_data(dev);
214+
124215
pci->dev = dev;
125216
pci->pp.ops = &ls_pcie_host_ops;
126217

@@ -131,6 +222,10 @@ static int ls_pcie_probe(struct platform_device *pdev)
131222
if (IS_ERR(pci->dbi_base))
132223
return PTR_ERR(pci->dbi_base);
133224

225+
pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian");
226+
227+
pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off;
228+
134229
if (!ls_pcie_is_bridge(pcie))
135230
return -ENODEV;
136231

@@ -139,12 +234,39 @@ static int ls_pcie_probe(struct platform_device *pdev)
139234
return dw_pcie_host_init(&pci->pp);
140235
}
141236

237+
static int ls_pcie_suspend_noirq(struct device *dev)
238+
{
239+
struct ls_pcie *pcie = dev_get_drvdata(dev);
240+
241+
if (!pcie->drvdata->pm_support)
242+
return 0;
243+
244+
return dw_pcie_suspend_noirq(pcie->pci);
245+
}
246+
247+
static int ls_pcie_resume_noirq(struct device *dev)
248+
{
249+
struct ls_pcie *pcie = dev_get_drvdata(dev);
250+
251+
if (!pcie->drvdata->pm_support)
252+
return 0;
253+
254+
ls_pcie_exit_from_l2(&pcie->pci->pp);
255+
256+
return dw_pcie_resume_noirq(pcie->pci);
257+
}
258+
259+
static const struct dev_pm_ops ls_pcie_pm_ops = {
260+
NOIRQ_SYSTEM_SLEEP_PM_OPS(ls_pcie_suspend_noirq, ls_pcie_resume_noirq)
261+
};
262+
142263
static struct platform_driver ls_pcie_driver = {
143264
.probe = ls_pcie_probe,
144265
.driver = {
145266
.name = "layerscape-pcie",
146267
.of_match_table = ls_pcie_of_match,
147268
.suppress_bind_attrs = true,
269+
.pm = &ls_pcie_pm_ops,
148270
},
149271
};
150272
builtin_platform_driver(ls_pcie_driver);

0 commit comments

Comments
 (0)