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>
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+
3245struct 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
3855static 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+
76155static 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
92171static 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
96185static 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+
142263static 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};
150272builtin_platform_driver (ls_pcie_driver );
0 commit comments