diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pci-meson.c')
-rw-r--r-- | drivers/pci/controller/dwc/pci-meson.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c new file mode 100644 index 000000000000..241ebe0c4505 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-meson.c | |||
@@ -0,0 +1,592 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * PCIe host controller driver for Amlogic MESON SoCs | ||
4 | * | ||
5 | * Copyright (c) 2018 Amlogic, inc. | ||
6 | * Author: Yue Wang <yue.wang@amlogic.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/clk.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/of_device.h> | ||
12 | #include <linux/of_gpio.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/reset.h> | ||
16 | #include <linux/resource.h> | ||
17 | #include <linux/types.h> | ||
18 | |||
19 | #include "pcie-designware.h" | ||
20 | |||
21 | #define to_meson_pcie(x) dev_get_drvdata((x)->dev) | ||
22 | |||
23 | /* External local bus interface registers */ | ||
24 | #define PLR_OFFSET 0x700 | ||
25 | #define PCIE_PORT_LINK_CTRL_OFF (PLR_OFFSET + 0x10) | ||
26 | #define FAST_LINK_MODE BIT(7) | ||
27 | #define LINK_CAPABLE_MASK GENMASK(21, 16) | ||
28 | #define LINK_CAPABLE_X1 BIT(16) | ||
29 | |||
30 | #define PCIE_GEN2_CTRL_OFF (PLR_OFFSET + 0x10c) | ||
31 | #define NUM_OF_LANES_MASK GENMASK(12, 8) | ||
32 | #define NUM_OF_LANES_X1 BIT(8) | ||
33 | #define DIRECT_SPEED_CHANGE BIT(17) | ||
34 | |||
35 | #define TYPE1_HDR_OFFSET 0x0 | ||
36 | #define PCIE_STATUS_COMMAND (TYPE1_HDR_OFFSET + 0x04) | ||
37 | #define PCI_IO_EN BIT(0) | ||
38 | #define PCI_MEM_SPACE_EN BIT(1) | ||
39 | #define PCI_BUS_MASTER_EN BIT(2) | ||
40 | |||
41 | #define PCIE_BASE_ADDR0 (TYPE1_HDR_OFFSET + 0x10) | ||
42 | #define PCIE_BASE_ADDR1 (TYPE1_HDR_OFFSET + 0x14) | ||
43 | |||
44 | #define PCIE_CAP_OFFSET 0x70 | ||
45 | #define PCIE_DEV_CTRL_DEV_STUS (PCIE_CAP_OFFSET + 0x08) | ||
46 | #define PCIE_CAP_MAX_PAYLOAD_MASK GENMASK(7, 5) | ||
47 | #define PCIE_CAP_MAX_PAYLOAD_SIZE(x) ((x) << 5) | ||
48 | #define PCIE_CAP_MAX_READ_REQ_MASK GENMASK(14, 12) | ||
49 | #define PCIE_CAP_MAX_READ_REQ_SIZE(x) ((x) << 12) | ||
50 | |||
51 | /* PCIe specific config registers */ | ||
52 | #define PCIE_CFG0 0x0 | ||
53 | #define APP_LTSSM_ENABLE BIT(7) | ||
54 | |||
55 | #define PCIE_CFG_STATUS12 0x30 | ||
56 | #define IS_SMLH_LINK_UP(x) ((x) & (1 << 6)) | ||
57 | #define IS_RDLH_LINK_UP(x) ((x) & (1 << 16)) | ||
58 | #define IS_LTSSM_UP(x) ((((x) >> 10) & 0x1f) == 0x11) | ||
59 | |||
60 | #define PCIE_CFG_STATUS17 0x44 | ||
61 | #define PM_CURRENT_STATE(x) (((x) >> 7) & 0x1) | ||
62 | |||
63 | #define WAIT_LINKUP_TIMEOUT 4000 | ||
64 | #define PORT_CLK_RATE 100000000UL | ||
65 | #define MAX_PAYLOAD_SIZE 256 | ||
66 | #define MAX_READ_REQ_SIZE 256 | ||
67 | #define MESON_PCIE_PHY_POWERUP 0x1c | ||
68 | #define PCIE_RESET_DELAY 500 | ||
69 | #define PCIE_SHARED_RESET 1 | ||
70 | #define PCIE_NORMAL_RESET 0 | ||
71 | |||
72 | enum pcie_data_rate { | ||
73 | PCIE_GEN1, | ||
74 | PCIE_GEN2, | ||
75 | PCIE_GEN3, | ||
76 | PCIE_GEN4 | ||
77 | }; | ||
78 | |||
79 | struct meson_pcie_mem_res { | ||
80 | void __iomem *elbi_base; | ||
81 | void __iomem *cfg_base; | ||
82 | void __iomem *phy_base; | ||
83 | }; | ||
84 | |||
85 | struct meson_pcie_clk_res { | ||
86 | struct clk *clk; | ||
87 | struct clk *mipi_gate; | ||
88 | struct clk *port_clk; | ||
89 | struct clk *general_clk; | ||
90 | }; | ||
91 | |||
92 | struct meson_pcie_rc_reset { | ||
93 | struct reset_control *phy; | ||
94 | struct reset_control *port; | ||
95 | struct reset_control *apb; | ||
96 | }; | ||
97 | |||
98 | struct meson_pcie { | ||
99 | struct dw_pcie pci; | ||
100 | struct meson_pcie_mem_res mem_res; | ||
101 | struct meson_pcie_clk_res clk_res; | ||
102 | struct meson_pcie_rc_reset mrst; | ||
103 | struct gpio_desc *reset_gpio; | ||
104 | }; | ||
105 | |||
106 | static struct reset_control *meson_pcie_get_reset(struct meson_pcie *mp, | ||
107 | const char *id, | ||
108 | u32 reset_type) | ||
109 | { | ||
110 | struct device *dev = mp->pci.dev; | ||
111 | struct reset_control *reset; | ||
112 | |||
113 | if (reset_type == PCIE_SHARED_RESET) | ||
114 | reset = devm_reset_control_get_shared(dev, id); | ||
115 | else | ||
116 | reset = devm_reset_control_get(dev, id); | ||
117 | |||
118 | return reset; | ||
119 | } | ||
120 | |||
121 | static int meson_pcie_get_resets(struct meson_pcie *mp) | ||
122 | { | ||
123 | struct meson_pcie_rc_reset *mrst = &mp->mrst; | ||
124 | |||
125 | mrst->phy = meson_pcie_get_reset(mp, "phy", PCIE_SHARED_RESET); | ||
126 | if (IS_ERR(mrst->phy)) | ||
127 | return PTR_ERR(mrst->phy); | ||
128 | reset_control_deassert(mrst->phy); | ||
129 | |||
130 | mrst->port = meson_pcie_get_reset(mp, "port", PCIE_NORMAL_RESET); | ||
131 | if (IS_ERR(mrst->port)) | ||
132 | return PTR_ERR(mrst->port); | ||
133 | reset_control_deassert(mrst->port); | ||
134 | |||
135 | mrst->apb = meson_pcie_get_reset(mp, "apb", PCIE_SHARED_RESET); | ||
136 | if (IS_ERR(mrst->apb)) | ||
137 | return PTR_ERR(mrst->apb); | ||
138 | reset_control_deassert(mrst->apb); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static void __iomem *meson_pcie_get_mem(struct platform_device *pdev, | ||
144 | struct meson_pcie *mp, | ||
145 | const char *id) | ||
146 | { | ||
147 | struct device *dev = mp->pci.dev; | ||
148 | struct resource *res; | ||
149 | |||
150 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); | ||
151 | |||
152 | return devm_ioremap_resource(dev, res); | ||
153 | } | ||
154 | |||
155 | static void __iomem *meson_pcie_get_mem_shared(struct platform_device *pdev, | ||
156 | struct meson_pcie *mp, | ||
157 | const char *id) | ||
158 | { | ||
159 | struct device *dev = mp->pci.dev; | ||
160 | struct resource *res; | ||
161 | |||
162 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); | ||
163 | if (!res) { | ||
164 | dev_err(dev, "No REG resource %s\n", id); | ||
165 | return ERR_PTR(-ENXIO); | ||
166 | } | ||
167 | |||
168 | return devm_ioremap(dev, res->start, resource_size(res)); | ||
169 | } | ||
170 | |||
171 | static int meson_pcie_get_mems(struct platform_device *pdev, | ||
172 | struct meson_pcie *mp) | ||
173 | { | ||
174 | mp->mem_res.elbi_base = meson_pcie_get_mem(pdev, mp, "elbi"); | ||
175 | if (IS_ERR(mp->mem_res.elbi_base)) | ||
176 | return PTR_ERR(mp->mem_res.elbi_base); | ||
177 | |||
178 | mp->mem_res.cfg_base = meson_pcie_get_mem(pdev, mp, "cfg"); | ||
179 | if (IS_ERR(mp->mem_res.cfg_base)) | ||
180 | return PTR_ERR(mp->mem_res.cfg_base); | ||
181 | |||
182 | /* Meson SoC has two PCI controllers use same phy register*/ | ||
183 | mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy"); | ||
184 | if (IS_ERR(mp->mem_res.phy_base)) | ||
185 | return PTR_ERR(mp->mem_res.phy_base); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static void meson_pcie_power_on(struct meson_pcie *mp) | ||
191 | { | ||
192 | writel(MESON_PCIE_PHY_POWERUP, mp->mem_res.phy_base); | ||
193 | } | ||
194 | |||
195 | static void meson_pcie_reset(struct meson_pcie *mp) | ||
196 | { | ||
197 | struct meson_pcie_rc_reset *mrst = &mp->mrst; | ||
198 | |||
199 | reset_control_assert(mrst->phy); | ||
200 | udelay(PCIE_RESET_DELAY); | ||
201 | reset_control_deassert(mrst->phy); | ||
202 | udelay(PCIE_RESET_DELAY); | ||
203 | |||
204 | reset_control_assert(mrst->port); | ||
205 | reset_control_assert(mrst->apb); | ||
206 | udelay(PCIE_RESET_DELAY); | ||
207 | reset_control_deassert(mrst->port); | ||
208 | reset_control_deassert(mrst->apb); | ||
209 | udelay(PCIE_RESET_DELAY); | ||
210 | } | ||
211 | |||
212 | static inline struct clk *meson_pcie_probe_clock(struct device *dev, | ||
213 | const char *id, u64 rate) | ||
214 | { | ||
215 | struct clk *clk; | ||
216 | int ret; | ||
217 | |||
218 | clk = devm_clk_get(dev, id); | ||
219 | if (IS_ERR(clk)) | ||
220 | return clk; | ||
221 | |||
222 | if (rate) { | ||
223 | ret = clk_set_rate(clk, rate); | ||
224 | if (ret) { | ||
225 | dev_err(dev, "set clk rate failed, ret = %d\n", ret); | ||
226 | return ERR_PTR(ret); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | ret = clk_prepare_enable(clk); | ||
231 | if (ret) { | ||
232 | dev_err(dev, "couldn't enable clk\n"); | ||
233 | return ERR_PTR(ret); | ||
234 | } | ||
235 | |||
236 | devm_add_action_or_reset(dev, | ||
237 | (void (*) (void *))clk_disable_unprepare, | ||
238 | clk); | ||
239 | |||
240 | return clk; | ||
241 | } | ||
242 | |||
243 | static int meson_pcie_probe_clocks(struct meson_pcie *mp) | ||
244 | { | ||
245 | struct device *dev = mp->pci.dev; | ||
246 | struct meson_pcie_clk_res *res = &mp->clk_res; | ||
247 | |||
248 | res->port_clk = meson_pcie_probe_clock(dev, "port", PORT_CLK_RATE); | ||
249 | if (IS_ERR(res->port_clk)) | ||
250 | return PTR_ERR(res->port_clk); | ||
251 | |||
252 | res->mipi_gate = meson_pcie_probe_clock(dev, "pcie_mipi_en", 0); | ||
253 | if (IS_ERR(res->mipi_gate)) | ||
254 | return PTR_ERR(res->mipi_gate); | ||
255 | |||
256 | res->general_clk = meson_pcie_probe_clock(dev, "pcie_general", 0); | ||
257 | if (IS_ERR(res->general_clk)) | ||
258 | return PTR_ERR(res->general_clk); | ||
259 | |||
260 | res->clk = meson_pcie_probe_clock(dev, "pcie", 0); | ||
261 | if (IS_ERR(res->clk)) | ||
262 | return PTR_ERR(res->clk); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static inline void meson_elb_writel(struct meson_pcie *mp, u32 val, u32 reg) | ||
268 | { | ||
269 | writel(val, mp->mem_res.elbi_base + reg); | ||
270 | } | ||
271 | |||
272 | static inline u32 meson_elb_readl(struct meson_pcie *mp, u32 reg) | ||
273 | { | ||
274 | return readl(mp->mem_res.elbi_base + reg); | ||
275 | } | ||
276 | |||
277 | static inline u32 meson_cfg_readl(struct meson_pcie *mp, u32 reg) | ||
278 | { | ||
279 | return readl(mp->mem_res.cfg_base + reg); | ||
280 | } | ||
281 | |||
282 | static inline void meson_cfg_writel(struct meson_pcie *mp, u32 val, u32 reg) | ||
283 | { | ||
284 | writel(val, mp->mem_res.cfg_base + reg); | ||
285 | } | ||
286 | |||
287 | static void meson_pcie_assert_reset(struct meson_pcie *mp) | ||
288 | { | ||
289 | gpiod_set_value_cansleep(mp->reset_gpio, 0); | ||
290 | udelay(500); | ||
291 | gpiod_set_value_cansleep(mp->reset_gpio, 1); | ||
292 | } | ||
293 | |||
294 | static void meson_pcie_init_dw(struct meson_pcie *mp) | ||
295 | { | ||
296 | u32 val; | ||
297 | |||
298 | val = meson_cfg_readl(mp, PCIE_CFG0); | ||
299 | val |= APP_LTSSM_ENABLE; | ||
300 | meson_cfg_writel(mp, val, PCIE_CFG0); | ||
301 | |||
302 | val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); | ||
303 | val &= ~LINK_CAPABLE_MASK; | ||
304 | meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); | ||
305 | |||
306 | val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); | ||
307 | val |= LINK_CAPABLE_X1 | FAST_LINK_MODE; | ||
308 | meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); | ||
309 | |||
310 | val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); | ||
311 | val &= ~NUM_OF_LANES_MASK; | ||
312 | meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); | ||
313 | |||
314 | val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); | ||
315 | val |= NUM_OF_LANES_X1 | DIRECT_SPEED_CHANGE; | ||
316 | meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); | ||
317 | |||
318 | meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR0); | ||
319 | meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR1); | ||
320 | } | ||
321 | |||
322 | static int meson_size_to_payload(struct meson_pcie *mp, int size) | ||
323 | { | ||
324 | struct device *dev = mp->pci.dev; | ||
325 | |||
326 | /* | ||
327 | * dwc supports 2^(val+7) payload size, which val is 0~5 default to 1. | ||
328 | * So if input size is not 2^order alignment or less than 2^7 or bigger | ||
329 | * than 2^12, just set to default size 2^(1+7). | ||
330 | */ | ||
331 | if (!is_power_of_2(size) || size < 128 || size > 4096) { | ||
332 | dev_warn(dev, "payload size %d, set to default 256\n", size); | ||
333 | return 1; | ||
334 | } | ||
335 | |||
336 | return fls(size) - 8; | ||
337 | } | ||
338 | |||
339 | static void meson_set_max_payload(struct meson_pcie *mp, int size) | ||
340 | { | ||
341 | u32 val; | ||
342 | int max_payload_size = meson_size_to_payload(mp, size); | ||
343 | |||
344 | val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); | ||
345 | val &= ~PCIE_CAP_MAX_PAYLOAD_MASK; | ||
346 | meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); | ||
347 | |||
348 | val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); | ||
349 | val |= PCIE_CAP_MAX_PAYLOAD_SIZE(max_payload_size); | ||
350 | meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); | ||
351 | } | ||
352 | |||
353 | static void meson_set_max_rd_req_size(struct meson_pcie *mp, int size) | ||
354 | { | ||
355 | u32 val; | ||
356 | int max_rd_req_size = meson_size_to_payload(mp, size); | ||
357 | |||
358 | val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); | ||
359 | val &= ~PCIE_CAP_MAX_READ_REQ_MASK; | ||
360 | meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); | ||
361 | |||
362 | val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); | ||
363 | val |= PCIE_CAP_MAX_READ_REQ_SIZE(max_rd_req_size); | ||
364 | meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); | ||
365 | } | ||
366 | |||
367 | static inline void meson_enable_memory_space(struct meson_pcie *mp) | ||
368 | { | ||
369 | /* Set the RC Bus Master, Memory Space and I/O Space enables */ | ||
370 | meson_elb_writel(mp, PCI_IO_EN | PCI_MEM_SPACE_EN | PCI_BUS_MASTER_EN, | ||
371 | PCIE_STATUS_COMMAND); | ||
372 | } | ||
373 | |||
374 | static int meson_pcie_establish_link(struct meson_pcie *mp) | ||
375 | { | ||
376 | struct dw_pcie *pci = &mp->pci; | ||
377 | struct pcie_port *pp = &pci->pp; | ||
378 | |||
379 | meson_pcie_init_dw(mp); | ||
380 | meson_set_max_payload(mp, MAX_PAYLOAD_SIZE); | ||
381 | meson_set_max_rd_req_size(mp, MAX_READ_REQ_SIZE); | ||
382 | |||
383 | dw_pcie_setup_rc(pp); | ||
384 | meson_enable_memory_space(mp); | ||
385 | |||
386 | meson_pcie_assert_reset(mp); | ||
387 | |||
388 | return dw_pcie_wait_for_link(pci); | ||
389 | } | ||
390 | |||
391 | static void meson_pcie_enable_interrupts(struct meson_pcie *mp) | ||
392 | { | ||
393 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
394 | dw_pcie_msi_init(&mp->pci.pp); | ||
395 | } | ||
396 | |||
397 | static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, | ||
398 | u32 *val) | ||
399 | { | ||
400 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
401 | int ret; | ||
402 | |||
403 | ret = dw_pcie_read(pci->dbi_base + where, size, val); | ||
404 | if (ret != PCIBIOS_SUCCESSFUL) | ||
405 | return ret; | ||
406 | |||
407 | /* | ||
408 | * There is a bug in the MESON AXG PCIe controller whereby software | ||
409 | * cannot program the PCI_CLASS_DEVICE register, so we must fabricate | ||
410 | * the return value in the config accessors. | ||
411 | */ | ||
412 | if (where == PCI_CLASS_REVISION && size == 4) | ||
413 | *val = (PCI_CLASS_BRIDGE_PCI << 16) | (*val & 0xffff); | ||
414 | else if (where == PCI_CLASS_DEVICE && size == 2) | ||
415 | *val = PCI_CLASS_BRIDGE_PCI; | ||
416 | else if (where == PCI_CLASS_DEVICE && size == 1) | ||
417 | *val = PCI_CLASS_BRIDGE_PCI & 0xff; | ||
418 | else if (where == PCI_CLASS_DEVICE + 1 && size == 1) | ||
419 | *val = (PCI_CLASS_BRIDGE_PCI >> 8) & 0xff; | ||
420 | |||
421 | return PCIBIOS_SUCCESSFUL; | ||
422 | } | ||
423 | |||
424 | static int meson_pcie_wr_own_conf(struct pcie_port *pp, int where, | ||
425 | int size, u32 val) | ||
426 | { | ||
427 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
428 | |||
429 | return dw_pcie_write(pci->dbi_base + where, size, val); | ||
430 | } | ||
431 | |||
432 | static int meson_pcie_link_up(struct dw_pcie *pci) | ||
433 | { | ||
434 | struct meson_pcie *mp = to_meson_pcie(pci); | ||
435 | struct device *dev = pci->dev; | ||
436 | u32 speed_okay = 0; | ||
437 | u32 cnt = 0; | ||
438 | u32 state12, state17, smlh_up, ltssm_up, rdlh_up; | ||
439 | |||
440 | do { | ||
441 | state12 = meson_cfg_readl(mp, PCIE_CFG_STATUS12); | ||
442 | state17 = meson_cfg_readl(mp, PCIE_CFG_STATUS17); | ||
443 | smlh_up = IS_SMLH_LINK_UP(state12); | ||
444 | rdlh_up = IS_RDLH_LINK_UP(state12); | ||
445 | ltssm_up = IS_LTSSM_UP(state12); | ||
446 | |||
447 | if (PM_CURRENT_STATE(state17) < PCIE_GEN3) | ||
448 | speed_okay = 1; | ||
449 | |||
450 | if (smlh_up) | ||
451 | dev_dbg(dev, "smlh_link_up is on\n"); | ||
452 | if (rdlh_up) | ||
453 | dev_dbg(dev, "rdlh_link_up is on\n"); | ||
454 | if (ltssm_up) | ||
455 | dev_dbg(dev, "ltssm_up is on\n"); | ||
456 | if (speed_okay) | ||
457 | dev_dbg(dev, "speed_okay\n"); | ||
458 | |||
459 | if (smlh_up && rdlh_up && ltssm_up && speed_okay) | ||
460 | return 1; | ||
461 | |||
462 | cnt++; | ||
463 | |||
464 | udelay(10); | ||
465 | } while (cnt < WAIT_LINKUP_TIMEOUT); | ||
466 | |||
467 | dev_err(dev, "error: wait linkup timeout\n"); | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int meson_pcie_host_init(struct pcie_port *pp) | ||
472 | { | ||
473 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
474 | struct meson_pcie *mp = to_meson_pcie(pci); | ||
475 | int ret; | ||
476 | |||
477 | ret = meson_pcie_establish_link(mp); | ||
478 | if (ret) | ||
479 | return ret; | ||
480 | |||
481 | meson_pcie_enable_interrupts(mp); | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static const struct dw_pcie_host_ops meson_pcie_host_ops = { | ||
487 | .rd_own_conf = meson_pcie_rd_own_conf, | ||
488 | .wr_own_conf = meson_pcie_wr_own_conf, | ||
489 | .host_init = meson_pcie_host_init, | ||
490 | }; | ||
491 | |||
492 | static int meson_add_pcie_port(struct meson_pcie *mp, | ||
493 | struct platform_device *pdev) | ||
494 | { | ||
495 | struct dw_pcie *pci = &mp->pci; | ||
496 | struct pcie_port *pp = &pci->pp; | ||
497 | struct device *dev = &pdev->dev; | ||
498 | int ret; | ||
499 | |||
500 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | ||
501 | pp->msi_irq = platform_get_irq(pdev, 0); | ||
502 | if (pp->msi_irq < 0) { | ||
503 | dev_err(dev, "failed to get MSI IRQ\n"); | ||
504 | return pp->msi_irq; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | pp->ops = &meson_pcie_host_ops; | ||
509 | pci->dbi_base = mp->mem_res.elbi_base; | ||
510 | |||
511 | ret = dw_pcie_host_init(pp); | ||
512 | if (ret) { | ||
513 | dev_err(dev, "failed to initialize host\n"); | ||
514 | return ret; | ||
515 | } | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static const struct dw_pcie_ops dw_pcie_ops = { | ||
521 | .link_up = meson_pcie_link_up, | ||
522 | }; | ||
523 | |||
524 | static int meson_pcie_probe(struct platform_device *pdev) | ||
525 | { | ||
526 | struct device *dev = &pdev->dev; | ||
527 | struct dw_pcie *pci; | ||
528 | struct meson_pcie *mp; | ||
529 | int ret; | ||
530 | |||
531 | mp = devm_kzalloc(dev, sizeof(*mp), GFP_KERNEL); | ||
532 | if (!mp) | ||
533 | return -ENOMEM; | ||
534 | |||
535 | pci = &mp->pci; | ||
536 | pci->dev = dev; | ||
537 | pci->ops = &dw_pcie_ops; | ||
538 | |||
539 | mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); | ||
540 | if (IS_ERR(mp->reset_gpio)) { | ||
541 | dev_err(dev, "get reset gpio failed\n"); | ||
542 | return PTR_ERR(mp->reset_gpio); | ||
543 | } | ||
544 | |||
545 | ret = meson_pcie_get_resets(mp); | ||
546 | if (ret) { | ||
547 | dev_err(dev, "get reset resource failed, %d\n", ret); | ||
548 | return ret; | ||
549 | } | ||
550 | |||
551 | ret = meson_pcie_get_mems(pdev, mp); | ||
552 | if (ret) { | ||
553 | dev_err(dev, "get memory resource failed, %d\n", ret); | ||
554 | return ret; | ||
555 | } | ||
556 | |||
557 | meson_pcie_power_on(mp); | ||
558 | meson_pcie_reset(mp); | ||
559 | |||
560 | ret = meson_pcie_probe_clocks(mp); | ||
561 | if (ret) { | ||
562 | dev_err(dev, "init clock resources failed, %d\n", ret); | ||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | platform_set_drvdata(pdev, mp); | ||
567 | |||
568 | ret = meson_add_pcie_port(mp, pdev); | ||
569 | if (ret < 0) { | ||
570 | dev_err(dev, "Add PCIe port failed, %d\n", ret); | ||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static const struct of_device_id meson_pcie_of_match[] = { | ||
578 | { | ||
579 | .compatible = "amlogic,axg-pcie", | ||
580 | }, | ||
581 | {}, | ||
582 | }; | ||
583 | |||
584 | static struct platform_driver meson_pcie_driver = { | ||
585 | .probe = meson_pcie_probe, | ||
586 | .driver = { | ||
587 | .name = "meson-pcie", | ||
588 | .of_match_table = meson_pcie_of_match, | ||
589 | }, | ||
590 | }; | ||
591 | |||
592 | builtin_platform_driver(meson_pcie_driver); | ||