Commit | Line | Data |
---|---|---|
4b1ced84 JH |
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> | |
caf5548c | 19 | #include <linux/init.h> |
32784780 | 20 | #include <linux/of_device.h> |
4b1ced84 JH |
21 | #include <linux/of_gpio.h> |
22 | #include <linux/pci.h> | |
23 | #include <linux/platform_device.h> | |
e7cd7ef5 | 24 | #include <linux/phy/phy.h> |
4b1ced84 JH |
25 | #include <linux/resource.h> |
26 | #include <linux/signal.h> | |
27 | #include <linux/types.h> | |
28 | ||
29 | #include "pcie-designware.h" | |
30 | ||
442ec4c0 | 31 | #define to_exynos_pcie(x) dev_get_drvdata((x)->dev) |
4b1ced84 | 32 | |
4b1ced84 JH |
33 | /* PCIe ELBI registers */ |
34 | #define PCIE_IRQ_PULSE 0x000 | |
2681c0e7 JC |
35 | #define IRQ_INTA_ASSERT BIT(0) |
36 | #define IRQ_INTB_ASSERT BIT(2) | |
37 | #define IRQ_INTC_ASSERT BIT(4) | |
38 | #define IRQ_INTD_ASSERT BIT(6) | |
4b1ced84 JH |
39 | #define PCIE_IRQ_LEVEL 0x004 |
40 | #define PCIE_IRQ_SPECIAL 0x008 | |
41 | #define PCIE_IRQ_EN_PULSE 0x00c | |
42 | #define PCIE_IRQ_EN_LEVEL 0x010 | |
2681c0e7 | 43 | #define IRQ_MSI_ENABLE BIT(2) |
4b1ced84 JH |
44 | #define PCIE_IRQ_EN_SPECIAL 0x014 |
45 | #define PCIE_PWR_RESET 0x018 | |
46 | #define PCIE_CORE_RESET 0x01c | |
2681c0e7 | 47 | #define PCIE_CORE_RESET_ENABLE BIT(0) |
4b1ced84 JH |
48 | #define PCIE_STICKY_RESET 0x020 |
49 | #define PCIE_NONSTICKY_RESET 0x024 | |
50 | #define PCIE_APP_INIT_RESET 0x028 | |
51 | #define PCIE_APP_LTSSM_ENABLE 0x02c | |
52 | #define PCIE_ELBI_RDLH_LINKUP 0x064 | |
53 | #define PCIE_ELBI_LTSSM_ENABLE 0x1 | |
54 | #define PCIE_ELBI_SLV_AWMISC 0x11c | |
55 | #define PCIE_ELBI_SLV_ARMISC 0x120 | |
2681c0e7 | 56 | #define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) |
4b1ced84 JH |
57 | |
58 | /* PCIe Purple registers */ | |
59 | #define PCIE_PHY_GLOBAL_RESET 0x000 | |
60 | #define PCIE_PHY_COMMON_RESET 0x004 | |
61 | #define PCIE_PHY_CMN_REG 0x008 | |
62 | #define PCIE_PHY_MAC_RESET 0x00c | |
63 | #define PCIE_PHY_PLL_LOCKED 0x010 | |
64 | #define PCIE_PHY_TRSVREG_RESET 0x020 | |
65 | #define PCIE_PHY_TRSV_RESET 0x024 | |
66 | ||
67 | /* PCIe PHY registers */ | |
68 | #define PCIE_PHY_IMPEDANCE 0x004 | |
69 | #define PCIE_PHY_PLL_DIV_0 0x008 | |
70 | #define PCIE_PHY_PLL_BIAS 0x00c | |
71 | #define PCIE_PHY_DCC_FEEDBACK 0x014 | |
72 | #define PCIE_PHY_PLL_DIV_1 0x05c | |
f62b878b | 73 | #define PCIE_PHY_COMMON_POWER 0x064 |
2681c0e7 | 74 | #define PCIE_PHY_COMMON_PD_CMN BIT(3) |
4b1ced84 JH |
75 | #define PCIE_PHY_TRSV0_EMP_LVL 0x084 |
76 | #define PCIE_PHY_TRSV0_DRV_LVL 0x088 | |
77 | #define PCIE_PHY_TRSV0_RXCDR 0x0ac | |
f62b878b | 78 | #define PCIE_PHY_TRSV0_POWER 0x0c4 |
2681c0e7 | 79 | #define PCIE_PHY_TRSV0_PD_TSV BIT(7) |
4b1ced84 JH |
80 | #define PCIE_PHY_TRSV0_LVCC 0x0dc |
81 | #define PCIE_PHY_TRSV1_EMP_LVL 0x144 | |
82 | #define PCIE_PHY_TRSV1_RXCDR 0x16c | |
f62b878b | 83 | #define PCIE_PHY_TRSV1_POWER 0x184 |
2681c0e7 | 84 | #define PCIE_PHY_TRSV1_PD_TSV BIT(7) |
4b1ced84 JH |
85 | #define PCIE_PHY_TRSV1_LVCC 0x19c |
86 | #define PCIE_PHY_TRSV2_EMP_LVL 0x204 | |
87 | #define PCIE_PHY_TRSV2_RXCDR 0x22c | |
f62b878b | 88 | #define PCIE_PHY_TRSV2_POWER 0x244 |
2681c0e7 | 89 | #define PCIE_PHY_TRSV2_PD_TSV BIT(7) |
4b1ced84 JH |
90 | #define PCIE_PHY_TRSV2_LVCC 0x25c |
91 | #define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 | |
92 | #define PCIE_PHY_TRSV3_RXCDR 0x2ec | |
f62b878b | 93 | #define PCIE_PHY_TRSV3_POWER 0x304 |
2681c0e7 | 94 | #define PCIE_PHY_TRSV3_PD_TSV BIT(7) |
4b1ced84 JH |
95 | #define PCIE_PHY_TRSV3_LVCC 0x31c |
96 | ||
32784780 NA |
97 | struct exynos_pcie_mem_res { |
98 | void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */ | |
99 | void __iomem *phy_base; /* DT 1st resource: PHY CTRL */ | |
100 | void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */ | |
101 | }; | |
102 | ||
103 | struct exynos_pcie_clk_res { | |
104 | struct clk *clk; | |
105 | struct clk *bus_clk; | |
106 | }; | |
107 | ||
108 | struct exynos_pcie { | |
b2e6d305 | 109 | struct dw_pcie *pci; |
32784780 NA |
110 | struct exynos_pcie_mem_res *mem_res; |
111 | struct exynos_pcie_clk_res *clk_res; | |
112 | const struct exynos_pcie_ops *ops; | |
113 | int reset_gpio; | |
e7cd7ef5 JC |
114 | |
115 | /* For Generic PHY Framework */ | |
116 | bool using_phy; | |
117 | struct phy *phy; | |
32784780 NA |
118 | }; |
119 | ||
120 | struct exynos_pcie_ops { | |
121 | int (*get_mem_resources)(struct platform_device *pdev, | |
122 | struct exynos_pcie *ep); | |
123 | int (*get_clk_resources)(struct exynos_pcie *ep); | |
124 | int (*init_clk_resources)(struct exynos_pcie *ep); | |
125 | void (*deinit_clk_resources)(struct exynos_pcie *ep); | |
126 | }; | |
127 | ||
128 | static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev, | |
129 | struct exynos_pcie *ep) | |
058dd016 | 130 | { |
b2e6d305 BH |
131 | struct dw_pcie *pci = ep->pci; |
132 | struct device *dev = pci->dev; | |
32784780 | 133 | struct resource *res; |
32784780 NA |
134 | |
135 | ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL); | |
136 | if (!ep->mem_res) | |
137 | return -ENOMEM; | |
138 | ||
139 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
140 | ep->mem_res->elbi_base = devm_ioremap_resource(dev, res); | |
141 | if (IS_ERR(ep->mem_res->elbi_base)) | |
142 | return PTR_ERR(ep->mem_res->elbi_base); | |
143 | ||
544714d8 JC |
144 | /* If using the PHY framework, doesn't need to get other resource */ |
145 | if (ep->using_phy) | |
146 | return 0; | |
147 | ||
32784780 NA |
148 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
149 | ep->mem_res->phy_base = devm_ioremap_resource(dev, res); | |
150 | if (IS_ERR(ep->mem_res->phy_base)) | |
151 | return PTR_ERR(ep->mem_res->phy_base); | |
152 | ||
153 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | |
154 | ep->mem_res->block_base = devm_ioremap_resource(dev, res); | |
155 | if (IS_ERR(ep->mem_res->block_base)) | |
156 | return PTR_ERR(ep->mem_res->block_base); | |
157 | ||
158 | return 0; | |
058dd016 SJ |
159 | } |
160 | ||
32784780 | 161 | static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep) |
058dd016 | 162 | { |
b2e6d305 BH |
163 | struct dw_pcie *pci = ep->pci; |
164 | struct device *dev = pci->dev; | |
32784780 NA |
165 | |
166 | ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL); | |
167 | if (!ep->clk_res) | |
168 | return -ENOMEM; | |
169 | ||
170 | ep->clk_res->clk = devm_clk_get(dev, "pcie"); | |
171 | if (IS_ERR(ep->clk_res->clk)) { | |
172 | dev_err(dev, "Failed to get pcie rc clock\n"); | |
173 | return PTR_ERR(ep->clk_res->clk); | |
174 | } | |
175 | ||
176 | ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus"); | |
177 | if (IS_ERR(ep->clk_res->bus_clk)) { | |
178 | dev_err(dev, "Failed to get pcie bus clock\n"); | |
179 | return PTR_ERR(ep->clk_res->bus_clk); | |
180 | } | |
181 | ||
182 | return 0; | |
058dd016 SJ |
183 | } |
184 | ||
32784780 | 185 | static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep) |
058dd016 | 186 | { |
b2e6d305 BH |
187 | struct dw_pcie *pci = ep->pci; |
188 | struct device *dev = pci->dev; | |
32784780 NA |
189 | int ret; |
190 | ||
191 | ret = clk_prepare_enable(ep->clk_res->clk); | |
192 | if (ret) { | |
193 | dev_err(dev, "cannot enable pcie rc clock"); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | ret = clk_prepare_enable(ep->clk_res->bus_clk); | |
198 | if (ret) { | |
199 | dev_err(dev, "cannot enable pcie bus clock"); | |
200 | goto err_bus_clk; | |
201 | } | |
202 | ||
203 | return 0; | |
204 | ||
205 | err_bus_clk: | |
206 | clk_disable_unprepare(ep->clk_res->clk); | |
207 | ||
208 | return ret; | |
058dd016 SJ |
209 | } |
210 | ||
32784780 | 211 | static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep) |
058dd016 | 212 | { |
32784780 NA |
213 | clk_disable_unprepare(ep->clk_res->bus_clk); |
214 | clk_disable_unprepare(ep->clk_res->clk); | |
058dd016 SJ |
215 | } |
216 | ||
32784780 NA |
217 | static const struct exynos_pcie_ops exynos5440_pcie_ops = { |
218 | .get_mem_resources = exynos5440_pcie_get_mem_resources, | |
219 | .get_clk_resources = exynos5440_pcie_get_clk_resources, | |
220 | .init_clk_resources = exynos5440_pcie_init_clk_resources, | |
221 | .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources, | |
222 | }; | |
223 | ||
d6da7d90 | 224 | static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg) |
058dd016 | 225 | { |
d6da7d90 | 226 | writel(val, base + reg); |
058dd016 SJ |
227 | } |
228 | ||
d6da7d90 | 229 | static u32 exynos_pcie_readl(void __iomem *base, u32 reg) |
058dd016 | 230 | { |
d6da7d90 | 231 | return readl(base + reg); |
058dd016 SJ |
232 | } |
233 | ||
4e0a90b3 | 234 | static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on) |
4b1ced84 JH |
235 | { |
236 | u32 val; | |
4b1ced84 | 237 | |
32784780 | 238 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC); |
92004a06 | 239 | if (on) |
4b1ced84 | 240 | val |= PCIE_ELBI_SLV_DBI_ENABLE; |
92004a06 | 241 | else |
4b1ced84 | 242 | val &= ~PCIE_ELBI_SLV_DBI_ENABLE; |
32784780 | 243 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC); |
4b1ced84 JH |
244 | } |
245 | ||
4e0a90b3 | 246 | static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on) |
4b1ced84 JH |
247 | { |
248 | u32 val; | |
4b1ced84 | 249 | |
32784780 | 250 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC); |
92004a06 | 251 | if (on) |
4b1ced84 | 252 | val |= PCIE_ELBI_SLV_DBI_ENABLE; |
92004a06 | 253 | else |
4b1ced84 | 254 | val &= ~PCIE_ELBI_SLV_DBI_ENABLE; |
32784780 | 255 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC); |
4b1ced84 JH |
256 | } |
257 | ||
4e0a90b3 | 258 | static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep) |
4b1ced84 JH |
259 | { |
260 | u32 val; | |
4b1ced84 | 261 | |
32784780 | 262 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); |
4b1ced84 | 263 | val &= ~PCIE_CORE_RESET_ENABLE; |
32784780 NA |
264 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); |
265 | exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET); | |
266 | exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET); | |
267 | exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET); | |
4b1ced84 JH |
268 | } |
269 | ||
4e0a90b3 | 270 | static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep) |
4b1ced84 JH |
271 | { |
272 | u32 val; | |
4b1ced84 | 273 | |
32784780 | 274 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); |
4b1ced84 | 275 | val |= PCIE_CORE_RESET_ENABLE; |
058dd016 | 276 | |
32784780 NA |
277 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); |
278 | exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET); | |
279 | exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET); | |
280 | exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET); | |
281 | exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET); | |
282 | exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET); | |
4b1ced84 JH |
283 | } |
284 | ||
4e0a90b3 | 285 | static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep) |
4b1ced84 | 286 | { |
32784780 NA |
287 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET); |
288 | exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET); | |
4b1ced84 JH |
289 | } |
290 | ||
4e0a90b3 | 291 | static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep) |
4b1ced84 | 292 | { |
32784780 NA |
293 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET); |
294 | exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET); | |
295 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET); | |
296 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG); | |
297 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET); | |
298 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET); | |
4b1ced84 JH |
299 | } |
300 | ||
4e0a90b3 | 301 | static void exynos_pcie_power_on_phy(struct exynos_pcie *ep) |
f62b878b JH |
302 | { |
303 | u32 val; | |
f62b878b | 304 | |
32784780 | 305 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER); |
f62b878b | 306 | val &= ~PCIE_PHY_COMMON_PD_CMN; |
32784780 | 307 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER); |
f62b878b | 308 | |
32784780 | 309 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER); |
f62b878b | 310 | val &= ~PCIE_PHY_TRSV0_PD_TSV; |
32784780 | 311 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER); |
f62b878b | 312 | |
32784780 | 313 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER); |
f62b878b | 314 | val &= ~PCIE_PHY_TRSV1_PD_TSV; |
32784780 | 315 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER); |
f62b878b | 316 | |
32784780 | 317 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER); |
f62b878b | 318 | val &= ~PCIE_PHY_TRSV2_PD_TSV; |
32784780 | 319 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER); |
f62b878b | 320 | |
32784780 | 321 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER); |
f62b878b | 322 | val &= ~PCIE_PHY_TRSV3_PD_TSV; |
32784780 | 323 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER); |
f62b878b JH |
324 | } |
325 | ||
4e0a90b3 | 326 | static void exynos_pcie_power_off_phy(struct exynos_pcie *ep) |
f62b878b JH |
327 | { |
328 | u32 val; | |
f62b878b | 329 | |
32784780 | 330 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER); |
f62b878b | 331 | val |= PCIE_PHY_COMMON_PD_CMN; |
32784780 | 332 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER); |
f62b878b | 333 | |
32784780 | 334 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER); |
f62b878b | 335 | val |= PCIE_PHY_TRSV0_PD_TSV; |
32784780 | 336 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER); |
f62b878b | 337 | |
32784780 | 338 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER); |
f62b878b | 339 | val |= PCIE_PHY_TRSV1_PD_TSV; |
32784780 | 340 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER); |
f62b878b | 341 | |
32784780 | 342 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER); |
f62b878b | 343 | val |= PCIE_PHY_TRSV2_PD_TSV; |
32784780 | 344 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER); |
f62b878b | 345 | |
32784780 | 346 | val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER); |
f62b878b | 347 | val |= PCIE_PHY_TRSV3_PD_TSV; |
32784780 | 348 | exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER); |
f62b878b JH |
349 | } |
350 | ||
4e0a90b3 | 351 | static void exynos_pcie_init_phy(struct exynos_pcie *ep) |
4b1ced84 | 352 | { |
4b1ced84 | 353 | /* DCC feedback control off */ |
32784780 | 354 | exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); |
4b1ced84 JH |
355 | |
356 | /* set TX/RX impedance */ | |
32784780 | 357 | exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); |
4b1ced84 JH |
358 | |
359 | /* set 50Mhz PHY clock */ | |
32784780 NA |
360 | exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); |
361 | exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); | |
4b1ced84 JH |
362 | |
363 | /* set TX Differential output for lane 0 */ | |
32784780 | 364 | exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); |
4b1ced84 JH |
365 | |
366 | /* set TX Pre-emphasis Level Control for lane 0 to minimum */ | |
32784780 | 367 | exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); |
4b1ced84 JH |
368 | |
369 | /* set RX clock and data recovery bandwidth */ | |
32784780 NA |
370 | exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); |
371 | exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); | |
372 | exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); | |
373 | exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); | |
374 | exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); | |
4b1ced84 JH |
375 | |
376 | /* change TX Pre-emphasis Level Control for lanes */ | |
32784780 NA |
377 | exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); |
378 | exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); | |
379 | exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); | |
380 | exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); | |
4b1ced84 JH |
381 | |
382 | /* set LVCC */ | |
32784780 NA |
383 | exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); |
384 | exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); | |
385 | exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); | |
386 | exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); | |
4b1ced84 JH |
387 | } |
388 | ||
4e0a90b3 | 389 | static void exynos_pcie_assert_reset(struct exynos_pcie *ep) |
4b1ced84 | 390 | { |
b2e6d305 | 391 | struct dw_pcie *pci = ep->pci; |
442ec4c0 | 392 | struct device *dev = pci->dev; |
4b1ced84 | 393 | |
4e0a90b3 JC |
394 | if (ep->reset_gpio >= 0) |
395 | devm_gpio_request_one(dev, ep->reset_gpio, | |
4b1ced84 | 396 | GPIOF_OUT_INIT_HIGH, "RESET"); |
4b1ced84 JH |
397 | } |
398 | ||
4e0a90b3 | 399 | static int exynos_pcie_establish_link(struct exynos_pcie *ep) |
4b1ced84 | 400 | { |
b2e6d305 | 401 | struct dw_pcie *pci = ep->pci; |
442ec4c0 KVA |
402 | struct pcie_port *pp = &pci->pp; |
403 | struct device *dev = pci->dev; | |
6cbb247e | 404 | u32 val; |
4b1ced84 | 405 | |
442ec4c0 | 406 | if (dw_pcie_link_up(pci)) { |
fae68d69 | 407 | dev_err(dev, "Link already up\n"); |
4b1ced84 JH |
408 | return 0; |
409 | } | |
410 | ||
4e0a90b3 | 411 | exynos_pcie_assert_core_reset(ep); |
e7cd7ef5 JC |
412 | |
413 | if (ep->using_phy) { | |
414 | phy_reset(ep->phy); | |
415 | ||
416 | exynos_pcie_writel(ep->mem_res->elbi_base, 1, | |
417 | PCIE_PWR_RESET); | |
418 | ||
419 | phy_power_on(ep->phy); | |
420 | phy_init(ep->phy); | |
421 | } else { | |
422 | exynos_pcie_assert_phy_reset(ep); | |
423 | exynos_pcie_deassert_phy_reset(ep); | |
424 | exynos_pcie_power_on_phy(ep); | |
425 | exynos_pcie_init_phy(ep); | |
426 | ||
427 | /* pulse for common reset */ | |
428 | exynos_pcie_writel(ep->mem_res->block_base, 1, | |
429 | PCIE_PHY_COMMON_RESET); | |
430 | udelay(500); | |
431 | exynos_pcie_writel(ep->mem_res->block_base, 0, | |
432 | PCIE_PHY_COMMON_RESET); | |
433 | } | |
4b1ced84 JH |
434 | |
435 | /* pulse for common reset */ | |
32784780 | 436 | exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_COMMON_RESET); |
4b1ced84 | 437 | udelay(500); |
32784780 | 438 | exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET); |
4b1ced84 | 439 | |
4e0a90b3 | 440 | exynos_pcie_deassert_core_reset(ep); |
4b1ced84 | 441 | dw_pcie_setup_rc(pp); |
4e0a90b3 | 442 | exynos_pcie_assert_reset(ep); |
4b1ced84 JH |
443 | |
444 | /* assert LTSSM enable */ | |
32784780 | 445 | exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE, |
058dd016 | 446 | PCIE_APP_LTSSM_ENABLE); |
4b1ced84 JH |
447 | |
448 | /* check if the link is up or not */ | |
442ec4c0 | 449 | if (!dw_pcie_wait_for_link(pci)) |
886bc5ce | 450 | return 0; |
4b1ced84 | 451 | |
e7cd7ef5 JC |
452 | if (ep->using_phy) { |
453 | phy_power_off(ep->phy); | |
454 | return -ETIMEDOUT; | |
455 | } | |
456 | ||
32784780 NA |
457 | while (exynos_pcie_readl(ep->mem_res->phy_base, |
458 | PCIE_PHY_PLL_LOCKED) == 0) { | |
459 | val = exynos_pcie_readl(ep->mem_res->block_base, | |
460 | PCIE_PHY_PLL_LOCKED); | |
fae68d69 | 461 | dev_info(dev, "PLL Locked: 0x%x\n", val); |
6cbb247e | 462 | } |
4e0a90b3 | 463 | exynos_pcie_power_off_phy(ep); |
886bc5ce | 464 | return -ETIMEDOUT; |
4b1ced84 JH |
465 | } |
466 | ||
4e0a90b3 | 467 | static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep) |
4b1ced84 JH |
468 | { |
469 | u32 val; | |
4b1ced84 | 470 | |
32784780 NA |
471 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE); |
472 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE); | |
4b1ced84 JH |
473 | } |
474 | ||
4e0a90b3 | 475 | static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep) |
4b1ced84 JH |
476 | { |
477 | u32 val; | |
4b1ced84 JH |
478 | |
479 | /* enable INTX interrupt */ | |
480 | val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | | |
01d06a9a | 481 | IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; |
32784780 | 482 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE); |
4b1ced84 JH |
483 | } |
484 | ||
485 | static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) | |
486 | { | |
4e0a90b3 | 487 | struct exynos_pcie *ep = arg; |
4b1ced84 | 488 | |
4e0a90b3 | 489 | exynos_pcie_clear_irq_pulse(ep); |
4b1ced84 JH |
490 | return IRQ_HANDLED; |
491 | } | |
492 | ||
f342d940 JH |
493 | static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) |
494 | { | |
4e0a90b3 | 495 | struct exynos_pcie *ep = arg; |
b2e6d305 | 496 | struct dw_pcie *pci = ep->pci; |
442ec4c0 | 497 | struct pcie_port *pp = &pci->pp; |
f342d940 | 498 | |
7f4f16ee | 499 | return dw_handle_msi_irq(pp); |
f342d940 JH |
500 | } |
501 | ||
4e0a90b3 | 502 | static void exynos_pcie_msi_init(struct exynos_pcie *ep) |
f342d940 | 503 | { |
b2e6d305 | 504 | struct dw_pcie *pci = ep->pci; |
442ec4c0 | 505 | struct pcie_port *pp = &pci->pp; |
f342d940 | 506 | u32 val; |
f342d940 JH |
507 | |
508 | dw_pcie_msi_init(pp); | |
509 | ||
510 | /* enable MSI interrupt */ | |
32784780 | 511 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL); |
f342d940 | 512 | val |= IRQ_MSI_ENABLE; |
32784780 | 513 | exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL); |
f342d940 JH |
514 | } |
515 | ||
4e0a90b3 | 516 | static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep) |
4b1ced84 | 517 | { |
4e0a90b3 | 518 | exynos_pcie_enable_irq_pulse(ep); |
f342d940 JH |
519 | |
520 | if (IS_ENABLED(CONFIG_PCI_MSI)) | |
4e0a90b3 | 521 | exynos_pcie_msi_init(ep); |
4b1ced84 JH |
522 | } |
523 | ||
442ec4c0 | 524 | static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) |
4b1ced84 | 525 | { |
b2e6d305 | 526 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
446fc23f BH |
527 | u32 val; |
528 | ||
4e0a90b3 | 529 | exynos_pcie_sideband_dbi_r_mode(ep, true); |
442ec4c0 | 530 | val = readl(pci->dbi_base + reg); |
4e0a90b3 | 531 | exynos_pcie_sideband_dbi_r_mode(ep, false); |
446fc23f | 532 | return val; |
4b1ced84 JH |
533 | } |
534 | ||
442ec4c0 | 535 | static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) |
4b1ced84 | 536 | { |
b2e6d305 | 537 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
cc08e82b | 538 | |
4e0a90b3 | 539 | exynos_pcie_sideband_dbi_w_mode(ep, true); |
442ec4c0 | 540 | writel(val, pci->dbi_base + reg); |
4e0a90b3 | 541 | exynos_pcie_sideband_dbi_w_mode(ep, false); |
4b1ced84 JH |
542 | } |
543 | ||
544 | static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, | |
545 | u32 *val) | |
546 | { | |
442ec4c0 | 547 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
b2e6d305 | 548 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
4b1ced84 JH |
549 | int ret; |
550 | ||
4e0a90b3 | 551 | exynos_pcie_sideband_dbi_r_mode(ep, true); |
442ec4c0 | 552 | ret = dw_pcie_read(pci->dbi_base + where, size, val); |
4e0a90b3 | 553 | exynos_pcie_sideband_dbi_r_mode(ep, false); |
4b1ced84 JH |
554 | return ret; |
555 | } | |
556 | ||
557 | static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, | |
558 | u32 val) | |
559 | { | |
442ec4c0 | 560 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
b2e6d305 | 561 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
4b1ced84 JH |
562 | int ret; |
563 | ||
4e0a90b3 | 564 | exynos_pcie_sideband_dbi_w_mode(ep, true); |
442ec4c0 | 565 | ret = dw_pcie_write(pci->dbi_base + where, size, val); |
4e0a90b3 | 566 | exynos_pcie_sideband_dbi_w_mode(ep, false); |
4b1ced84 JH |
567 | return ret; |
568 | } | |
569 | ||
442ec4c0 | 570 | static int exynos_pcie_link_up(struct dw_pcie *pci) |
4b1ced84 | 571 | { |
b2e6d305 | 572 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
cc08e82b | 573 | u32 val; |
4b1ced84 | 574 | |
32784780 | 575 | val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_RDLH_LINKUP); |
4b1ced84 JH |
576 | if (val == PCIE_ELBI_LTSSM_ENABLE) |
577 | return 1; | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
582 | static void exynos_pcie_host_init(struct pcie_port *pp) | |
583 | { | |
442ec4c0 | 584 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
b2e6d305 | 585 | struct exynos_pcie *ep = to_exynos_pcie(pci); |
cc08e82b | 586 | |
4e0a90b3 JC |
587 | exynos_pcie_establish_link(ep); |
588 | exynos_pcie_enable_interrupts(ep); | |
4b1ced84 JH |
589 | } |
590 | ||
442ec4c0 | 591 | static struct dw_pcie_host_ops exynos_pcie_host_ops = { |
4b1ced84 JH |
592 | .rd_own_conf = exynos_pcie_rd_own_conf, |
593 | .wr_own_conf = exynos_pcie_wr_own_conf, | |
4b1ced84 JH |
594 | .host_init = exynos_pcie_host_init, |
595 | }; | |
596 | ||
4e0a90b3 | 597 | static int __init exynos_add_pcie_port(struct exynos_pcie *ep, |
70b3e89a | 598 | struct platform_device *pdev) |
4b1ced84 | 599 | { |
b2e6d305 | 600 | struct dw_pcie *pci = ep->pci; |
442ec4c0 KVA |
601 | struct pcie_port *pp = &pci->pp; |
602 | struct device *dev = &pdev->dev; | |
4b1ced84 JH |
603 | int ret; |
604 | ||
605 | pp->irq = platform_get_irq(pdev, 1); | |
606 | if (!pp->irq) { | |
fae68d69 | 607 | dev_err(dev, "failed to get irq\n"); |
4b1ced84 JH |
608 | return -ENODEV; |
609 | } | |
fae68d69 | 610 | ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, |
4e0a90b3 | 611 | IRQF_SHARED, "exynos-pcie", ep); |
4b1ced84 | 612 | if (ret) { |
fae68d69 | 613 | dev_err(dev, "failed to request irq\n"); |
4b1ced84 JH |
614 | return ret; |
615 | } | |
616 | ||
f342d940 JH |
617 | if (IS_ENABLED(CONFIG_PCI_MSI)) { |
618 | pp->msi_irq = platform_get_irq(pdev, 0); | |
619 | if (!pp->msi_irq) { | |
fae68d69 | 620 | dev_err(dev, "failed to get msi irq\n"); |
f342d940 JH |
621 | return -ENODEV; |
622 | } | |
623 | ||
fae68d69 | 624 | ret = devm_request_irq(dev, pp->msi_irq, |
f342d940 | 625 | exynos_pcie_msi_irq_handler, |
8ff0ef99 | 626 | IRQF_SHARED | IRQF_NO_THREAD, |
4e0a90b3 | 627 | "exynos-pcie", ep); |
f342d940 | 628 | if (ret) { |
fae68d69 | 629 | dev_err(dev, "failed to request msi irq\n"); |
f342d940 JH |
630 | return ret; |
631 | } | |
632 | } | |
633 | ||
4b1ced84 JH |
634 | pp->root_bus_nr = -1; |
635 | pp->ops = &exynos_pcie_host_ops; | |
636 | ||
4b1ced84 JH |
637 | ret = dw_pcie_host_init(pp); |
638 | if (ret) { | |
fae68d69 | 639 | dev_err(dev, "failed to initialize host\n"); |
4b1ced84 JH |
640 | return ret; |
641 | } | |
642 | ||
643 | return 0; | |
644 | } | |
645 | ||
442ec4c0 KVA |
646 | static const struct dw_pcie_ops dw_pcie_ops = { |
647 | .readl_dbi = exynos_pcie_readl_dbi, | |
648 | .writel_dbi = exynos_pcie_writel_dbi, | |
649 | .link_up = exynos_pcie_link_up, | |
650 | }; | |
651 | ||
4b1ced84 JH |
652 | static int __init exynos_pcie_probe(struct platform_device *pdev) |
653 | { | |
fae68d69 | 654 | struct device *dev = &pdev->dev; |
442ec4c0 | 655 | struct dw_pcie *pci; |
4e0a90b3 | 656 | struct exynos_pcie *ep; |
fae68d69 | 657 | struct device_node *np = dev->of_node; |
4b1ced84 JH |
658 | int ret; |
659 | ||
4e0a90b3 JC |
660 | ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); |
661 | if (!ep) | |
4b1ced84 | 662 | return -ENOMEM; |
4b1ced84 | 663 | |
442ec4c0 KVA |
664 | pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); |
665 | if (!pci) | |
666 | return -ENOMEM; | |
667 | ||
668 | pci->dev = dev; | |
669 | pci->ops = &dw_pcie_ops; | |
4b1ced84 | 670 | |
c0464062 | 671 | ep->pci = pci; |
32784780 NA |
672 | ep->ops = (const struct exynos_pcie_ops *) |
673 | of_device_get_match_data(dev); | |
4b1ced84 | 674 | |
32784780 | 675 | ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); |
4b1ced84 | 676 | |
e7cd7ef5 JC |
677 | /* Assume that controller doesn't use the PHY framework */ |
678 | ep->using_phy = false; | |
4b1ced84 | 679 | |
e7cd7ef5 JC |
680 | ep->phy = devm_of_phy_get(dev, np, NULL); |
681 | if (IS_ERR(ep->phy)) { | |
682 | if (PTR_ERR(ep->phy) == -EPROBE_DEFER) | |
683 | return PTR_ERR(ep->phy); | |
684 | dev_warn(dev, "Use the 'phy' property. Current DT of pci-exynos was deprecated!!\n"); | |
685 | } else | |
686 | ep->using_phy = true; | |
687 | ||
32784780 NA |
688 | if (ep->ops && ep->ops->get_mem_resources) { |
689 | ret = ep->ops->get_mem_resources(pdev, ep); | |
690 | if (ret) | |
691 | return ret; | |
f8db3c90 | 692 | } |
4b1ced84 | 693 | |
32784780 NA |
694 | if (ep->ops && ep->ops->get_clk_resources) { |
695 | ret = ep->ops->get_clk_resources(ep); | |
696 | if (ret) | |
697 | return ret; | |
698 | ret = ep->ops->init_clk_resources(ep); | |
699 | if (ret) | |
700 | return ret; | |
f8db3c90 | 701 | } |
4b1ced84 | 702 | |
b2e6d305 | 703 | platform_set_drvdata(pdev, ep); |
9bcf0a6f | 704 | |
4e0a90b3 | 705 | ret = exynos_add_pcie_port(ep, pdev); |
4b1ced84 | 706 | if (ret < 0) |
32784780 | 707 | goto fail_probe; |
4b1ced84 | 708 | |
4b1ced84 JH |
709 | return 0; |
710 | ||
32784780 | 711 | fail_probe: |
e7cd7ef5 JC |
712 | if (ep->using_phy) |
713 | phy_exit(ep->phy); | |
714 | ||
32784780 NA |
715 | if (ep->ops && ep->ops->deinit_clk_resources) |
716 | ep->ops->deinit_clk_resources(ep); | |
4b1ced84 JH |
717 | return ret; |
718 | } | |
719 | ||
720 | static int __exit exynos_pcie_remove(struct platform_device *pdev) | |
721 | { | |
4e0a90b3 | 722 | struct exynos_pcie *ep = platform_get_drvdata(pdev); |
4b1ced84 | 723 | |
32784780 NA |
724 | if (ep->ops && ep->ops->deinit_clk_resources) |
725 | ep->ops->deinit_clk_resources(ep); | |
4b1ced84 JH |
726 | |
727 | return 0; | |
728 | } | |
729 | ||
730 | static const struct of_device_id exynos_pcie_of_match[] = { | |
32784780 NA |
731 | { |
732 | .compatible = "samsung,exynos5440-pcie", | |
733 | .data = &exynos5440_pcie_ops | |
734 | }, | |
4b1ced84 JH |
735 | {}, |
736 | }; | |
4b1ced84 JH |
737 | |
738 | static struct platform_driver exynos_pcie_driver = { | |
739 | .remove = __exit_p(exynos_pcie_remove), | |
740 | .driver = { | |
741 | .name = "exynos-pcie", | |
eb36309a | 742 | .of_match_table = exynos_pcie_of_match, |
4b1ced84 JH |
743 | }, |
744 | }; | |
745 | ||
746 | /* Exynos PCIe driver does not allow module unload */ | |
747 | ||
70b3e89a | 748 | static int __init exynos_pcie_init(void) |
4b1ced84 JH |
749 | { |
750 | return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); | |
751 | } | |
70b3e89a | 752 | subsys_initcall(exynos_pcie_init); |