]>
Commit | Line | Data |
---|---|---|
2c32c701 NA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Amlogic DesignWare based PCIe host controller driver | |
4 | * | |
5 | * Copyright (c) 2021 BayLibre, SAS | |
6 | * Author: Neil Armstrong <[email protected]> | |
7 | * | |
8 | * Based on pcie_dw_rockchip.c | |
9 | * Copyright (c) 2021 Rockchip, Inc. | |
10 | */ | |
11 | ||
2c32c701 NA |
12 | #include <clk.h> |
13 | #include <dm.h> | |
14 | #include <generic-phy.h> | |
15 | #include <pci.h> | |
16 | #include <power-domain.h> | |
17 | #include <reset.h> | |
18 | #include <syscon.h> | |
19 | #include <asm/global_data.h> | |
20 | #include <asm/io.h> | |
21 | #include <asm-generic/gpio.h> | |
22 | #include <dm/device_compat.h> | |
23 | #include <linux/iopoll.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/log2.h> | |
26 | #include <linux/bitfield.h> | |
27 | ||
28 | #include "pcie_dw_common.h" | |
29 | ||
30 | DECLARE_GLOBAL_DATA_PTR; | |
31 | ||
32 | /** | |
33 | * struct meson_pcie - Amlogic Meson DW PCIe controller state | |
34 | * | |
35 | * @pci: The common PCIe DW structure | |
36 | * @meson_cfg_base: The base address of vendor regs | |
37 | * @phy | |
38 | * @clk_port | |
39 | * @clk_general | |
40 | * @clk_pclk | |
41 | * @rsts | |
42 | * @rst_gpio: The #PERST signal for slot | |
43 | */ | |
44 | struct meson_pcie { | |
45 | /* Must be first member of the struct */ | |
46 | struct pcie_dw dw; | |
47 | void *meson_cfg_base; | |
48 | struct phy phy; | |
49 | struct clk clk_port; | |
50 | struct clk clk_general; | |
51 | struct clk clk_pclk; | |
52 | struct reset_ctl_bulk rsts; | |
53 | struct gpio_desc rst_gpio; | |
54 | }; | |
55 | ||
56 | #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ | |
57 | ||
58 | #define PCIE_CAP_MAX_PAYLOAD_SIZE(x) ((x) << 5) | |
59 | #define PCIE_CAP_MAX_READ_REQ_SIZE(x) ((x) << 12) | |
60 | ||
61 | /* PCIe specific config registers */ | |
62 | #define PCIE_CFG0 0x0 | |
63 | #define APP_LTSSM_ENABLE BIT(7) | |
64 | ||
65 | #define PCIE_CFG_STATUS12 0x30 | |
66 | #define IS_SMLH_LINK_UP(x) ((x) & (1 << 6)) | |
67 | #define IS_RDLH_LINK_UP(x) ((x) & (1 << 16)) | |
68 | #define IS_LTSSM_UP(x) ((((x) >> 10) & 0x1f) == 0x11) | |
69 | ||
70 | #define PCIE_CFG_STATUS17 0x44 | |
71 | #define PM_CURRENT_STATE(x) (((x) >> 7) & 0x1) | |
72 | ||
73 | #define WAIT_LINKUP_TIMEOUT 4000 | |
74 | #define PORT_CLK_RATE 100000000UL | |
75 | #define MAX_PAYLOAD_SIZE 256 | |
76 | #define MAX_READ_REQ_SIZE 256 | |
77 | #define PCIE_RESET_DELAY 500 | |
78 | #define PCIE_SHARED_RESET 1 | |
79 | #define PCIE_NORMAL_RESET 0 | |
80 | ||
81 | enum pcie_data_rate { | |
82 | PCIE_GEN1, | |
83 | PCIE_GEN2, | |
84 | PCIE_GEN3, | |
85 | PCIE_GEN4 | |
86 | }; | |
87 | ||
88 | /* Parameters for the waiting for #perst signal */ | |
89 | #define PERST_WAIT_US 1000000 | |
90 | ||
91 | static inline u32 meson_cfg_readl(struct meson_pcie *priv, u32 reg) | |
92 | { | |
93 | return readl(priv->meson_cfg_base + reg); | |
94 | } | |
95 | ||
96 | static inline void meson_cfg_writel(struct meson_pcie *priv, u32 val, u32 reg) | |
97 | { | |
98 | writel(val, priv->meson_cfg_base + reg); | |
99 | } | |
100 | ||
101 | /** | |
102 | * meson_pcie_configure() - Configure link | |
103 | * | |
104 | * @meson_pcie: Pointer to the PCI controller state | |
105 | * | |
106 | * Configure the link mode and width | |
107 | */ | |
108 | static void meson_pcie_configure(struct meson_pcie *priv) | |
109 | { | |
110 | u32 val; | |
111 | ||
112 | dw_pcie_dbi_write_enable(&priv->dw, true); | |
113 | ||
114 | val = readl(priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); | |
115 | val &= ~PORT_LINK_FAST_LINK_MODE; | |
116 | val |= PORT_LINK_DLL_LINK_EN; | |
117 | val &= ~PORT_LINK_MODE_MASK; | |
118 | val |= PORT_LINK_MODE_1_LANES; | |
119 | writel(val, priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); | |
120 | ||
121 | val = readl(priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); | |
122 | val &= ~PORT_LOGIC_LINK_WIDTH_MASK; | |
123 | val |= PORT_LOGIC_LINK_WIDTH_1_LANES; | |
124 | writel(val, priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); | |
125 | ||
126 | dw_pcie_dbi_write_enable(&priv->dw, false); | |
127 | } | |
128 | ||
129 | static inline void meson_pcie_enable_ltssm(struct meson_pcie *priv) | |
130 | { | |
131 | u32 val; | |
132 | ||
133 | val = meson_cfg_readl(priv, PCIE_CFG0); | |
134 | val |= APP_LTSSM_ENABLE; | |
135 | meson_cfg_writel(priv, val, PCIE_CFG0); | |
136 | } | |
137 | ||
138 | static int meson_pcie_wait_link_up(struct meson_pcie *priv) | |
139 | { | |
140 | u32 speed_okay = 0; | |
141 | u32 cnt = 0; | |
142 | u32 state12, state17, smlh_up, ltssm_up, rdlh_up; | |
143 | ||
144 | do { | |
145 | state12 = meson_cfg_readl(priv, PCIE_CFG_STATUS12); | |
146 | state17 = meson_cfg_readl(priv, PCIE_CFG_STATUS17); | |
147 | smlh_up = IS_SMLH_LINK_UP(state12); | |
148 | rdlh_up = IS_RDLH_LINK_UP(state12); | |
149 | ltssm_up = IS_LTSSM_UP(state12); | |
150 | ||
151 | if (PM_CURRENT_STATE(state17) < PCIE_GEN3) | |
152 | speed_okay = 1; | |
153 | ||
154 | if (smlh_up) | |
155 | debug("%s: smlh_link_up is on\n", __func__); | |
156 | if (rdlh_up) | |
157 | debug("%s: rdlh_link_up is on\n", __func__); | |
158 | if (ltssm_up) | |
159 | debug("%s: ltssm_up is on\n", __func__); | |
160 | if (speed_okay) | |
161 | debug("%s: speed_okay\n", __func__); | |
162 | ||
163 | if (smlh_up && rdlh_up && ltssm_up && speed_okay) | |
164 | return 0; | |
165 | ||
166 | cnt++; | |
167 | ||
168 | udelay(10); | |
169 | } while (cnt < WAIT_LINKUP_TIMEOUT); | |
170 | ||
171 | printf("%s: error: wait linkup timeout\n", __func__); | |
172 | return -EIO; | |
173 | } | |
174 | ||
175 | /** | |
176 | * meson_pcie_link_up() - Wait for the link to come up | |
177 | * | |
178 | * @meson_pcie: Pointer to the PCI controller state | |
179 | * @cap_speed: Desired link speed | |
180 | * | |
181 | * Return: 1 (true) for active line and negative (false) for no link (timeout) | |
182 | */ | |
183 | static int meson_pcie_link_up(struct meson_pcie *priv, u32 cap_speed) | |
184 | { | |
185 | /* DW link configurations */ | |
186 | meson_pcie_configure(priv); | |
187 | ||
188 | /* Reset the device */ | |
189 | if (dm_gpio_is_valid(&priv->rst_gpio)) { | |
190 | dm_gpio_set_value(&priv->rst_gpio, 1); | |
191 | /* | |
192 | * Minimal is 100ms from spec but we see | |
193 | * some wired devices need much more, such as 600ms. | |
194 | * Add a enough delay to cover all cases. | |
195 | */ | |
196 | udelay(PERST_WAIT_US); | |
197 | dm_gpio_set_value(&priv->rst_gpio, 0); | |
198 | } | |
199 | ||
200 | /* Enable LTSSM */ | |
201 | meson_pcie_enable_ltssm(priv); | |
202 | ||
203 | return meson_pcie_wait_link_up(priv); | |
204 | } | |
205 | ||
206 | static int meson_size_to_payload(int size) | |
207 | { | |
208 | /* | |
209 | * dwc supports 2^(val+7) payload size, which val is 0~5 default to 1. | |
210 | * So if input size is not 2^order alignment or less than 2^7 or bigger | |
211 | * than 2^12, just set to default size 2^(1+7). | |
212 | */ | |
213 | if (!is_power_of_2(size) || size < 128 || size > 4096) { | |
214 | debug("%s: payload size %d, set to default 256\n", __func__, size); | |
215 | return 1; | |
216 | } | |
217 | ||
218 | return fls(size) - 8; | |
219 | } | |
220 | ||
221 | static void meson_set_max_payload(struct meson_pcie *priv, int size) | |
222 | { | |
223 | u32 val; | |
224 | u16 offset = dm_pci_find_capability(priv->dw.dev, PCI_CAP_ID_EXP); | |
225 | int max_payload_size = meson_size_to_payload(size); | |
226 | ||
227 | dw_pcie_dbi_write_enable(&priv->dw, true); | |
228 | ||
229 | val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
230 | val &= ~PCI_EXP_DEVCTL_PAYLOAD; | |
231 | writel(val, priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
232 | ||
233 | val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
234 | val |= PCIE_CAP_MAX_PAYLOAD_SIZE(max_payload_size); | |
235 | writel(val, priv->dw.dbi_base + PCI_EXP_DEVCTL); | |
236 | ||
237 | dw_pcie_dbi_write_enable(&priv->dw, false); | |
238 | } | |
239 | ||
240 | static void meson_set_max_rd_req_size(struct meson_pcie *priv, int size) | |
241 | { | |
242 | u32 val; | |
243 | u16 offset = dm_pci_find_capability(priv->dw.dev, PCI_CAP_ID_EXP); | |
244 | int max_rd_req_size = meson_size_to_payload(size); | |
245 | ||
246 | dw_pcie_dbi_write_enable(&priv->dw, true); | |
247 | ||
248 | val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
249 | val &= ~PCI_EXP_DEVCTL_PAYLOAD; | |
250 | writel(val, priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
251 | ||
252 | val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); | |
253 | val |= PCIE_CAP_MAX_READ_REQ_SIZE(max_rd_req_size); | |
254 | writel(val, priv->dw.dbi_base + PCI_EXP_DEVCTL); | |
255 | ||
256 | dw_pcie_dbi_write_enable(&priv->dw, false); | |
257 | } | |
258 | ||
259 | static int meson_pcie_init_port(struct udevice *dev) | |
260 | { | |
261 | int ret; | |
262 | struct meson_pcie *priv = dev_get_priv(dev); | |
263 | ||
264 | ret = generic_phy_init(&priv->phy); | |
265 | if (ret) { | |
266 | dev_err(dev, "failed to init phy (ret=%d)\n", ret); | |
267 | return ret; | |
268 | } | |
269 | ||
270 | ret = generic_phy_power_on(&priv->phy); | |
271 | if (ret) { | |
272 | dev_err(dev, "failed to power on phy (ret=%d)\n", ret); | |
273 | goto err_exit_phy; | |
274 | } | |
275 | ||
276 | ret = generic_phy_reset(&priv->phy); | |
277 | if (ret) { | |
278 | dev_err(dev, "failed to reset phy (ret=%d)\n", ret); | |
279 | goto err_exit_phy; | |
280 | } | |
281 | ||
282 | ret = reset_assert_bulk(&priv->rsts); | |
283 | if (ret) { | |
284 | dev_err(dev, "failed to assert resets (ret=%d)\n", ret); | |
285 | goto err_power_off_phy; | |
286 | } | |
287 | ||
288 | udelay(PCIE_RESET_DELAY); | |
289 | ||
290 | ret = reset_deassert_bulk(&priv->rsts); | |
291 | if (ret) { | |
292 | dev_err(dev, "failed to deassert resets (ret=%d)\n", ret); | |
293 | goto err_power_off_phy; | |
294 | } | |
295 | ||
296 | udelay(PCIE_RESET_DELAY); | |
297 | ||
298 | ret = clk_set_rate(&priv->clk_port, PORT_CLK_RATE); | |
299 | if (ret) { | |
300 | dev_err(dev, "failed to set port clk rate (ret=%d)\n", ret); | |
301 | goto err_deassert_bulk; | |
302 | } | |
303 | ||
304 | ret = clk_enable(&priv->clk_general); | |
305 | if (ret) { | |
306 | dev_err(dev, "failed to enable clk general (ret=%d)\n", ret); | |
307 | goto err_deassert_bulk; | |
308 | } | |
309 | ||
310 | ret = clk_enable(&priv->clk_pclk); | |
311 | if (ret) { | |
312 | dev_err(dev, "failed to enable pclk (ret=%d)\n", ret); | |
313 | goto err_deassert_bulk; | |
314 | } | |
315 | ||
316 | meson_set_max_payload(priv, MAX_PAYLOAD_SIZE); | |
317 | meson_set_max_rd_req_size(priv, MAX_READ_REQ_SIZE); | |
318 | ||
319 | pcie_dw_setup_host(&priv->dw); | |
320 | ||
d6c10360 | 321 | meson_pcie_link_up(priv, LINK_SPEED_GEN_2); |
2c32c701 NA |
322 | |
323 | return 0; | |
2c32c701 NA |
324 | err_deassert_bulk: |
325 | reset_assert_bulk(&priv->rsts); | |
326 | err_power_off_phy: | |
327 | generic_phy_power_off(&priv->phy); | |
328 | err_exit_phy: | |
329 | generic_phy_exit(&priv->phy); | |
330 | ||
331 | return ret; | |
332 | } | |
333 | ||
334 | static int meson_pcie_parse_dt(struct udevice *dev) | |
335 | { | |
336 | struct meson_pcie *priv = dev_get_priv(dev); | |
337 | int ret; | |
338 | ||
e5822ecb | 339 | priv->dw.dbi_base = dev_read_addr_index_ptr(dev, 0); |
2c32c701 | 340 | if (!priv->dw.dbi_base) |
e5822ecb | 341 | return -EINVAL; |
2c32c701 NA |
342 | |
343 | dev_dbg(dev, "ELBI address is 0x%p\n", priv->dw.dbi_base); | |
344 | ||
e5822ecb | 345 | priv->meson_cfg_base = dev_read_addr_index_ptr(dev, 1); |
2c32c701 | 346 | if (!priv->meson_cfg_base) |
e5822ecb | 347 | return -EINVAL; |
2c32c701 NA |
348 | |
349 | dev_dbg(dev, "CFG address is 0x%p\n", priv->meson_cfg_base); | |
350 | ||
351 | ret = gpio_request_by_name(dev, "reset-gpios", 0, | |
352 | &priv->rst_gpio, GPIOD_IS_OUT); | |
353 | if (ret) { | |
354 | dev_err(dev, "failed to find reset-gpios property\n"); | |
355 | return ret; | |
356 | } | |
357 | ||
358 | ret = reset_get_bulk(dev, &priv->rsts); | |
359 | if (ret) { | |
360 | dev_err(dev, "Can't get reset: %d\n", ret); | |
361 | return ret; | |
362 | } | |
363 | ||
364 | ret = clk_get_by_name(dev, "port", &priv->clk_port); | |
365 | if (ret) { | |
366 | dev_err(dev, "Can't get port clock: %d\n", ret); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | ret = clk_get_by_name(dev, "general", &priv->clk_general); | |
371 | if (ret) { | |
372 | dev_err(dev, "Can't get port clock: %d\n", ret); | |
373 | return ret; | |
374 | } | |
375 | ||
376 | ret = clk_get_by_name(dev, "pclk", &priv->clk_pclk); | |
377 | if (ret) { | |
378 | dev_err(dev, "Can't get port clock: %d\n", ret); | |
379 | return ret; | |
380 | } | |
381 | ||
382 | ret = generic_phy_get_by_index(dev, 0, &priv->phy); | |
383 | if (ret) { | |
384 | dev_err(dev, "failed to get pcie phy (ret=%d)\n", ret); | |
385 | return ret; | |
386 | } | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
391 | /** | |
392 | * meson_pcie_probe() - Probe the PCIe bus for active link | |
393 | * | |
394 | * @dev: A pointer to the device being operated on | |
395 | * | |
396 | * Probe for an active link on the PCIe bus and configure the controller | |
397 | * to enable this port. | |
398 | * | |
399 | * Return: 0 on success, else -ENODEV | |
400 | */ | |
401 | static int meson_pcie_probe(struct udevice *dev) | |
402 | { | |
403 | struct meson_pcie *priv = dev_get_priv(dev); | |
404 | struct udevice *ctlr = pci_get_controller(dev); | |
405 | struct pci_controller *hose = dev_get_uclass_priv(ctlr); | |
406 | int ret = 0; | |
407 | ||
408 | priv->dw.first_busno = dev_seq(dev); | |
409 | priv->dw.dev = dev; | |
410 | ||
411 | ret = meson_pcie_parse_dt(dev); | |
412 | if (ret) | |
413 | return ret; | |
414 | ||
415 | ret = meson_pcie_init_port(dev); | |
416 | if (ret) { | |
417 | dm_gpio_free(dev, &priv->rst_gpio); | |
418 | return ret; | |
419 | } | |
420 | ||
421 | printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", | |
422 | dev_seq(dev), pcie_dw_get_link_speed(&priv->dw), | |
423 | pcie_dw_get_link_width(&priv->dw), | |
424 | hose->first_busno); | |
425 | ||
426 | return pcie_dw_prog_outbound_atu_unroll(&priv->dw, | |
427 | PCIE_ATU_REGION_INDEX0, | |
428 | PCIE_ATU_TYPE_MEM, | |
429 | priv->dw.mem.phys_start, | |
430 | priv->dw.mem.bus_start, | |
431 | priv->dw.mem.size); | |
432 | } | |
433 | ||
434 | static const struct dm_pci_ops meson_pcie_ops = { | |
435 | .read_config = pcie_dw_read_config, | |
436 | .write_config = pcie_dw_write_config, | |
437 | }; | |
438 | ||
439 | static const struct udevice_id meson_pcie_ids[] = { | |
440 | { .compatible = "amlogic,axg-pcie" }, | |
441 | { .compatible = "amlogic,g12a-pcie" }, | |
442 | { } | |
443 | }; | |
444 | ||
445 | U_BOOT_DRIVER(meson_dw_pcie) = { | |
446 | .name = "pcie_dw_meson", | |
447 | .id = UCLASS_PCI, | |
448 | .of_match = meson_pcie_ids, | |
449 | .ops = &meson_pcie_ops, | |
450 | .probe = meson_pcie_probe, | |
451 | .priv_auto = sizeof(struct meson_pcie), | |
452 | }; |