diff options
author | Jingoo Han <jg1.han@samsung.com> | 2013-07-31 04:14:10 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-08-12 14:18:20 -0400 |
commit | 4b1ced841b2e31470ae4bb47988891754ce4d8c7 (patch) | |
tree | 8fdf59944d73d9b946922e9d3c42239acce6aa8f /drivers/pci/host/pci-exynos.c | |
parent | 5477a33b51b7282aca731213dc592b5f0c4e7c13 (diff) |
PCI: exynos: Split into Synopsys part and Exynos part
Exynos PCIe IP consists of Synopsys specific part and Exynos
specific part. Only core block is a Synopsys Designware part;
other parts are Exynos specific.
Also, the Synopsys Designware part can be shared with other
platforms; thus, it can be split two parts such as Synopsys
Designware part and Exynos specific part.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Pratyush Anand <pratyush.anand@st.com>
Cc: Mohit KUMAR <Mohit.KUMAR@st.com>
Diffstat (limited to 'drivers/pci/host/pci-exynos.c')
-rw-r--r-- | drivers/pci/host/pci-exynos.c | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c new file mode 100644 index 000000000000..012ca8aec71a --- /dev/null +++ b/drivers/pci/host/pci-exynos.c | |||
@@ -0,0 +1,530 @@ | |||
1 | /* | ||
2 | * PCIe host controller driver for Samsung EXYNOS SoCs | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * Author: Jingoo Han <jg1.han@samsung.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/interrupt.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of_gpio.h> | ||
21 | #include <linux/pci.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/resource.h> | ||
24 | #include <linux/signal.h> | ||
25 | #include <linux/types.h> | ||
26 | |||
27 | #include "pcie-designware.h" | ||
28 | |||
29 | #define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp) | ||
30 | |||
31 | struct exynos_pcie { | ||
32 | void __iomem *elbi_base; | ||
33 | void __iomem *phy_base; | ||
34 | void __iomem *block_base; | ||
35 | int reset_gpio; | ||
36 | struct clk *clk; | ||
37 | struct clk *bus_clk; | ||
38 | struct pcie_port pp; | ||
39 | }; | ||
40 | |||
41 | /* PCIe ELBI registers */ | ||
42 | #define PCIE_IRQ_PULSE 0x000 | ||
43 | #define IRQ_INTA_ASSERT (0x1 << 0) | ||
44 | #define IRQ_INTB_ASSERT (0x1 << 2) | ||
45 | #define IRQ_INTC_ASSERT (0x1 << 4) | ||
46 | #define IRQ_INTD_ASSERT (0x1 << 6) | ||
47 | #define PCIE_IRQ_LEVEL 0x004 | ||
48 | #define PCIE_IRQ_SPECIAL 0x008 | ||
49 | #define PCIE_IRQ_EN_PULSE 0x00c | ||
50 | #define PCIE_IRQ_EN_LEVEL 0x010 | ||
51 | #define PCIE_IRQ_EN_SPECIAL 0x014 | ||
52 | #define PCIE_PWR_RESET 0x018 | ||
53 | #define PCIE_CORE_RESET 0x01c | ||
54 | #define PCIE_CORE_RESET_ENABLE (0x1 << 0) | ||
55 | #define PCIE_STICKY_RESET 0x020 | ||
56 | #define PCIE_NONSTICKY_RESET 0x024 | ||
57 | #define PCIE_APP_INIT_RESET 0x028 | ||
58 | #define PCIE_APP_LTSSM_ENABLE 0x02c | ||
59 | #define PCIE_ELBI_RDLH_LINKUP 0x064 | ||
60 | #define PCIE_ELBI_LTSSM_ENABLE 0x1 | ||
61 | #define PCIE_ELBI_SLV_AWMISC 0x11c | ||
62 | #define PCIE_ELBI_SLV_ARMISC 0x120 | ||
63 | #define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21) | ||
64 | |||
65 | /* PCIe Purple registers */ | ||
66 | #define PCIE_PHY_GLOBAL_RESET 0x000 | ||
67 | #define PCIE_PHY_COMMON_RESET 0x004 | ||
68 | #define PCIE_PHY_CMN_REG 0x008 | ||
69 | #define PCIE_PHY_MAC_RESET 0x00c | ||
70 | #define PCIE_PHY_PLL_LOCKED 0x010 | ||
71 | #define PCIE_PHY_TRSVREG_RESET 0x020 | ||
72 | #define PCIE_PHY_TRSV_RESET 0x024 | ||
73 | |||
74 | /* PCIe PHY registers */ | ||
75 | #define PCIE_PHY_IMPEDANCE 0x004 | ||
76 | #define PCIE_PHY_PLL_DIV_0 0x008 | ||
77 | #define PCIE_PHY_PLL_BIAS 0x00c | ||
78 | #define PCIE_PHY_DCC_FEEDBACK 0x014 | ||
79 | #define PCIE_PHY_PLL_DIV_1 0x05c | ||
80 | #define PCIE_PHY_TRSV0_EMP_LVL 0x084 | ||
81 | #define PCIE_PHY_TRSV0_DRV_LVL 0x088 | ||
82 | #define PCIE_PHY_TRSV0_RXCDR 0x0ac | ||
83 | #define PCIE_PHY_TRSV0_LVCC 0x0dc | ||
84 | #define PCIE_PHY_TRSV1_EMP_LVL 0x144 | ||
85 | #define PCIE_PHY_TRSV1_RXCDR 0x16c | ||
86 | #define PCIE_PHY_TRSV1_LVCC 0x19c | ||
87 | #define PCIE_PHY_TRSV2_EMP_LVL 0x204 | ||
88 | #define PCIE_PHY_TRSV2_RXCDR 0x22c | ||
89 | #define PCIE_PHY_TRSV2_LVCC 0x25c | ||
90 | #define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 | ||
91 | #define PCIE_PHY_TRSV3_RXCDR 0x2ec | ||
92 | #define PCIE_PHY_TRSV3_LVCC 0x31c | ||
93 | |||
94 | static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on) | ||
95 | { | ||
96 | u32 val; | ||
97 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
98 | |||
99 | if (on) { | ||
100 | val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC); | ||
101 | val |= PCIE_ELBI_SLV_DBI_ENABLE; | ||
102 | writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC); | ||
103 | } else { | ||
104 | val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC); | ||
105 | val &= ~PCIE_ELBI_SLV_DBI_ENABLE; | ||
106 | writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on) | ||
111 | { | ||
112 | u32 val; | ||
113 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
114 | |||
115 | if (on) { | ||
116 | val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC); | ||
117 | val |= PCIE_ELBI_SLV_DBI_ENABLE; | ||
118 | writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC); | ||
119 | } else { | ||
120 | val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC); | ||
121 | val &= ~PCIE_ELBI_SLV_DBI_ENABLE; | ||
122 | writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static void exynos_pcie_assert_core_reset(struct pcie_port *pp) | ||
127 | { | ||
128 | u32 val; | ||
129 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
130 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
131 | |||
132 | val = readl(elbi_base + PCIE_CORE_RESET); | ||
133 | val &= ~PCIE_CORE_RESET_ENABLE; | ||
134 | writel(val, elbi_base + PCIE_CORE_RESET); | ||
135 | writel(0, elbi_base + PCIE_PWR_RESET); | ||
136 | writel(0, elbi_base + PCIE_STICKY_RESET); | ||
137 | writel(0, elbi_base + PCIE_NONSTICKY_RESET); | ||
138 | } | ||
139 | |||
140 | static void exynos_pcie_deassert_core_reset(struct pcie_port *pp) | ||
141 | { | ||
142 | u32 val; | ||
143 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
144 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
145 | void __iomem *block_base = exynos_pcie->block_base; | ||
146 | |||
147 | val = readl(elbi_base + PCIE_CORE_RESET); | ||
148 | val |= PCIE_CORE_RESET_ENABLE; | ||
149 | writel(val, elbi_base + PCIE_CORE_RESET); | ||
150 | writel(1, elbi_base + PCIE_STICKY_RESET); | ||
151 | writel(1, elbi_base + PCIE_NONSTICKY_RESET); | ||
152 | writel(1, elbi_base + PCIE_APP_INIT_RESET); | ||
153 | writel(0, elbi_base + PCIE_APP_INIT_RESET); | ||
154 | writel(1, block_base + PCIE_PHY_MAC_RESET); | ||
155 | } | ||
156 | |||
157 | static void exynos_pcie_assert_phy_reset(struct pcie_port *pp) | ||
158 | { | ||
159 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
160 | void __iomem *block_base = exynos_pcie->block_base; | ||
161 | |||
162 | writel(0, block_base + PCIE_PHY_MAC_RESET); | ||
163 | writel(1, block_base + PCIE_PHY_GLOBAL_RESET); | ||
164 | } | ||
165 | |||
166 | static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) | ||
167 | { | ||
168 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
169 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
170 | void __iomem *block_base = exynos_pcie->block_base; | ||
171 | |||
172 | writel(0, block_base + PCIE_PHY_GLOBAL_RESET); | ||
173 | writel(1, elbi_base + PCIE_PWR_RESET); | ||
174 | writel(0, block_base + PCIE_PHY_COMMON_RESET); | ||
175 | writel(0, block_base + PCIE_PHY_CMN_REG); | ||
176 | writel(0, block_base + PCIE_PHY_TRSVREG_RESET); | ||
177 | writel(0, block_base + PCIE_PHY_TRSV_RESET); | ||
178 | } | ||
179 | |||
180 | static void exynos_pcie_init_phy(struct pcie_port *pp) | ||
181 | { | ||
182 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
183 | void __iomem *phy_base = exynos_pcie->phy_base; | ||
184 | |||
185 | /* DCC feedback control off */ | ||
186 | writel(0x29, phy_base + PCIE_PHY_DCC_FEEDBACK); | ||
187 | |||
188 | /* set TX/RX impedance */ | ||
189 | writel(0xd5, phy_base + PCIE_PHY_IMPEDANCE); | ||
190 | |||
191 | /* set 50Mhz PHY clock */ | ||
192 | writel(0x14, phy_base + PCIE_PHY_PLL_DIV_0); | ||
193 | writel(0x12, phy_base + PCIE_PHY_PLL_DIV_1); | ||
194 | |||
195 | /* set TX Differential output for lane 0 */ | ||
196 | writel(0x7f, phy_base + PCIE_PHY_TRSV0_DRV_LVL); | ||
197 | |||
198 | /* set TX Pre-emphasis Level Control for lane 0 to minimum */ | ||
199 | writel(0x0, phy_base + PCIE_PHY_TRSV0_EMP_LVL); | ||
200 | |||
201 | /* set RX clock and data recovery bandwidth */ | ||
202 | writel(0xe7, phy_base + PCIE_PHY_PLL_BIAS); | ||
203 | writel(0x82, phy_base + PCIE_PHY_TRSV0_RXCDR); | ||
204 | writel(0x82, phy_base + PCIE_PHY_TRSV1_RXCDR); | ||
205 | writel(0x82, phy_base + PCIE_PHY_TRSV2_RXCDR); | ||
206 | writel(0x82, phy_base + PCIE_PHY_TRSV3_RXCDR); | ||
207 | |||
208 | /* change TX Pre-emphasis Level Control for lanes */ | ||
209 | writel(0x39, phy_base + PCIE_PHY_TRSV0_EMP_LVL); | ||
210 | writel(0x39, phy_base + PCIE_PHY_TRSV1_EMP_LVL); | ||
211 | writel(0x39, phy_base + PCIE_PHY_TRSV2_EMP_LVL); | ||
212 | writel(0x39, phy_base + PCIE_PHY_TRSV3_EMP_LVL); | ||
213 | |||
214 | /* set LVCC */ | ||
215 | writel(0x20, phy_base + PCIE_PHY_TRSV0_LVCC); | ||
216 | writel(0xa0, phy_base + PCIE_PHY_TRSV1_LVCC); | ||
217 | writel(0xa0, phy_base + PCIE_PHY_TRSV2_LVCC); | ||
218 | writel(0xa0, phy_base + PCIE_PHY_TRSV3_LVCC); | ||
219 | } | ||
220 | |||
221 | static void exynos_pcie_assert_reset(struct pcie_port *pp) | ||
222 | { | ||
223 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
224 | |||
225 | if (exynos_pcie->reset_gpio >= 0) | ||
226 | devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio, | ||
227 | GPIOF_OUT_INIT_HIGH, "RESET"); | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | static int exynos_pcie_establish_link(struct pcie_port *pp) | ||
232 | { | ||
233 | u32 val; | ||
234 | int count = 0; | ||
235 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
236 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
237 | void __iomem *block_base = exynos_pcie->block_base; | ||
238 | void __iomem *phy_base = exynos_pcie->phy_base; | ||
239 | |||
240 | if (dw_pcie_link_up(pp)) { | ||
241 | dev_err(pp->dev, "Link already up\n"); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /* assert reset signals */ | ||
246 | exynos_pcie_assert_core_reset(pp); | ||
247 | exynos_pcie_assert_phy_reset(pp); | ||
248 | |||
249 | /* de-assert phy reset */ | ||
250 | exynos_pcie_deassert_phy_reset(pp); | ||
251 | |||
252 | /* initialize phy */ | ||
253 | exynos_pcie_init_phy(pp); | ||
254 | |||
255 | /* pulse for common reset */ | ||
256 | writel(1, block_base + PCIE_PHY_COMMON_RESET); | ||
257 | udelay(500); | ||
258 | writel(0, block_base + PCIE_PHY_COMMON_RESET); | ||
259 | |||
260 | /* de-assert core reset */ | ||
261 | exynos_pcie_deassert_core_reset(pp); | ||
262 | |||
263 | /* setup root complex */ | ||
264 | dw_pcie_setup_rc(pp); | ||
265 | |||
266 | /* assert reset signal */ | ||
267 | exynos_pcie_assert_reset(pp); | ||
268 | |||
269 | /* assert LTSSM enable */ | ||
270 | writel(PCIE_ELBI_LTSSM_ENABLE, elbi_base + PCIE_APP_LTSSM_ENABLE); | ||
271 | |||
272 | /* check if the link is up or not */ | ||
273 | while (!dw_pcie_link_up(pp)) { | ||
274 | mdelay(100); | ||
275 | count++; | ||
276 | if (count == 10) { | ||
277 | while (readl(phy_base + PCIE_PHY_PLL_LOCKED) == 0) { | ||
278 | val = readl(block_base + PCIE_PHY_PLL_LOCKED); | ||
279 | dev_info(pp->dev, "PLL Locked: 0x%x\n", val); | ||
280 | } | ||
281 | dev_err(pp->dev, "PCIe Link Fail\n"); | ||
282 | return -EINVAL; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | dev_info(pp->dev, "Link up\n"); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp) | ||
292 | { | ||
293 | u32 val; | ||
294 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
295 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
296 | |||
297 | val = readl(elbi_base + PCIE_IRQ_PULSE); | ||
298 | writel(val, elbi_base + PCIE_IRQ_PULSE); | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp) | ||
303 | { | ||
304 | u32 val; | ||
305 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
306 | void __iomem *elbi_base = exynos_pcie->elbi_base; | ||
307 | |||
308 | /* enable INTX interrupt */ | ||
309 | val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | | ||
310 | IRQ_INTC_ASSERT | IRQ_INTD_ASSERT, | ||
311 | writel(val, elbi_base + PCIE_IRQ_EN_PULSE); | ||
312 | return; | ||
313 | } | ||
314 | |||
315 | static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) | ||
316 | { | ||
317 | struct pcie_port *pp = arg; | ||
318 | |||
319 | exynos_pcie_clear_irq_pulse(pp); | ||
320 | return IRQ_HANDLED; | ||
321 | } | ||
322 | |||
323 | static void exynos_pcie_enable_interrupts(struct pcie_port *pp) | ||
324 | { | ||
325 | exynos_pcie_enable_irq_pulse(pp); | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | static inline void exynos_pcie_readl_rc(struct pcie_port *pp, | ||
330 | void __iomem *dbi_base, u32 *val) | ||
331 | { | ||
332 | exynos_pcie_sideband_dbi_r_mode(pp, true); | ||
333 | *val = readl(dbi_base); | ||
334 | exynos_pcie_sideband_dbi_r_mode(pp, false); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | static inline void exynos_pcie_writel_rc(struct pcie_port *pp, | ||
339 | u32 val, void __iomem *dbi_base) | ||
340 | { | ||
341 | exynos_pcie_sideband_dbi_w_mode(pp, true); | ||
342 | writel(val, dbi_base); | ||
343 | exynos_pcie_sideband_dbi_w_mode(pp, false); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, | ||
348 | u32 *val) | ||
349 | { | ||
350 | int ret; | ||
351 | |||
352 | exynos_pcie_sideband_dbi_r_mode(pp, true); | ||
353 | ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); | ||
354 | exynos_pcie_sideband_dbi_r_mode(pp, false); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, | ||
359 | u32 val) | ||
360 | { | ||
361 | int ret; | ||
362 | |||
363 | exynos_pcie_sideband_dbi_w_mode(pp, true); | ||
364 | ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, val); | ||
365 | exynos_pcie_sideband_dbi_w_mode(pp, false); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static int exynos_pcie_link_up(struct pcie_port *pp) | ||
370 | { | ||
371 | struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); | ||
372 | u32 val = readl(exynos_pcie->elbi_base + PCIE_ELBI_RDLH_LINKUP); | ||
373 | |||
374 | if (val == PCIE_ELBI_LTSSM_ENABLE) | ||
375 | return 1; | ||
376 | |||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static void exynos_pcie_host_init(struct pcie_port *pp) | ||
381 | { | ||
382 | exynos_pcie_establish_link(pp); | ||
383 | exynos_pcie_enable_interrupts(pp); | ||
384 | } | ||
385 | |||
386 | static struct pcie_host_ops exynos_pcie_host_ops = { | ||
387 | .readl_rc = exynos_pcie_readl_rc, | ||
388 | .writel_rc = exynos_pcie_writel_rc, | ||
389 | .rd_own_conf = exynos_pcie_rd_own_conf, | ||
390 | .wr_own_conf = exynos_pcie_wr_own_conf, | ||
391 | .link_up = exynos_pcie_link_up, | ||
392 | .host_init = exynos_pcie_host_init, | ||
393 | }; | ||
394 | |||
395 | static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) | ||
396 | { | ||
397 | int ret; | ||
398 | |||
399 | pp->irq = platform_get_irq(pdev, 1); | ||
400 | if (!pp->irq) { | ||
401 | dev_err(&pdev->dev, "failed to get irq\n"); | ||
402 | return -ENODEV; | ||
403 | } | ||
404 | ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler, | ||
405 | IRQF_SHARED, "exynos-pcie", pp); | ||
406 | if (ret) { | ||
407 | dev_err(&pdev->dev, "failed to request irq\n"); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | pp->root_bus_nr = -1; | ||
412 | pp->ops = &exynos_pcie_host_ops; | ||
413 | |||
414 | spin_lock_init(&pp->conf_lock); | ||
415 | ret = dw_pcie_host_init(pp); | ||
416 | if (ret) { | ||
417 | dev_err(&pdev->dev, "failed to initialize host\n"); | ||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static int __init exynos_pcie_probe(struct platform_device *pdev) | ||
425 | { | ||
426 | struct exynos_pcie *exynos_pcie; | ||
427 | struct pcie_port *pp; | ||
428 | struct device_node *np = pdev->dev.of_node; | ||
429 | struct resource *elbi_base; | ||
430 | struct resource *phy_base; | ||
431 | struct resource *block_base; | ||
432 | int ret; | ||
433 | |||
434 | exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie), | ||
435 | GFP_KERNEL); | ||
436 | if (!exynos_pcie) { | ||
437 | dev_err(&pdev->dev, "no memory for exynos pcie\n"); | ||
438 | return -ENOMEM; | ||
439 | } | ||
440 | |||
441 | pp = &exynos_pcie->pp; | ||
442 | |||
443 | pp->dev = &pdev->dev; | ||
444 | |||
445 | exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); | ||
446 | |||
447 | exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie"); | ||
448 | if (IS_ERR(exynos_pcie->clk)) { | ||
449 | dev_err(&pdev->dev, "Failed to get pcie rc clock\n"); | ||
450 | return PTR_ERR(exynos_pcie->clk); | ||
451 | } | ||
452 | ret = clk_prepare_enable(exynos_pcie->clk); | ||
453 | if (ret) | ||
454 | return ret; | ||
455 | |||
456 | exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus"); | ||
457 | if (IS_ERR(exynos_pcie->bus_clk)) { | ||
458 | dev_err(&pdev->dev, "Failed to get pcie bus clock\n"); | ||
459 | ret = PTR_ERR(exynos_pcie->bus_clk); | ||
460 | goto fail_clk; | ||
461 | } | ||
462 | ret = clk_prepare_enable(exynos_pcie->bus_clk); | ||
463 | if (ret) | ||
464 | goto fail_clk; | ||
465 | |||
466 | elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
467 | exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); | ||
468 | if (IS_ERR(exynos_pcie->elbi_base)) | ||
469 | return PTR_ERR(exynos_pcie->elbi_base); | ||
470 | |||
471 | phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
472 | exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base); | ||
473 | if (IS_ERR(exynos_pcie->phy_base)) | ||
474 | return PTR_ERR(exynos_pcie->phy_base); | ||
475 | |||
476 | block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
477 | exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base); | ||
478 | if (IS_ERR(exynos_pcie->block_base)) | ||
479 | return PTR_ERR(exynos_pcie->block_base); | ||
480 | |||
481 | ret = add_pcie_port(pp, pdev); | ||
482 | if (ret < 0) | ||
483 | goto fail_bus_clk; | ||
484 | |||
485 | platform_set_drvdata(pdev, exynos_pcie); | ||
486 | return 0; | ||
487 | |||
488 | fail_bus_clk: | ||
489 | clk_disable_unprepare(exynos_pcie->bus_clk); | ||
490 | fail_clk: | ||
491 | clk_disable_unprepare(exynos_pcie->clk); | ||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | static int __exit exynos_pcie_remove(struct platform_device *pdev) | ||
496 | { | ||
497 | struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev); | ||
498 | |||
499 | clk_disable_unprepare(exynos_pcie->bus_clk); | ||
500 | clk_disable_unprepare(exynos_pcie->clk); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static const struct of_device_id exynos_pcie_of_match[] = { | ||
506 | { .compatible = "samsung,exynos5440-pcie", }, | ||
507 | {}, | ||
508 | }; | ||
509 | MODULE_DEVICE_TABLE(of, exynos_pcie_of_match); | ||
510 | |||
511 | static struct platform_driver exynos_pcie_driver = { | ||
512 | .remove = __exit_p(exynos_pcie_remove), | ||
513 | .driver = { | ||
514 | .name = "exynos-pcie", | ||
515 | .owner = THIS_MODULE, | ||
516 | .of_match_table = of_match_ptr(exynos_pcie_of_match), | ||
517 | }, | ||
518 | }; | ||
519 | |||
520 | /* Exynos PCIe driver does not allow module unload */ | ||
521 | |||
522 | static int __init pcie_init(void) | ||
523 | { | ||
524 | return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); | ||
525 | } | ||
526 | subsys_initcall(pcie_init); | ||
527 | |||
528 | MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); | ||
529 | MODULE_DESCRIPTION("Samsung PCIe host controller driver"); | ||
530 | MODULE_LICENSE("GPL v2"); | ||