]>
Commit | Line | Data |
---|---|---|
bb38919e SC |
1 | /* |
2 | * PCIe host controller driver for Freescale i.MX6 SoCs | |
3 | * | |
4 | * Copyright (C) 2013 Kosagi | |
5 | * http://www.kosagi.com | |
6 | * | |
7 | * Author: Sean Cross <xobs@kosagi.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/gpio.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/mfd/syscon.h> | |
19 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | |
9b3fe679 | 20 | #include <linux/mfd/syscon/imx7-iomuxc-gpr.h> |
bb38919e SC |
21 | #include <linux/module.h> |
22 | #include <linux/of_gpio.h> | |
e6f1fef0 | 23 | #include <linux/of_device.h> |
bb38919e SC |
24 | #include <linux/pci.h> |
25 | #include <linux/platform_device.h> | |
26 | #include <linux/regmap.h> | |
27 | #include <linux/resource.h> | |
28 | #include <linux/signal.h> | |
29 | #include <linux/types.h> | |
d1dc9749 | 30 | #include <linux/interrupt.h> |
9b3fe679 | 31 | #include <linux/reset.h> |
bb38919e SC |
32 | |
33 | #include "pcie-designware.h" | |
34 | ||
442ec4c0 | 35 | #define to_imx6_pcie(x) dev_get_drvdata((x)->dev) |
bb38919e | 36 | |
e6f1fef0 AS |
37 | enum imx6_pcie_variants { |
38 | IMX6Q, | |
4d31c610 AS |
39 | IMX6SX, |
40 | IMX6QP, | |
9b3fe679 | 41 | IMX7D, |
e6f1fef0 AS |
42 | }; |
43 | ||
bb38919e | 44 | struct imx6_pcie { |
442ec4c0 | 45 | struct dw_pcie *pci; |
b2d7a9cd | 46 | int reset_gpio; |
3ea8529a | 47 | bool gpio_active_high; |
57526136 LS |
48 | struct clk *pcie_bus; |
49 | struct clk *pcie_phy; | |
e3c06cd0 | 50 | struct clk *pcie_inbound_axi; |
57526136 | 51 | struct clk *pcie; |
bb38919e | 52 | struct regmap *iomuxc_gpr; |
9b3fe679 AS |
53 | struct reset_control *pciephy_reset; |
54 | struct reset_control *apps_reset; | |
e6f1fef0 | 55 | enum imx6_pcie_variants variant; |
28e3abe5 JW |
56 | u32 tx_deemph_gen1; |
57 | u32 tx_deemph_gen2_3p5db; | |
58 | u32 tx_deemph_gen2_6db; | |
59 | u32 tx_swing_full; | |
60 | u32 tx_swing_low; | |
a5fcec48 | 61 | int link_gen; |
bb38919e SC |
62 | }; |
63 | ||
9b3fe679 AS |
64 | /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ |
65 | #define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000 | |
66 | #define PHY_PLL_LOCK_WAIT_USLEEP_MIN 50 | |
67 | #define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 | |
68 | ||
fa33a6d8 MV |
69 | /* PCIe Root Complex registers (memory-mapped) */ |
70 | #define PCIE_RC_LCR 0x7c | |
71 | #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 | |
72 | #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 | |
73 | #define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf | |
74 | ||
2393f79c BH |
75 | #define PCIE_RC_LCSR 0x80 |
76 | ||
bb38919e SC |
77 | /* PCIe Port Logic registers (memory-mapped) */ |
78 | #define PL_OFFSET 0x700 | |
3e3e406e LS |
79 | #define PCIE_PL_PFLR (PL_OFFSET + 0x08) |
80 | #define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16) | |
81 | #define PCIE_PL_PFLR_FORCE_LINK (1 << 15) | |
bb38919e SC |
82 | #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) |
83 | #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) | |
7f9f40c0 MV |
84 | #define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) |
85 | #define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) | |
bb38919e SC |
86 | |
87 | #define PCIE_PHY_CTRL (PL_OFFSET + 0x114) | |
88 | #define PCIE_PHY_CTRL_DATA_LOC 0 | |
89 | #define PCIE_PHY_CTRL_CAP_ADR_LOC 16 | |
90 | #define PCIE_PHY_CTRL_CAP_DAT_LOC 17 | |
91 | #define PCIE_PHY_CTRL_WR_LOC 18 | |
92 | #define PCIE_PHY_CTRL_RD_LOC 19 | |
93 | ||
94 | #define PCIE_PHY_STAT (PL_OFFSET + 0x110) | |
95 | #define PCIE_PHY_STAT_ACK_LOC 16 | |
96 | ||
fa33a6d8 MV |
97 | #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C |
98 | #define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) | |
99 | ||
bb38919e SC |
100 | /* PHY registers (not memory-mapped) */ |
101 | #define PCIE_PHY_RX_ASIC_OUT 0x100D | |
111feb7f | 102 | #define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0) |
bb38919e SC |
103 | |
104 | #define PHY_RX_OVRD_IN_LO 0x1005 | |
105 | #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) | |
106 | #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) | |
107 | ||
8bad7f2f | 108 | static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) |
bb38919e | 109 | { |
442ec4c0 | 110 | struct dw_pcie *pci = imx6_pcie->pci; |
bb38919e SC |
111 | u32 val; |
112 | u32 max_iterations = 10; | |
113 | u32 wait_counter = 0; | |
114 | ||
115 | do { | |
442ec4c0 | 116 | val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); |
bb38919e SC |
117 | val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; |
118 | wait_counter++; | |
119 | ||
120 | if (val == exp_val) | |
121 | return 0; | |
122 | ||
123 | udelay(1); | |
124 | } while (wait_counter < max_iterations); | |
125 | ||
126 | return -ETIMEDOUT; | |
127 | } | |
128 | ||
8bad7f2f | 129 | static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) |
bb38919e | 130 | { |
442ec4c0 | 131 | struct dw_pcie *pci = imx6_pcie->pci; |
bb38919e SC |
132 | u32 val; |
133 | int ret; | |
134 | ||
135 | val = addr << PCIE_PHY_CTRL_DATA_LOC; | |
442ec4c0 | 136 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); |
bb38919e SC |
137 | |
138 | val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); | |
442ec4c0 | 139 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); |
bb38919e | 140 | |
8bad7f2f | 141 | ret = pcie_phy_poll_ack(imx6_pcie, 1); |
bb38919e SC |
142 | if (ret) |
143 | return ret; | |
144 | ||
145 | val = addr << PCIE_PHY_CTRL_DATA_LOC; | |
442ec4c0 | 146 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); |
bb38919e | 147 | |
8bad7f2f | 148 | return pcie_phy_poll_ack(imx6_pcie, 0); |
bb38919e SC |
149 | } |
150 | ||
151 | /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ | |
8bad7f2f | 152 | static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) |
bb38919e | 153 | { |
442ec4c0 | 154 | struct dw_pcie *pci = imx6_pcie->pci; |
bb38919e SC |
155 | u32 val, phy_ctl; |
156 | int ret; | |
157 | ||
8bad7f2f | 158 | ret = pcie_phy_wait_ack(imx6_pcie, addr); |
bb38919e SC |
159 | if (ret) |
160 | return ret; | |
161 | ||
162 | /* assert Read signal */ | |
163 | phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; | |
442ec4c0 | 164 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl); |
bb38919e | 165 | |
8bad7f2f | 166 | ret = pcie_phy_poll_ack(imx6_pcie, 1); |
bb38919e SC |
167 | if (ret) |
168 | return ret; | |
169 | ||
442ec4c0 | 170 | val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); |
bb38919e SC |
171 | *data = val & 0xffff; |
172 | ||
173 | /* deassert Read signal */ | |
442ec4c0 | 174 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00); |
bb38919e | 175 | |
8bad7f2f | 176 | return pcie_phy_poll_ack(imx6_pcie, 0); |
bb38919e SC |
177 | } |
178 | ||
8bad7f2f | 179 | static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) |
bb38919e | 180 | { |
442ec4c0 | 181 | struct dw_pcie *pci = imx6_pcie->pci; |
bb38919e SC |
182 | u32 var; |
183 | int ret; | |
184 | ||
185 | /* write addr */ | |
186 | /* cap addr */ | |
8bad7f2f | 187 | ret = pcie_phy_wait_ack(imx6_pcie, addr); |
bb38919e SC |
188 | if (ret) |
189 | return ret; | |
190 | ||
191 | var = data << PCIE_PHY_CTRL_DATA_LOC; | |
442ec4c0 | 192 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); |
bb38919e SC |
193 | |
194 | /* capture data */ | |
195 | var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); | |
442ec4c0 | 196 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); |
bb38919e | 197 | |
8bad7f2f | 198 | ret = pcie_phy_poll_ack(imx6_pcie, 1); |
bb38919e SC |
199 | if (ret) |
200 | return ret; | |
201 | ||
202 | /* deassert cap data */ | |
203 | var = data << PCIE_PHY_CTRL_DATA_LOC; | |
442ec4c0 | 204 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); |
bb38919e SC |
205 | |
206 | /* wait for ack de-assertion */ | |
8bad7f2f | 207 | ret = pcie_phy_poll_ack(imx6_pcie, 0); |
bb38919e SC |
208 | if (ret) |
209 | return ret; | |
210 | ||
211 | /* assert wr signal */ | |
212 | var = 0x1 << PCIE_PHY_CTRL_WR_LOC; | |
442ec4c0 | 213 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); |
bb38919e SC |
214 | |
215 | /* wait for ack */ | |
8bad7f2f | 216 | ret = pcie_phy_poll_ack(imx6_pcie, 1); |
bb38919e SC |
217 | if (ret) |
218 | return ret; | |
219 | ||
220 | /* deassert wr signal */ | |
221 | var = data << PCIE_PHY_CTRL_DATA_LOC; | |
442ec4c0 | 222 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); |
bb38919e SC |
223 | |
224 | /* wait for ack de-assertion */ | |
8bad7f2f | 225 | ret = pcie_phy_poll_ack(imx6_pcie, 0); |
bb38919e SC |
226 | if (ret) |
227 | return ret; | |
228 | ||
442ec4c0 | 229 | dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0); |
bb38919e SC |
230 | |
231 | return 0; | |
232 | } | |
233 | ||
e7d7705a | 234 | static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) |
53eeb48b LS |
235 | { |
236 | u32 tmp; | |
237 | ||
8bad7f2f | 238 | pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); |
53eeb48b LS |
239 | tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | |
240 | PHY_RX_OVRD_IN_LO_RX_PLL_EN); | |
8bad7f2f | 241 | pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); |
53eeb48b LS |
242 | |
243 | usleep_range(2000, 3000); | |
244 | ||
8bad7f2f | 245 | pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); |
53eeb48b LS |
246 | tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | |
247 | PHY_RX_OVRD_IN_LO_RX_PLL_EN); | |
8bad7f2f | 248 | pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); |
53eeb48b LS |
249 | } |
250 | ||
bb38919e SC |
251 | /* Added for PCI abort handling */ |
252 | static int imx6q_pcie_abort_handler(unsigned long addr, | |
253 | unsigned int fsr, struct pt_regs *regs) | |
254 | { | |
415b6185 LS |
255 | unsigned long pc = instruction_pointer(regs); |
256 | unsigned long instr = *(unsigned long *)pc; | |
257 | int reg = (instr >> 12) & 15; | |
258 | ||
259 | /* | |
260 | * If the instruction being executed was a read, | |
261 | * make it look like it read all-ones. | |
262 | */ | |
263 | if ((instr & 0x0c100000) == 0x04100000) { | |
264 | unsigned long val; | |
265 | ||
266 | if (instr & 0x00400000) | |
267 | val = 255; | |
268 | else | |
269 | val = -1; | |
270 | ||
271 | regs->uregs[reg] = val; | |
272 | regs->ARM_pc += 4; | |
273 | return 0; | |
274 | } | |
275 | ||
276 | if ((instr & 0x0e100090) == 0x00100090) { | |
277 | regs->uregs[reg] = -1; | |
278 | regs->ARM_pc += 4; | |
279 | return 0; | |
280 | } | |
281 | ||
282 | return 1; | |
bb38919e SC |
283 | } |
284 | ||
9ab021b6 | 285 | static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) |
bb38919e | 286 | { |
e6f1fef0 | 287 | switch (imx6_pcie->variant) { |
9b3fe679 AS |
288 | case IMX7D: |
289 | reset_control_assert(imx6_pcie->pciephy_reset); | |
290 | reset_control_assert(imx6_pcie->apps_reset); | |
291 | break; | |
e6f1fef0 | 292 | case IMX6SX: |
e3c06cd0 CF |
293 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, |
294 | IMX6SX_GPR12_PCIE_TEST_POWERDOWN, | |
295 | IMX6SX_GPR12_PCIE_TEST_POWERDOWN); | |
296 | /* Force PCIe PHY reset */ | |
297 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, | |
298 | IMX6SX_GPR5_PCIE_BTNRST_RESET, | |
299 | IMX6SX_GPR5_PCIE_BTNRST_RESET); | |
e6f1fef0 | 300 | break; |
4d31c610 AS |
301 | case IMX6QP: |
302 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, | |
303 | IMX6Q_GPR1_PCIE_SW_RST, | |
304 | IMX6Q_GPR1_PCIE_SW_RST); | |
305 | break; | |
e6f1fef0 | 306 | case IMX6Q: |
e6f1fef0 AS |
307 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, |
308 | IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); | |
309 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, | |
310 | IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); | |
311 | break; | |
3e3e406e | 312 | } |
bb38919e SC |
313 | } |
314 | ||
4d1821e7 BH |
315 | static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) |
316 | { | |
442ec4c0 KVA |
317 | struct dw_pcie *pci = imx6_pcie->pci; |
318 | struct device *dev = pci->dev; | |
e6f1fef0 | 319 | int ret = 0; |
e3c06cd0 | 320 | |
e6f1fef0 AS |
321 | switch (imx6_pcie->variant) { |
322 | case IMX6SX: | |
e3c06cd0 CF |
323 | ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); |
324 | if (ret) { | |
13957652 | 325 | dev_err(dev, "unable to enable pcie_axi clock\n"); |
e6f1fef0 | 326 | break; |
e3c06cd0 CF |
327 | } |
328 | ||
329 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | |
330 | IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0); | |
e6f1fef0 | 331 | break; |
4d31c610 | 332 | case IMX6QP: /* FALLTHROUGH */ |
e6f1fef0 AS |
333 | case IMX6Q: |
334 | /* power up core phy and enable ref clock */ | |
335 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, | |
336 | IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); | |
337 | /* | |
338 | * the async reset input need ref clock to sync internally, | |
339 | * when the ref clock comes after reset, internal synced | |
340 | * reset time is too short, cannot meet the requirement. | |
341 | * add one ~10us delay here. | |
342 | */ | |
343 | udelay(10); | |
344 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, | |
345 | IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); | |
346 | break; | |
9b3fe679 AS |
347 | case IMX7D: |
348 | break; | |
e3c06cd0 CF |
349 | } |
350 | ||
e6f1fef0 | 351 | return ret; |
4d1821e7 BH |
352 | } |
353 | ||
9b3fe679 AS |
354 | static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) |
355 | { | |
356 | u32 val; | |
357 | unsigned int retries; | |
358 | struct device *dev = imx6_pcie->pci->dev; | |
359 | ||
360 | for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { | |
361 | regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val); | |
362 | ||
363 | if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) | |
364 | return; | |
365 | ||
366 | usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN, | |
367 | PHY_PLL_LOCK_WAIT_USLEEP_MAX); | |
368 | } | |
369 | ||
370 | dev_err(dev, "PCIe PLL lock timeout\n"); | |
371 | } | |
372 | ||
9ab021b6 | 373 | static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) |
bb38919e | 374 | { |
442ec4c0 KVA |
375 | struct dw_pcie *pci = imx6_pcie->pci; |
376 | struct device *dev = pci->dev; | |
bb38919e SC |
377 | int ret; |
378 | ||
57526136 | 379 | ret = clk_prepare_enable(imx6_pcie->pcie_phy); |
bb38919e | 380 | if (ret) { |
13957652 | 381 | dev_err(dev, "unable to enable pcie_phy clock\n"); |
9ab021b6 | 382 | return; |
bb38919e SC |
383 | } |
384 | ||
57526136 | 385 | ret = clk_prepare_enable(imx6_pcie->pcie_bus); |
bb38919e | 386 | if (ret) { |
13957652 | 387 | dev_err(dev, "unable to enable pcie_bus clock\n"); |
57526136 | 388 | goto err_pcie_bus; |
bb38919e SC |
389 | } |
390 | ||
57526136 | 391 | ret = clk_prepare_enable(imx6_pcie->pcie); |
bb38919e | 392 | if (ret) { |
13957652 | 393 | dev_err(dev, "unable to enable pcie clock\n"); |
57526136 | 394 | goto err_pcie; |
bb38919e SC |
395 | } |
396 | ||
4d1821e7 BH |
397 | ret = imx6_pcie_enable_ref_clk(imx6_pcie); |
398 | if (ret) { | |
13957652 | 399 | dev_err(dev, "unable to enable pcie ref clock\n"); |
4d1821e7 BH |
400 | goto err_ref_clk; |
401 | } | |
3fce0e88 | 402 | |
a2fa6f64 RZ |
403 | /* allow the clocks to stabilize */ |
404 | usleep_range(200, 500); | |
405 | ||
bc9ef770 | 406 | /* Some boards don't have PCIe reset GPIO. */ |
b2d7a9cd | 407 | if (gpio_is_valid(imx6_pcie->reset_gpio)) { |
3ea8529a PŠ |
408 | gpio_set_value_cansleep(imx6_pcie->reset_gpio, |
409 | imx6_pcie->gpio_active_high); | |
bc9ef770 | 410 | msleep(100); |
3ea8529a PŠ |
411 | gpio_set_value_cansleep(imx6_pcie->reset_gpio, |
412 | !imx6_pcie->gpio_active_high); | |
bc9ef770 | 413 | } |
e3c06cd0 | 414 | |
4d31c610 | 415 | switch (imx6_pcie->variant) { |
9b3fe679 AS |
416 | case IMX7D: |
417 | reset_control_deassert(imx6_pcie->pciephy_reset); | |
418 | imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); | |
419 | break; | |
4d31c610 | 420 | case IMX6SX: |
e3c06cd0 CF |
421 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, |
422 | IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); | |
4d31c610 AS |
423 | break; |
424 | case IMX6QP: | |
425 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, | |
426 | IMX6Q_GPR1_PCIE_SW_RST, 0); | |
427 | ||
428 | usleep_range(200, 500); | |
429 | break; | |
430 | case IMX6Q: /* Nothing to do */ | |
431 | break; | |
432 | } | |
e3c06cd0 | 433 | |
9ab021b6 | 434 | return; |
bb38919e | 435 | |
4d1821e7 BH |
436 | err_ref_clk: |
437 | clk_disable_unprepare(imx6_pcie->pcie); | |
57526136 LS |
438 | err_pcie: |
439 | clk_disable_unprepare(imx6_pcie->pcie_bus); | |
440 | err_pcie_bus: | |
441 | clk_disable_unprepare(imx6_pcie->pcie_phy); | |
bb38919e SC |
442 | } |
443 | ||
e7d7705a | 444 | static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) |
bb38919e | 445 | { |
9b3fe679 AS |
446 | switch (imx6_pcie->variant) { |
447 | case IMX7D: | |
448 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | |
449 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); | |
450 | break; | |
451 | case IMX6SX: | |
e3c06cd0 CF |
452 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, |
453 | IMX6SX_GPR12_PCIE_RX_EQ_MASK, | |
454 | IMX6SX_GPR12_PCIE_RX_EQ_2); | |
9b3fe679 AS |
455 | /* FALLTHROUGH */ |
456 | default: | |
457 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | |
458 | IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); | |
e3c06cd0 | 459 | |
9b3fe679 AS |
460 | /* configure constant input signal to the pcie ctrl and phy */ |
461 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | |
462 | IMX6Q_GPR12_LOS_LEVEL, 9 << 4); | |
463 | ||
464 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, | |
465 | IMX6Q_GPR8_TX_DEEMPH_GEN1, | |
466 | imx6_pcie->tx_deemph_gen1 << 0); | |
467 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, | |
468 | IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, | |
469 | imx6_pcie->tx_deemph_gen2_3p5db << 6); | |
470 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, | |
471 | IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, | |
472 | imx6_pcie->tx_deemph_gen2_6db << 12); | |
473 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, | |
474 | IMX6Q_GPR8_TX_SWING_FULL, | |
475 | imx6_pcie->tx_swing_full << 18); | |
476 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, | |
477 | IMX6Q_GPR8_TX_SWING_LOW, | |
478 | imx6_pcie->tx_swing_low << 25); | |
479 | break; | |
480 | } | |
bb38919e | 481 | |
bb38919e SC |
482 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, |
483 | IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); | |
bb38919e SC |
484 | } |
485 | ||
e7d7705a | 486 | static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) |
66a60f93 | 487 | { |
442ec4c0 KVA |
488 | struct dw_pcie *pci = imx6_pcie->pci; |
489 | struct device *dev = pci->dev; | |
13957652 | 490 | |
886bc5ce | 491 | /* check if the link is up or not */ |
442ec4c0 | 492 | if (!dw_pcie_wait_for_link(pci)) |
886bc5ce | 493 | return 0; |
66a60f93 | 494 | |
13957652 | 495 | dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", |
442ec4c0 KVA |
496 | dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), |
497 | dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); | |
886bc5ce | 498 | return -ETIMEDOUT; |
66a60f93 MV |
499 | } |
500 | ||
e7d7705a | 501 | static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) |
a0427464 | 502 | { |
442ec4c0 KVA |
503 | struct dw_pcie *pci = imx6_pcie->pci; |
504 | struct device *dev = pci->dev; | |
1c7fae18 | 505 | u32 tmp; |
a0427464 TK |
506 | unsigned int retries; |
507 | ||
508 | for (retries = 0; retries < 200; retries++) { | |
442ec4c0 | 509 | tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); |
a0427464 TK |
510 | /* Test if the speed change finished. */ |
511 | if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) | |
512 | return 0; | |
513 | usleep_range(100, 1000); | |
514 | } | |
515 | ||
13957652 | 516 | dev_err(dev, "Speed change timeout\n"); |
a0427464 | 517 | return -EINVAL; |
66a60f93 MV |
518 | } |
519 | ||
d1dc9749 LS |
520 | static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg) |
521 | { | |
e7d7705a | 522 | struct imx6_pcie *imx6_pcie = arg; |
442ec4c0 KVA |
523 | struct dw_pcie *pci = imx6_pcie->pci; |
524 | struct pcie_port *pp = &pci->pp; | |
d1dc9749 LS |
525 | |
526 | return dw_handle_msi_irq(pp); | |
527 | } | |
528 | ||
e7d7705a | 529 | static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) |
bb38919e | 530 | { |
442ec4c0 KVA |
531 | struct dw_pcie *pci = imx6_pcie->pci; |
532 | struct device *dev = pci->dev; | |
1c7fae18 | 533 | u32 tmp; |
a0427464 | 534 | int ret; |
fa33a6d8 MV |
535 | |
536 | /* | |
537 | * Force Gen1 operation when starting the link. In case the link is | |
538 | * started in Gen2 mode, there is a possibility the devices on the | |
539 | * bus will not be detected at all. This happens with PCIe switches. | |
540 | */ | |
442ec4c0 | 541 | tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); |
fa33a6d8 MV |
542 | tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; |
543 | tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; | |
442ec4c0 | 544 | dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); |
fa33a6d8 MV |
545 | |
546 | /* Start LTSSM. */ | |
9b3fe679 AS |
547 | if (imx6_pcie->variant == IMX7D) |
548 | reset_control_deassert(imx6_pcie->apps_reset); | |
549 | else | |
550 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | |
551 | IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); | |
fa33a6d8 | 552 | |
e7d7705a | 553 | ret = imx6_pcie_wait_for_link(imx6_pcie); |
caf3f562 | 554 | if (ret) |
54a47a83 | 555 | goto err_reset_phy; |
fa33a6d8 | 556 | |
a5fcec48 TH |
557 | if (imx6_pcie->link_gen == 2) { |
558 | /* Allow Gen2 mode after the link is up. */ | |
442ec4c0 | 559 | tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); |
a5fcec48 TH |
560 | tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; |
561 | tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; | |
442ec4c0 | 562 | dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); |
fa33a6d8 | 563 | |
e6dcd87f | 564 | /* |
93b226f9 AS |
565 | * Start Directed Speed Change so the best possible |
566 | * speed both link partners support can be negotiated. | |
e6dcd87f | 567 | */ |
93b226f9 AS |
568 | tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); |
569 | tmp |= PORT_LOGIC_SPEED_CHANGE; | |
570 | dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); | |
571 | ||
572 | if (imx6_pcie->variant != IMX7D) { | |
573 | /* | |
574 | * On i.MX7, DIRECT_SPEED_CHANGE behaves differently | |
575 | * from i.MX6 family when no link speed transition | |
576 | * occurs and we go Gen1 -> yep, Gen1. The difference | |
577 | * is that, in such case, it will not be cleared by HW | |
578 | * which will cause the following code to report false | |
579 | * failure. | |
580 | */ | |
581 | ||
582 | ret = imx6_pcie_wait_for_speed_change(imx6_pcie); | |
583 | if (ret) { | |
584 | dev_err(dev, "Failed to bring link up!\n"); | |
585 | goto err_reset_phy; | |
586 | } | |
587 | } | |
e6dcd87f | 588 | |
93b226f9 AS |
589 | /* Make sure link training is finished as well! */ |
590 | ret = imx6_pcie_wait_for_link(imx6_pcie); | |
e6dcd87f AS |
591 | if (ret) { |
592 | dev_err(dev, "Failed to bring link up!\n"); | |
593 | goto err_reset_phy; | |
594 | } | |
93b226f9 AS |
595 | } else { |
596 | dev_info(dev, "Link: Gen2 disabled\n"); | |
fa33a6d8 MV |
597 | } |
598 | ||
442ec4c0 | 599 | tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); |
13957652 | 600 | dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); |
a0427464 | 601 | return 0; |
54a47a83 LS |
602 | |
603 | err_reset_phy: | |
13957652 | 604 | dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", |
442ec4c0 KVA |
605 | dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), |
606 | dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); | |
2a6a85d5 | 607 | imx6_pcie_reset_phy(imx6_pcie); |
54a47a83 | 608 | return ret; |
fa33a6d8 MV |
609 | } |
610 | ||
611 | static void imx6_pcie_host_init(struct pcie_port *pp) | |
612 | { | |
442ec4c0 KVA |
613 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
614 | struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); | |
bb38919e | 615 | |
e7d7705a BH |
616 | imx6_pcie_assert_core_reset(imx6_pcie); |
617 | imx6_pcie_init_phy(imx6_pcie); | |
618 | imx6_pcie_deassert_core_reset(imx6_pcie); | |
bb38919e | 619 | dw_pcie_setup_rc(pp); |
e7d7705a | 620 | imx6_pcie_establish_link(imx6_pcie); |
d1dc9749 LS |
621 | |
622 | if (IS_ENABLED(CONFIG_PCI_MSI)) | |
623 | dw_pcie_msi_init(pp); | |
bb38919e SC |
624 | } |
625 | ||
442ec4c0 | 626 | static int imx6_pcie_link_up(struct dw_pcie *pci) |
bb38919e | 627 | { |
442ec4c0 | 628 | return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) & |
4d107d3b | 629 | PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; |
bb38919e SC |
630 | } |
631 | ||
442ec4c0 | 632 | static struct dw_pcie_host_ops imx6_pcie_host_ops = { |
bb38919e SC |
633 | .host_init = imx6_pcie_host_init, |
634 | }; | |
635 | ||
bde4a5a0 AS |
636 | static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, |
637 | struct platform_device *pdev) | |
bb38919e | 638 | { |
442ec4c0 KVA |
639 | struct dw_pcie *pci = imx6_pcie->pci; |
640 | struct pcie_port *pp = &pci->pp; | |
641 | struct device *dev = &pdev->dev; | |
bb38919e SC |
642 | int ret; |
643 | ||
d1dc9749 LS |
644 | if (IS_ENABLED(CONFIG_PCI_MSI)) { |
645 | pp->msi_irq = platform_get_irq_byname(pdev, "msi"); | |
646 | if (pp->msi_irq <= 0) { | |
13957652 | 647 | dev_err(dev, "failed to get MSI irq\n"); |
d1dc9749 LS |
648 | return -ENODEV; |
649 | } | |
650 | ||
13957652 | 651 | ret = devm_request_irq(dev, pp->msi_irq, |
d88a7ef9 | 652 | imx6_pcie_msi_handler, |
8ff0ef99 | 653 | IRQF_SHARED | IRQF_NO_THREAD, |
e7d7705a | 654 | "mx6-pcie-msi", imx6_pcie); |
d1dc9749 | 655 | if (ret) { |
13957652 | 656 | dev_err(dev, "failed to request MSI irq\n"); |
89b2d4f1 | 657 | return ret; |
d1dc9749 LS |
658 | } |
659 | } | |
660 | ||
bb38919e SC |
661 | pp->root_bus_nr = -1; |
662 | pp->ops = &imx6_pcie_host_ops; | |
663 | ||
bb38919e SC |
664 | ret = dw_pcie_host_init(pp); |
665 | if (ret) { | |
13957652 | 666 | dev_err(dev, "failed to initialize host\n"); |
bb38919e SC |
667 | return ret; |
668 | } | |
669 | ||
670 | return 0; | |
671 | } | |
672 | ||
442ec4c0 KVA |
673 | static const struct dw_pcie_ops dw_pcie_ops = { |
674 | .link_up = imx6_pcie_link_up, | |
675 | }; | |
676 | ||
bde4a5a0 | 677 | static int imx6_pcie_probe(struct platform_device *pdev) |
bb38919e | 678 | { |
13957652 | 679 | struct device *dev = &pdev->dev; |
442ec4c0 | 680 | struct dw_pcie *pci; |
bb38919e | 681 | struct imx6_pcie *imx6_pcie; |
bb38919e | 682 | struct resource *dbi_base; |
13957652 | 683 | struct device_node *node = dev->of_node; |
bb38919e SC |
684 | int ret; |
685 | ||
13957652 | 686 | imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); |
bb38919e SC |
687 | if (!imx6_pcie) |
688 | return -ENOMEM; | |
689 | ||
442ec4c0 KVA |
690 | pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); |
691 | if (!pci) | |
692 | return -ENOMEM; | |
693 | ||
694 | pci->dev = dev; | |
695 | pci->ops = &dw_pcie_ops; | |
bb38919e | 696 | |
c0464062 | 697 | imx6_pcie->pci = pci; |
e6f1fef0 | 698 | imx6_pcie->variant = |
13957652 | 699 | (enum imx6_pcie_variants)of_device_get_match_data(dev); |
e3c06cd0 | 700 | |
bb38919e | 701 | dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
442ec4c0 KVA |
702 | pci->dbi_base = devm_ioremap_resource(dev, dbi_base); |
703 | if (IS_ERR(pci->dbi_base)) | |
704 | return PTR_ERR(pci->dbi_base); | |
bb38919e SC |
705 | |
706 | /* Fetch GPIOs */ | |
c5af4074 BH |
707 | imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); |
708 | imx6_pcie->gpio_active_high = of_property_read_bool(node, | |
3ea8529a | 709 | "reset-gpio-active-high"); |
b2d7a9cd | 710 | if (gpio_is_valid(imx6_pcie->reset_gpio)) { |
13957652 | 711 | ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio, |
3ea8529a PŠ |
712 | imx6_pcie->gpio_active_high ? |
713 | GPIOF_OUT_INIT_HIGH : | |
714 | GPIOF_OUT_INIT_LOW, | |
715 | "PCIe reset"); | |
b2d7a9cd | 716 | if (ret) { |
13957652 | 717 | dev_err(dev, "unable to get reset gpio\n"); |
b2d7a9cd FE |
718 | return ret; |
719 | } | |
bde4a5a0 AS |
720 | } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { |
721 | return imx6_pcie->reset_gpio; | |
b2d7a9cd | 722 | } |
bb38919e | 723 | |
bb38919e | 724 | /* Fetch clocks */ |
13957652 | 725 | imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); |
57526136 | 726 | if (IS_ERR(imx6_pcie->pcie_phy)) { |
13957652 | 727 | dev_err(dev, "pcie_phy clock source missing or invalid\n"); |
57526136 | 728 | return PTR_ERR(imx6_pcie->pcie_phy); |
bb38919e SC |
729 | } |
730 | ||
13957652 | 731 | imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); |
57526136 | 732 | if (IS_ERR(imx6_pcie->pcie_bus)) { |
13957652 | 733 | dev_err(dev, "pcie_bus clock source missing or invalid\n"); |
57526136 | 734 | return PTR_ERR(imx6_pcie->pcie_bus); |
bb38919e SC |
735 | } |
736 | ||
13957652 | 737 | imx6_pcie->pcie = devm_clk_get(dev, "pcie"); |
57526136 | 738 | if (IS_ERR(imx6_pcie->pcie)) { |
13957652 | 739 | dev_err(dev, "pcie clock source missing or invalid\n"); |
57526136 | 740 | return PTR_ERR(imx6_pcie->pcie); |
bb38919e SC |
741 | } |
742 | ||
9b3fe679 AS |
743 | switch (imx6_pcie->variant) { |
744 | case IMX6SX: | |
13957652 | 745 | imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, |
e3c06cd0 CF |
746 | "pcie_inbound_axi"); |
747 | if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { | |
21b72450 | 748 | dev_err(dev, "pcie_inbound_axi clock missing or invalid\n"); |
e3c06cd0 CF |
749 | return PTR_ERR(imx6_pcie->pcie_inbound_axi); |
750 | } | |
9b3fe679 AS |
751 | break; |
752 | case IMX7D: | |
753 | imx6_pcie->pciephy_reset = devm_reset_control_get(dev, | |
754 | "pciephy"); | |
755 | if (IS_ERR(imx6_pcie->pciephy_reset)) { | |
7221547e | 756 | dev_err(dev, "Failed to get PCIEPHY reset control\n"); |
9b3fe679 AS |
757 | return PTR_ERR(imx6_pcie->pciephy_reset); |
758 | } | |
759 | ||
760 | imx6_pcie->apps_reset = devm_reset_control_get(dev, "apps"); | |
761 | if (IS_ERR(imx6_pcie->apps_reset)) { | |
7221547e | 762 | dev_err(dev, "Failed to get PCIE APPS reset control\n"); |
9b3fe679 AS |
763 | return PTR_ERR(imx6_pcie->apps_reset); |
764 | } | |
765 | break; | |
766 | default: | |
767 | break; | |
e3c06cd0 CF |
768 | } |
769 | ||
bb38919e SC |
770 | /* Grab GPR config register range */ |
771 | imx6_pcie->iomuxc_gpr = | |
772 | syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); | |
773 | if (IS_ERR(imx6_pcie->iomuxc_gpr)) { | |
13957652 | 774 | dev_err(dev, "unable to find iomuxc registers\n"); |
b391bf31 | 775 | return PTR_ERR(imx6_pcie->iomuxc_gpr); |
bb38919e | 776 | } |
28e3abe5 JW |
777 | |
778 | /* Grab PCIe PHY Tx Settings */ | |
779 | if (of_property_read_u32(node, "fsl,tx-deemph-gen1", | |
780 | &imx6_pcie->tx_deemph_gen1)) | |
781 | imx6_pcie->tx_deemph_gen1 = 0; | |
782 | ||
783 | if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db", | |
784 | &imx6_pcie->tx_deemph_gen2_3p5db)) | |
785 | imx6_pcie->tx_deemph_gen2_3p5db = 0; | |
786 | ||
787 | if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db", | |
788 | &imx6_pcie->tx_deemph_gen2_6db)) | |
789 | imx6_pcie->tx_deemph_gen2_6db = 20; | |
790 | ||
791 | if (of_property_read_u32(node, "fsl,tx-swing-full", | |
792 | &imx6_pcie->tx_swing_full)) | |
793 | imx6_pcie->tx_swing_full = 127; | |
794 | ||
795 | if (of_property_read_u32(node, "fsl,tx-swing-low", | |
796 | &imx6_pcie->tx_swing_low)) | |
797 | imx6_pcie->tx_swing_low = 127; | |
bb38919e | 798 | |
a5fcec48 | 799 | /* Limit link speed */ |
c5af4074 | 800 | ret = of_property_read_u32(node, "fsl,max-link-speed", |
a5fcec48 TH |
801 | &imx6_pcie->link_gen); |
802 | if (ret) | |
803 | imx6_pcie->link_gen = 1; | |
804 | ||
9bcf0a6f KVA |
805 | platform_set_drvdata(pdev, imx6_pcie); |
806 | ||
e7d7705a | 807 | ret = imx6_add_pcie_port(imx6_pcie, pdev); |
bb38919e | 808 | if (ret < 0) |
b391bf31 | 809 | return ret; |
bb38919e | 810 | |
bb38919e | 811 | return 0; |
bb38919e SC |
812 | } |
813 | ||
3e3e406e LS |
814 | static void imx6_pcie_shutdown(struct platform_device *pdev) |
815 | { | |
816 | struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev); | |
817 | ||
818 | /* bring down link, so bootloader gets clean state in case of reboot */ | |
e7d7705a | 819 | imx6_pcie_assert_core_reset(imx6_pcie); |
3e3e406e LS |
820 | } |
821 | ||
bb38919e | 822 | static const struct of_device_id imx6_pcie_of_match[] = { |
e6f1fef0 AS |
823 | { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, |
824 | { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, | |
4d31c610 | 825 | { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, |
9b3fe679 | 826 | { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, |
bb38919e SC |
827 | {}, |
828 | }; | |
bb38919e SC |
829 | |
830 | static struct platform_driver imx6_pcie_driver = { | |
831 | .driver = { | |
832 | .name = "imx6q-pcie", | |
8bcadbe1 | 833 | .of_match_table = imx6_pcie_of_match, |
a5f40e80 | 834 | .suppress_bind_attrs = true, |
bb38919e | 835 | }, |
bde4a5a0 | 836 | .probe = imx6_pcie_probe, |
3e3e406e | 837 | .shutdown = imx6_pcie_shutdown, |
bb38919e SC |
838 | }; |
839 | ||
bb38919e SC |
840 | static int __init imx6_pcie_init(void) |
841 | { | |
bde4a5a0 AS |
842 | /* |
843 | * Since probe() can be deferred we need to make sure that | |
844 | * hook_fault_code is not called after __init memory is freed | |
845 | * by kernel and since imx6q_pcie_abort_handler() is a no-op, | |
846 | * we can install the handler here without risking it | |
847 | * accessing some uninitialized driver state. | |
848 | */ | |
415b6185 LS |
849 | hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, |
850 | "external abort on non-linefetch"); | |
bde4a5a0 AS |
851 | |
852 | return platform_driver_register(&imx6_pcie_driver); | |
bb38919e | 853 | } |
f90d8e84 | 854 | device_initcall(imx6_pcie_init); |