Skip to content

Commit f38176d

Browse files
committed
Merge branch 'pci/controller/dwc'
- Add an imx6 .host_deinit() callback so we can clean up things like regulators on probe failure or driver unload (Mark Brown) - Add PCIE_PME_TO_L2_TIMEOUT_US value for controller drivers that need to manually synchronize power removal (Frank Li) - Add generic dwc suspend/resume APIs (dw_pcie_suspend_noirq() and dw_pcie_resume_noirq()) to be called by controller driver suspend/resume ops, and a controller callback to send PME_Turn_Off (Frank Li) - Add layerscape suspend/resume support, including manual PME_Turn_off/PME_TO_Ack handshake (Hou Zhiqiang, Frank Li) * pci/controller/dwc: PCI: layerscape: Add power management support for ls1028a PCI: dwc: Implement generic suspend/resume functionality PCI: Add PCIE_PME_TO_L2_TIMEOUT_US L2 ready timeout value PCI: dwc: Provide deinit callback for i.MX
2 parents 4b3f481 + 9fda4d0 commit f38176d

File tree

5 files changed

+237
-9
lines changed

5 files changed

+237
-9
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ static void imx6_pcie_host_exit(struct dw_pcie_rp *pp)
10401040

10411041
static const struct dw_pcie_host_ops imx6_pcie_host_ops = {
10421042
.host_init = imx6_pcie_host_init,
1043+
.host_deinit = imx6_pcie_host_exit,
10431044
};
10441045

10451046
static const struct dw_pcie_ops dw_pcie_ops = {

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);

drivers/pci/controller/dwc/pcie-designware-host.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Author: Jingoo Han <jg1.han@samsung.com>
99
*/
1010

11+
#include <linux/iopoll.h>
1112
#include <linux/irqchip/chained_irq.h>
1213
#include <linux/irqdomain.h>
1314
#include <linux/msi.h>
@@ -16,6 +17,7 @@
1617
#include <linux/pci_regs.h>
1718
#include <linux/platform_device.h>
1819

20+
#include "../../pci.h"
1921
#include "pcie-designware.h"
2022

2123
static struct pci_ops dw_pcie_ops;
@@ -812,3 +814,72 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
812814
return 0;
813815
}
814816
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
817+
818+
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
819+
{
820+
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
821+
u32 val;
822+
int ret;
823+
824+
/*
825+
* If L1SS is supported, then do not put the link into L2 as some
826+
* devices such as NVMe expect low resume latency.
827+
*/
828+
if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
829+
return 0;
830+
831+
if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
832+
return 0;
833+
834+
if (!pci->pp.ops->pme_turn_off)
835+
return 0;
836+
837+
pci->pp.ops->pme_turn_off(&pci->pp);
838+
839+
ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
840+
PCIE_PME_TO_L2_TIMEOUT_US/10,
841+
PCIE_PME_TO_L2_TIMEOUT_US, false, pci);
842+
if (ret) {
843+
dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val);
844+
return ret;
845+
}
846+
847+
if (pci->pp.ops->host_deinit)
848+
pci->pp.ops->host_deinit(&pci->pp);
849+
850+
pci->suspended = true;
851+
852+
return ret;
853+
}
854+
EXPORT_SYMBOL_GPL(dw_pcie_suspend_noirq);
855+
856+
int dw_pcie_resume_noirq(struct dw_pcie *pci)
857+
{
858+
int ret;
859+
860+
if (!pci->suspended)
861+
return 0;
862+
863+
pci->suspended = false;
864+
865+
if (pci->pp.ops->host_init) {
866+
ret = pci->pp.ops->host_init(&pci->pp);
867+
if (ret) {
868+
dev_err(pci->dev, "Host init failed: %d\n", ret);
869+
return ret;
870+
}
871+
}
872+
873+
dw_pcie_setup_rc(&pci->pp);
874+
875+
ret = dw_pcie_start_link(pci);
876+
if (ret)
877+
return ret;
878+
879+
ret = dw_pcie_wait_for_link(pci);
880+
if (ret)
881+
return ret;
882+
883+
return ret;
884+
}
885+
EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq);

drivers/pci/controller/dwc/pcie-designware.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,21 @@ enum dw_pcie_core_rst {
288288
DW_PCIE_NUM_CORE_RSTS
289289
};
290290

291+
enum dw_pcie_ltssm {
292+
/* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */
293+
DW_PCIE_LTSSM_DETECT_QUIET = 0x0,
294+
DW_PCIE_LTSSM_DETECT_ACT = 0x1,
295+
DW_PCIE_LTSSM_L0 = 0x11,
296+
DW_PCIE_LTSSM_L2_IDLE = 0x15,
297+
298+
DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF,
299+
};
300+
291301
struct dw_pcie_host_ops {
292302
int (*host_init)(struct dw_pcie_rp *pp);
293303
void (*host_deinit)(struct dw_pcie_rp *pp);
294304
int (*msi_host_init)(struct dw_pcie_rp *pp);
305+
void (*pme_turn_off)(struct dw_pcie_rp *pp);
295306
};
296307

297308
struct dw_pcie_rp {
@@ -364,6 +375,7 @@ struct dw_pcie_ops {
364375
void (*write_dbi2)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
365376
size_t size, u32 val);
366377
int (*link_up)(struct dw_pcie *pcie);
378+
enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie);
367379
int (*start_link)(struct dw_pcie *pcie);
368380
void (*stop_link)(struct dw_pcie *pcie);
369381
};
@@ -393,6 +405,7 @@ struct dw_pcie {
393405
struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS];
394406
struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS];
395407
struct gpio_desc *pe_rst;
408+
bool suspended;
396409
};
397410

398411
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -431,6 +444,9 @@ int dw_pcie_edma_detect(struct dw_pcie *pci);
431444
void dw_pcie_edma_remove(struct dw_pcie *pci);
432445
void dw_pcie_print_link_status(struct dw_pcie *pci);
433446

447+
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
448+
int dw_pcie_resume_noirq(struct dw_pcie *pci);
449+
434450
static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
435451
{
436452
dw_pcie_write_dbi(pci, reg, 0x4, val);
@@ -502,6 +518,18 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci)
502518
pci->ops->stop_link(pci);
503519
}
504520

521+
static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
522+
{
523+
u32 val;
524+
525+
if (pci->ops && pci->ops->get_ltssm)
526+
return pci->ops->get_ltssm(pci);
527+
528+
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
529+
530+
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val);
531+
}
532+
505533
#ifdef CONFIG_PCIE_DW_HOST
506534
irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
507535
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);

drivers/pci/pci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
#define PCIE_LINK_RETRAIN_TIMEOUT_MS 1000
1515

16+
/*
17+
* PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization>
18+
* Recommends 1ms to 10ms timeout to check L2 ready.
19+
*/
20+
#define PCIE_PME_TO_L2_TIMEOUT_US 10000
21+
1622
extern const unsigned char pcie_link_speed[];
1723
extern bool pci_early_dump;
1824

0 commit comments

Comments
 (0)