8
8
* Author: Minghuan Lian <Minghuan.Lian@freescale.com>
9
9
*/
10
10
11
+ #include <linux/delay.h>
11
12
#include <linux/kernel.h>
12
13
#include <linux/interrupt.h>
13
14
#include <linux/init.h>
15
+ #include <linux/iopoll.h>
14
16
#include <linux/of_pci.h>
15
17
#include <linux/of_platform.h>
16
18
#include <linux/of_address.h>
20
22
#include <linux/mfd/syscon.h>
21
23
#include <linux/regmap.h>
22
24
25
+ #include "../../pci.h"
23
26
#include "pcie-designware.h"
24
27
25
28
/* PEX Internal Configuration Registers */
26
29
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
27
30
#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */
28
31
#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */
29
32
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
+
30
38
#define PCIE_IATU_NUM 6
31
39
40
+ struct ls_pcie_drvdata {
41
+ const u32 pf_off ;
42
+ bool pm_support ;
43
+ };
44
+
32
45
struct ls_pcie {
33
46
struct dw_pcie * pci ;
47
+ const struct ls_pcie_drvdata * drvdata ;
48
+ void __iomem * pf_base ;
49
+ bool big_endian ;
34
50
};
35
51
52
+ #define ls_pcie_pf_readl_addr (addr ) ls_pcie_pf_readl(pcie, addr)
36
53
#define to_ls_pcie (x ) dev_get_drvdata((x)->dev)
37
54
38
55
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)
73
90
iowrite32 (PCIE_ABSERR_SETTING , pci -> dbi_base + PCIE_ABSERR );
74
91
}
75
92
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
+
76
155
static int ls_pcie_host_init (struct dw_pcie_rp * pp )
77
156
{
78
157
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)
91
170
92
171
static const struct dw_pcie_host_ops ls_pcie_host_ops = {
93
172
.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,
94
183
};
95
184
96
185
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 },
106
195
{ },
107
196
};
108
197
@@ -121,6 +210,8 @@ static int ls_pcie_probe(struct platform_device *pdev)
121
210
if (!pci )
122
211
return - ENOMEM ;
123
212
213
+ pcie -> drvdata = of_device_get_match_data (dev );
214
+
124
215
pci -> dev = dev ;
125
216
pci -> pp .ops = & ls_pcie_host_ops ;
126
217
@@ -131,6 +222,10 @@ static int ls_pcie_probe(struct platform_device *pdev)
131
222
if (IS_ERR (pci -> dbi_base ))
132
223
return PTR_ERR (pci -> dbi_base );
133
224
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
+
134
229
if (!ls_pcie_is_bridge (pcie ))
135
230
return - ENODEV ;
136
231
@@ -139,12 +234,39 @@ static int ls_pcie_probe(struct platform_device *pdev)
139
234
return dw_pcie_host_init (& pci -> pp );
140
235
}
141
236
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
+
142
263
static struct platform_driver ls_pcie_driver = {
143
264
.probe = ls_pcie_probe ,
144
265
.driver = {
145
266
.name = "layerscape-pcie" ,
146
267
.of_match_table = ls_pcie_of_match ,
147
268
.suppress_bind_attrs = true,
269
+ .pm = & ls_pcie_pm_ops ,
148
270
},
149
271
};
150
272
builtin_platform_driver (ls_pcie_driver );
0 commit comments