]>
Commit | Line | Data |
---|---|---|
736759ef | 1 | // SPDX-License-Identifier: GPL-2.0+ |
e77f847d SL |
2 | /* |
3 | * Rockchip AXI PCIe host controller driver | |
4 | * | |
5 | * Copyright (c) 2016 Rockchip, Inc. | |
6 | * | |
7 | * Author: Shawn Lin <[email protected]> | |
8 | * Wenrui Li <[email protected]> | |
9 | * | |
96291d56 | 10 | * Bits taken from Synopsys DesignWare Host controller driver and |
e77f847d | 11 | * ARM PCI Host generic driver. |
e77f847d SL |
12 | */ |
13 | ||
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/gpio/consumer.h> | |
e77f847d | 17 | #include <linux/of_pci.h> |
e77f847d SL |
18 | #include <linux/phy/phy.h> |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/reset.h> | |
e77f847d | 21 | |
9e2aee80 | 22 | #include "../pci.h" |
956cd99b | 23 | #include "pcie-rockchip.h" |
9e2aee80 | 24 | |
964bac94 | 25 | int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) |
e77f847d | 26 | { |
964bac94 SL |
27 | struct device *dev = rockchip->dev; |
28 | struct platform_device *pdev = to_platform_device(dev); | |
29 | struct device_node *node = dev->of_node; | |
30 | struct resource *regs; | |
31 | int err; | |
e77f847d | 32 | |
cf590b07 SL |
33 | if (rockchip->is_rc) { |
34 | regs = platform_get_resource_byname(pdev, | |
35 | IORESOURCE_MEM, | |
36 | "axi-base"); | |
37 | rockchip->reg_base = devm_pci_remap_cfg_resource(dev, regs); | |
38 | if (IS_ERR(rockchip->reg_base)) | |
39 | return PTR_ERR(rockchip->reg_base); | |
e77f847d | 40 | } else { |
cf590b07 SL |
41 | rockchip->mem_res = |
42 | platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
43 | "mem-base"); | |
44 | if (!rockchip->mem_res) | |
45 | return -EINVAL; | |
e77f847d | 46 | } |
e77f847d | 47 | |
cf590b07 | 48 | regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
964bac94 SL |
49 | "apb-base"); |
50 | rockchip->apb_base = devm_ioremap_resource(dev, regs); | |
51 | if (IS_ERR(rockchip->apb_base)) | |
52 | return PTR_ERR(rockchip->apb_base); | |
e77f847d | 53 | |
964bac94 SL |
54 | err = rockchip_pcie_get_phys(rockchip); |
55 | if (err) | |
56 | return err; | |
e77f847d | 57 | |
964bac94 SL |
58 | rockchip->lanes = 1; |
59 | err = of_property_read_u32(node, "num-lanes", &rockchip->lanes); | |
60 | if (!err && (rockchip->lanes == 0 || | |
61 | rockchip->lanes == 3 || | |
62 | rockchip->lanes > 4)) { | |
63 | dev_warn(dev, "invalid num-lanes, default to use one lane\n"); | |
64 | rockchip->lanes = 1; | |
e77f847d SL |
65 | } |
66 | ||
964bac94 SL |
67 | rockchip->link_gen = of_pci_get_max_link_speed(node); |
68 | if (rockchip->link_gen < 0 || rockchip->link_gen > 2) | |
69 | rockchip->link_gen = 2; | |
e77f847d | 70 | |
964bac94 SL |
71 | rockchip->core_rst = devm_reset_control_get_exclusive(dev, "core"); |
72 | if (IS_ERR(rockchip->core_rst)) { | |
73 | if (PTR_ERR(rockchip->core_rst) != -EPROBE_DEFER) | |
74 | dev_err(dev, "missing core reset property in node\n"); | |
75 | return PTR_ERR(rockchip->core_rst); | |
e77f847d SL |
76 | } |
77 | ||
964bac94 SL |
78 | rockchip->mgmt_rst = devm_reset_control_get_exclusive(dev, "mgmt"); |
79 | if (IS_ERR(rockchip->mgmt_rst)) { | |
80 | if (PTR_ERR(rockchip->mgmt_rst) != -EPROBE_DEFER) | |
81 | dev_err(dev, "missing mgmt reset property in node\n"); | |
82 | return PTR_ERR(rockchip->mgmt_rst); | |
e77f847d | 83 | } |
e77f847d | 84 | |
964bac94 SL |
85 | rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev, |
86 | "mgmt-sticky"); | |
87 | if (IS_ERR(rockchip->mgmt_sticky_rst)) { | |
88 | if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER) | |
89 | dev_err(dev, "missing mgmt-sticky reset property in node\n"); | |
90 | return PTR_ERR(rockchip->mgmt_sticky_rst); | |
e77f847d SL |
91 | } |
92 | ||
964bac94 SL |
93 | rockchip->pipe_rst = devm_reset_control_get_exclusive(dev, "pipe"); |
94 | if (IS_ERR(rockchip->pipe_rst)) { | |
95 | if (PTR_ERR(rockchip->pipe_rst) != -EPROBE_DEFER) | |
96 | dev_err(dev, "missing pipe reset property in node\n"); | |
97 | return PTR_ERR(rockchip->pipe_rst); | |
98 | } | |
e77f847d | 99 | |
964bac94 SL |
100 | rockchip->pm_rst = devm_reset_control_get_exclusive(dev, "pm"); |
101 | if (IS_ERR(rockchip->pm_rst)) { | |
102 | if (PTR_ERR(rockchip->pm_rst) != -EPROBE_DEFER) | |
103 | dev_err(dev, "missing pm reset property in node\n"); | |
104 | return PTR_ERR(rockchip->pm_rst); | |
105 | } | |
e77f847d | 106 | |
964bac94 SL |
107 | rockchip->pclk_rst = devm_reset_control_get_exclusive(dev, "pclk"); |
108 | if (IS_ERR(rockchip->pclk_rst)) { | |
109 | if (PTR_ERR(rockchip->pclk_rst) != -EPROBE_DEFER) | |
110 | dev_err(dev, "missing pclk reset property in node\n"); | |
111 | return PTR_ERR(rockchip->pclk_rst); | |
112 | } | |
e77f847d | 113 | |
964bac94 SL |
114 | rockchip->aclk_rst = devm_reset_control_get_exclusive(dev, "aclk"); |
115 | if (IS_ERR(rockchip->aclk_rst)) { | |
116 | if (PTR_ERR(rockchip->aclk_rst) != -EPROBE_DEFER) | |
117 | dev_err(dev, "missing aclk reset property in node\n"); | |
118 | return PTR_ERR(rockchip->aclk_rst); | |
119 | } | |
e77f847d | 120 | |
964bac94 SL |
121 | if (rockchip->is_rc) { |
122 | rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH); | |
123 | if (IS_ERR(rockchip->ep_gpio)) { | |
124 | dev_err(dev, "missing ep-gpios property in node\n"); | |
125 | return PTR_ERR(rockchip->ep_gpio); | |
126 | } | |
127 | } | |
e77f847d | 128 | |
964bac94 SL |
129 | rockchip->aclk_pcie = devm_clk_get(dev, "aclk"); |
130 | if (IS_ERR(rockchip->aclk_pcie)) { | |
131 | dev_err(dev, "aclk clock not found\n"); | |
132 | return PTR_ERR(rockchip->aclk_pcie); | |
133 | } | |
e77f847d | 134 | |
964bac94 SL |
135 | rockchip->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf"); |
136 | if (IS_ERR(rockchip->aclk_perf_pcie)) { | |
137 | dev_err(dev, "aclk_perf clock not found\n"); | |
138 | return PTR_ERR(rockchip->aclk_perf_pcie); | |
139 | } | |
4816c4c7 | 140 | |
964bac94 SL |
141 | rockchip->hclk_pcie = devm_clk_get(dev, "hclk"); |
142 | if (IS_ERR(rockchip->hclk_pcie)) { | |
143 | dev_err(dev, "hclk clock not found\n"); | |
144 | return PTR_ERR(rockchip->hclk_pcie); | |
145 | } | |
4816c4c7 | 146 | |
964bac94 SL |
147 | rockchip->clk_pcie_pm = devm_clk_get(dev, "pm"); |
148 | if (IS_ERR(rockchip->clk_pcie_pm)) { | |
149 | dev_err(dev, "pm clock not found\n"); | |
150 | return PTR_ERR(rockchip->clk_pcie_pm); | |
4816c4c7 | 151 | } |
73edd2b1 | 152 | |
964bac94 | 153 | return 0; |
4816c4c7 | 154 | } |
964bac94 | 155 | EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt); |
4816c4c7 | 156 | |
3593709f | 157 | int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) |
e77f847d SL |
158 | { |
159 | struct device *dev = rockchip->dev; | |
9e87240c | 160 | int err, i; |
3593709f | 161 | u32 regs; |
e77f847d | 162 | |
31a3a7b5 SL |
163 | err = reset_control_assert(rockchip->aclk_rst); |
164 | if (err) { | |
165 | dev_err(dev, "assert aclk_rst err %d\n", err); | |
166 | return err; | |
167 | } | |
168 | ||
169 | err = reset_control_assert(rockchip->pclk_rst); | |
170 | if (err) { | |
171 | dev_err(dev, "assert pclk_rst err %d\n", err); | |
172 | return err; | |
173 | } | |
174 | ||
175 | err = reset_control_assert(rockchip->pm_rst); | |
176 | if (err) { | |
177 | dev_err(dev, "assert pm_rst err %d\n", err); | |
178 | return err; | |
179 | } | |
180 | ||
9e87240c SL |
181 | for (i = 0; i < MAX_LANE_NUM; i++) { |
182 | err = phy_init(rockchip->phys[i]); | |
183 | if (err) { | |
184 | dev_err(dev, "init phy%d err %d\n", i, err); | |
8c595dd1 | 185 | goto err_exit_phy; |
9e87240c | 186 | } |
e77f847d SL |
187 | } |
188 | ||
189 | err = reset_control_assert(rockchip->core_rst); | |
190 | if (err) { | |
191 | dev_err(dev, "assert core_rst err %d\n", err); | |
8c595dd1 | 192 | goto err_exit_phy; |
e77f847d SL |
193 | } |
194 | ||
195 | err = reset_control_assert(rockchip->mgmt_rst); | |
196 | if (err) { | |
197 | dev_err(dev, "assert mgmt_rst err %d\n", err); | |
8c595dd1 | 198 | goto err_exit_phy; |
e77f847d SL |
199 | } |
200 | ||
201 | err = reset_control_assert(rockchip->mgmt_sticky_rst); | |
202 | if (err) { | |
203 | dev_err(dev, "assert mgmt_sticky_rst err %d\n", err); | |
8c595dd1 | 204 | goto err_exit_phy; |
e77f847d SL |
205 | } |
206 | ||
207 | err = reset_control_assert(rockchip->pipe_rst); | |
208 | if (err) { | |
209 | dev_err(dev, "assert pipe_rst err %d\n", err); | |
8c595dd1 | 210 | goto err_exit_phy; |
e77f847d SL |
211 | } |
212 | ||
0722bdd2 SL |
213 | udelay(10); |
214 | ||
215 | err = reset_control_deassert(rockchip->pm_rst); | |
216 | if (err) { | |
217 | dev_err(dev, "deassert pm_rst err %d\n", err); | |
8c595dd1 | 218 | goto err_exit_phy; |
0722bdd2 SL |
219 | } |
220 | ||
221 | err = reset_control_deassert(rockchip->aclk_rst); | |
222 | if (err) { | |
223 | dev_err(dev, "deassert aclk_rst err %d\n", err); | |
8c595dd1 | 224 | goto err_exit_phy; |
0722bdd2 SL |
225 | } |
226 | ||
227 | err = reset_control_deassert(rockchip->pclk_rst); | |
228 | if (err) { | |
229 | dev_err(dev, "deassert pclk_rst err %d\n", err); | |
8c595dd1 | 230 | goto err_exit_phy; |
0722bdd2 SL |
231 | } |
232 | ||
f2fb5b8f SL |
233 | if (rockchip->link_gen == 2) |
234 | rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2, | |
235 | PCIE_CLIENT_CONFIG); | |
236 | else | |
237 | rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1, | |
238 | PCIE_CLIENT_CONFIG); | |
239 | ||
3593709f SL |
240 | regs = PCIE_CLIENT_LINK_TRAIN_ENABLE | PCIE_CLIENT_ARI_ENABLE | |
241 | PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes); | |
242 | ||
243 | if (rockchip->is_rc) | |
244 | regs |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC; | |
245 | else | |
246 | regs |= PCIE_CLIENT_CONF_DISABLE | PCIE_CLIENT_MODE_EP; | |
247 | ||
248 | rockchip_pcie_write(rockchip, regs, PCIE_CLIENT_CONFIG); | |
e77f847d | 249 | |
9e87240c SL |
250 | for (i = 0; i < MAX_LANE_NUM; i++) { |
251 | err = phy_power_on(rockchip->phys[i]); | |
252 | if (err) { | |
253 | dev_err(dev, "power on phy%d err %d\n", i, err); | |
8c595dd1 | 254 | goto err_power_off_phy; |
9e87240c | 255 | } |
e77f847d SL |
256 | } |
257 | ||
58c6990c SL |
258 | /* |
259 | * Please don't reorder the deassert sequence of the following | |
260 | * four reset pins. | |
261 | */ | |
262 | err = reset_control_deassert(rockchip->mgmt_sticky_rst); | |
e77f847d | 263 | if (err) { |
58c6990c | 264 | dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err); |
8c595dd1 | 265 | goto err_power_off_phy; |
e77f847d SL |
266 | } |
267 | ||
58c6990c | 268 | err = reset_control_deassert(rockchip->core_rst); |
e77f847d | 269 | if (err) { |
58c6990c | 270 | dev_err(dev, "deassert core_rst err %d\n", err); |
8c595dd1 | 271 | goto err_power_off_phy; |
e77f847d SL |
272 | } |
273 | ||
58c6990c | 274 | err = reset_control_deassert(rockchip->mgmt_rst); |
e77f847d | 275 | if (err) { |
58c6990c | 276 | dev_err(dev, "deassert mgmt_rst err %d\n", err); |
8c595dd1 | 277 | goto err_power_off_phy; |
e77f847d SL |
278 | } |
279 | ||
280 | err = reset_control_deassert(rockchip->pipe_rst); | |
281 | if (err) { | |
282 | dev_err(dev, "deassert pipe_rst err %d\n", err); | |
8c595dd1 | 283 | goto err_power_off_phy; |
e77f847d SL |
284 | } |
285 | ||
3593709f SL |
286 | return 0; |
287 | err_power_off_phy: | |
288 | while (i--) | |
289 | phy_power_off(rockchip->phys[i]); | |
290 | i = MAX_LANE_NUM; | |
291 | err_exit_phy: | |
292 | while (i--) | |
293 | phy_exit(rockchip->phys[i]); | |
294 | return err; | |
295 | } | |
296 | EXPORT_SYMBOL_GPL(rockchip_pcie_init_port); | |
e77f847d | 297 | |
956cd99b | 298 | int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) |
2ba5991f SL |
299 | { |
300 | struct device *dev = rockchip->dev; | |
9e87240c SL |
301 | struct phy *phy; |
302 | char *name; | |
303 | u32 i; | |
e77f847d | 304 | |
9e87240c SL |
305 | phy = devm_phy_get(dev, "pcie-phy"); |
306 | if (!IS_ERR(phy)) { | |
307 | rockchip->legacy_phy = true; | |
308 | rockchip->phys[0] = phy; | |
309 | dev_warn(dev, "legacy phy model is deprecated!\n"); | |
310 | return 0; | |
e77f847d SL |
311 | } |
312 | ||
9e87240c SL |
313 | if (PTR_ERR(phy) == -EPROBE_DEFER) |
314 | return PTR_ERR(phy); | |
e77f847d | 315 | |
9e87240c | 316 | dev_dbg(dev, "missing legacy phy; search for per-lane PHY\n"); |
e77f847d | 317 | |
f06c6c41 | 318 | for (i = 0; i < MAX_LANE_NUM; i++) { |
9e87240c SL |
319 | name = kasprintf(GFP_KERNEL, "pcie-phy-%u", i); |
320 | if (!name) | |
321 | return -ENOMEM; | |
2ba5991f | 322 | |
9e87240c SL |
323 | phy = devm_of_phy_get(dev, dev->of_node, name); |
324 | kfree(name); | |
325 | ||
326 | if (IS_ERR(phy)) { | |
327 | if (PTR_ERR(phy) != -EPROBE_DEFER) | |
328 | dev_err(dev, "missing phy for lane %d: %ld\n", | |
329 | i, PTR_ERR(phy)); | |
330 | return PTR_ERR(phy); | |
f06c6c41 | 331 | } |
f06c6c41 | 332 | |
9e87240c | 333 | rockchip->phys[i] = phy; |
afc9595e SL |
334 | } |
335 | ||
e77f847d SL |
336 | return 0; |
337 | } | |
956cd99b | 338 | EXPORT_SYMBOL_GPL(rockchip_pcie_get_phys); |
e77f847d | 339 | |
956cd99b | 340 | void rockchip_pcie_deinit_phys(struct rockchip_pcie *rockchip) |
de8473f5 SL |
341 | { |
342 | int i; | |
343 | ||
344 | for (i = 0; i < MAX_LANE_NUM; i++) { | |
345 | /* inactive lanes are already powered off */ | |
346 | if (rockchip->lanes_map & BIT(i)) | |
347 | phy_power_off(rockchip->phys[i]); | |
348 | phy_exit(rockchip->phys[i]); | |
349 | } | |
350 | } | |
956cd99b | 351 | EXPORT_SYMBOL_GPL(rockchip_pcie_deinit_phys); |
de8473f5 | 352 | |
956cd99b | 353 | int rockchip_pcie_enable_clocks(struct rockchip_pcie *rockchip) |
e77f847d | 354 | { |
e77f847d | 355 | struct device *dev = rockchip->dev; |
09df7bc4 | 356 | int err; |
e77f847d | 357 | |
09df7bc4 SL |
358 | err = clk_prepare_enable(rockchip->aclk_pcie); |
359 | if (err) { | |
360 | dev_err(dev, "unable to enable aclk_pcie clock\n"); | |
361 | return err; | |
362 | } | |
e77f847d | 363 | |
09df7bc4 SL |
364 | err = clk_prepare_enable(rockchip->aclk_perf_pcie); |
365 | if (err) { | |
366 | dev_err(dev, "unable to enable aclk_perf_pcie clock\n"); | |
367 | goto err_aclk_perf_pcie; | |
368 | } | |
e77f847d | 369 | |
09df7bc4 SL |
370 | err = clk_prepare_enable(rockchip->hclk_pcie); |
371 | if (err) { | |
372 | dev_err(dev, "unable to enable hclk_pcie clock\n"); | |
373 | goto err_hclk_pcie; | |
374 | } | |
375 | ||
376 | err = clk_prepare_enable(rockchip->clk_pcie_pm); | |
377 | if (err) { | |
378 | dev_err(dev, "unable to enable clk_pcie_pm clock\n"); | |
379 | goto err_clk_pcie_pm; | |
380 | } | |
381 | ||
382 | return 0; | |
383 | ||
384 | err_clk_pcie_pm: | |
385 | clk_disable_unprepare(rockchip->hclk_pcie); | |
386 | err_hclk_pcie: | |
387 | clk_disable_unprepare(rockchip->aclk_perf_pcie); | |
388 | err_aclk_perf_pcie: | |
389 | clk_disable_unprepare(rockchip->aclk_pcie); | |
390 | return err; | |
391 | } | |
956cd99b | 392 | EXPORT_SYMBOL_GPL(rockchip_pcie_enable_clocks); |
09df7bc4 | 393 | |
956cd99b | 394 | void rockchip_pcie_disable_clocks(void *data) |
41b70b2c SL |
395 | { |
396 | struct rockchip_pcie *rockchip = data; | |
397 | ||
398 | clk_disable_unprepare(rockchip->clk_pcie_pm); | |
399 | clk_disable_unprepare(rockchip->hclk_pcie); | |
400 | clk_disable_unprepare(rockchip->aclk_perf_pcie); | |
401 | clk_disable_unprepare(rockchip->aclk_pcie); | |
402 | } | |
956cd99b | 403 | EXPORT_SYMBOL_GPL(rockchip_pcie_disable_clocks); |
41b70b2c | 404 | |
956cd99b SL |
405 | void rockchip_pcie_cfg_configuration_accesses( |
406 | struct rockchip_pcie *rockchip, u32 type) | |
073d3dbe | 407 | { |
956cd99b | 408 | u32 ob_desc_0; |
073d3dbe | 409 | |
956cd99b SL |
410 | /* Configuration Accesses for region 0 */ |
411 | rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF); | |
073d3dbe | 412 | |
956cd99b SL |
413 | rockchip_pcie_write(rockchip, |
414 | (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS), | |
415 | PCIE_CORE_OB_REGION_ADDR0); | |
416 | rockchip_pcie_write(rockchip, RC_REGION_0_ADDR_TRANS_H, | |
417 | PCIE_CORE_OB_REGION_ADDR1); | |
418 | ob_desc_0 = rockchip_pcie_read(rockchip, PCIE_CORE_OB_REGION_DESC0); | |
419 | ob_desc_0 &= ~(RC_REGION_0_TYPE_MASK); | |
420 | ob_desc_0 |= (type | (0x1 << 23)); | |
421 | rockchip_pcie_write(rockchip, ob_desc_0, PCIE_CORE_OB_REGION_DESC0); | |
422 | rockchip_pcie_write(rockchip, 0x0, PCIE_CORE_OB_REGION_DESC1); | |
073d3dbe | 423 | } |
956cd99b | 424 | EXPORT_SYMBOL_GPL(rockchip_pcie_cfg_configuration_accesses); |