blob: fdb463710ba19567d67848982c9efa53c7a2390a [file] [log] [blame]
Sumit Garg3aa50882024-03-21 20:25:03 +05301// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2024 Linaro Ltd.
4 *
5 * Author: Sumit Garg <sumit.garg@linaro.org>
6 */
7
8#include <asm/io.h>
9#include <asm-generic/gpio.h>
10#include <clk.h>
11#include <dm.h>
12#include <dm/device_compat.h>
13#include <generic-phy.h>
14#include <linux/bitops.h>
15#include <linux/delay.h>
16#include <linux/err.h>
17#include <linux/iopoll.h>
18#include <log.h>
19#include <pci.h>
20#include <power/regulator.h>
21#include <regmap.h>
22#include <reset.h>
23#include <syscon.h>
24#include <time.h>
25
26#include "pcie_dw_common.h"
27
28#define PCIE_LINK_CAPABILITY 0x7c
29#define TARGET_LINK_SPEED_MASK 0xf
30#define LINK_SPEED_GEN_1 0x1
31#define LINK_SPEED_GEN_2 0x2
32#define LINK_SPEED_GEN_3 0x3
33
34#define PCIE_MISC_CONTROL_1_OFF 0x8bc
35#define PCIE_DBI_RO_WR_EN BIT(0)
36
37#define PCIE_PORT_DEBUG0 0x728
38#define PCIE_PORT_DEBUG1 0x72c
39#define PCIE_PORT_DEBUG1_LINK_UP BIT(4)
40#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29)
41
42#define PCIE_LINK_UP_TIMEOUT_MS 100
43
44#define IOMUXC_GPR14_OFFSET 0x38
45#define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10)
46#define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11)
47
48struct pcie_dw_imx {
49 /* Must be first member of the struct */
50 struct pcie_dw dw;
51 struct regmap *iomuxc_gpr;
52 struct clk_bulk clks;
53 struct gpio_desc reset_gpio;
54 struct reset_ctl apps_reset;
55 struct phy phy;
56 struct udevice *vpcie;
57};
58
Tim Harvey0b90b8c2024-04-19 08:29:01 -070059struct pcie_chip_info {
60 const char *gpr;
61};
62
63static const struct pcie_chip_info imx8mm_chip_info = {
64 .gpr = "fsl,imx8mm-iomuxc-gpr",
65};
66
67static const struct pcie_chip_info imx8mp_chip_info = {
68 .gpr = "fsl,imx8mp-iomuxc-gpr",
69};
70
Sumit Garg3aa50882024-03-21 20:25:03 +053071static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed)
72{
73 dw_pcie_dbi_write_enable(&priv->dw, true);
74
75 clrsetbits_le32(priv->dw.dbi_base + PCIE_LINK_CAPABILITY,
76 TARGET_LINK_SPEED_MASK, cap_speed);
77
78 dw_pcie_dbi_write_enable(&priv->dw, false);
79}
80
81static void imx_pcie_ltssm_enable(struct pcie_dw_imx *priv)
82{
83 reset_deassert(&priv->apps_reset);
84}
85
86static void imx_pcie_ltssm_disable(struct pcie_dw_imx *priv)
87{
88 reset_assert(&priv->apps_reset);
89}
90
91static bool is_link_up(u32 val)
92{
93 return ((val & PCIE_PORT_DEBUG1_LINK_UP) &&
94 (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
95}
96
97static int wait_link_up(struct pcie_dw_imx *priv)
98{
99 u32 val;
100
101 return readl_poll_sleep_timeout(priv->dw.dbi_base + PCIE_PORT_DEBUG1,
102 val, is_link_up(val), 10000, 100000);
103}
104
105static int pcie_link_up(struct pcie_dw_imx *priv, u32 cap_speed)
106{
107 int ret;
108
109 /* DW pre link configurations */
110 pcie_dw_configure(priv, cap_speed);
111
112 /* Initiate link training */
113 imx_pcie_ltssm_enable(priv);
114
115 /* Check that link was established */
116 ret = wait_link_up(priv);
117 if (ret)
118 imx_pcie_ltssm_disable(priv);
119
120 return ret;
121}
122
123static int imx_pcie_assert_core_reset(struct pcie_dw_imx *priv)
124{
125 if (dm_gpio_is_valid(&priv->reset_gpio)) {
126 dm_gpio_set_value(&priv->reset_gpio, 1);
127 mdelay(20);
128 }
129
130 return reset_assert(&priv->apps_reset);
131}
132
133static int imx_pcie_clk_enable(struct pcie_dw_imx *priv)
134{
135 int ret;
136
137 ret = clk_enable_bulk(&priv->clks);
138 if (ret)
139 return ret;
140
141 /*
142 * Set the over ride low and enabled make sure that
143 * REF_CLK is turned on.
144 */
145 regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
146 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE, 0);
147 regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
148 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
149 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
150
151 /* allow the clocks to stabilize */
152 udelay(500);
153
154 return 0;
155}
156
157static void imx_pcie_deassert_core_reset(struct pcie_dw_imx *priv)
158{
159 if (!dm_gpio_is_valid(&priv->reset_gpio))
160 return;
161
162 mdelay(100);
163 dm_gpio_set_value(&priv->reset_gpio, 0);
164 /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
165 mdelay(100);
166}
167
168static int pcie_dw_imx_probe(struct udevice *dev)
169{
170 struct pcie_dw_imx *priv = dev_get_priv(dev);
171 struct udevice *ctlr = pci_get_controller(dev);
172 struct pci_controller *hose = dev_get_uclass_priv(ctlr);
173 int ret;
174
175 if (priv->vpcie) {
176 ret = regulator_set_enable(priv->vpcie, true);
177 if (ret) {
178 dev_err(dev, "failed to enable vpcie regulator\n");
179 return ret;
180 }
181 }
182
183 ret = imx_pcie_assert_core_reset(priv);
184 if (ret) {
185 dev_err(dev, "failed to assert core reset\n");
186 return ret;
187 }
188
189 ret = imx_pcie_clk_enable(priv);
190 if (ret) {
191 dev_err(dev, "failed to enable clocks\n");
192 goto err_clk;
193 }
194
195 ret = generic_phy_init(&priv->phy);
196 if (ret) {
197 dev_err(dev, "failed to initialize PHY\n");
198 goto err_phy_init;
199 }
200
201 ret = generic_phy_power_on(&priv->phy);
202 if (ret) {
203 dev_err(dev, "failed to power on PHY\n");
204 goto err_phy_power;
205 }
206
207 imx_pcie_deassert_core_reset(priv);
208
209 priv->dw.first_busno = dev_seq(dev);
210 priv->dw.dev = dev;
211 pcie_dw_setup_host(&priv->dw);
212
213 if (pcie_link_up(priv, LINK_SPEED_GEN_1)) {
214 printf("PCIE-%d: Link down\n", dev_seq(dev));
215 ret = -ENODEV;
216 goto err_link;
217 }
218
219 printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
220 pcie_dw_get_link_speed(&priv->dw),
221 pcie_dw_get_link_width(&priv->dw),
222 hose->first_busno);
223
224 pcie_dw_prog_outbound_atu_unroll(&priv->dw, PCIE_ATU_REGION_INDEX0,
225 PCIE_ATU_TYPE_MEM,
226 priv->dw.mem.phys_start,
227 priv->dw.mem.bus_start, priv->dw.mem.size);
228
229 return 0;
230
231err_link:
232 generic_shutdown_phy(&priv->phy);
233err_phy_power:
234 generic_phy_exit(&priv->phy);
235err_phy_init:
236 clk_disable_bulk(&priv->clks);
237err_clk:
238 imx_pcie_deassert_core_reset(priv);
239
240 return ret;
241}
242
243static int pcie_dw_imx_remove(struct udevice *dev)
244{
245 struct pcie_dw_imx *priv = dev_get_priv(dev);
246
247 generic_shutdown_phy(&priv->phy);
248 dm_gpio_free(dev, &priv->reset_gpio);
249 reset_free(&priv->apps_reset);
250 clk_release_bulk(&priv->clks);
251
252 return 0;
253}
254
255static int pcie_dw_imx_of_to_plat(struct udevice *dev)
256{
Tim Harvey0b90b8c2024-04-19 08:29:01 -0700257 struct pcie_chip_info *info = (void *)dev_get_driver_data(dev);
Sumit Garg3aa50882024-03-21 20:25:03 +0530258 struct pcie_dw_imx *priv = dev_get_priv(dev);
259 ofnode gpr;
260 int ret;
261
262 /* Get the controller base address */
263 priv->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi");
264 if ((fdt_addr_t)priv->dw.dbi_base == FDT_ADDR_T_NONE) {
265 dev_err(dev, "failed to get dbi_base address\n");
266 return -EINVAL;
267 }
268
269 /* Get the config space base address and size */
270 priv->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config",
271 &priv->dw.cfg_size);
272 if ((fdt_addr_t)priv->dw.cfg_base == FDT_ADDR_T_NONE) {
273 dev_err(dev, "failed to get cfg_base address\n");
274 return -EINVAL;
275 }
276
277 ret = clk_get_bulk(dev, &priv->clks);
278 if (ret) {
279 dev_err(dev, "failed to get PCIe clks\n");
280 return ret;
281 }
282
283 ret = reset_get_by_name(dev, "apps", &priv->apps_reset);
284 if (ret) {
285 dev_err(dev,
286 "Failed to get PCIe apps reset control\n");
287 goto err_reset;
288 }
289
290 ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset_gpio,
291 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
292 if (ret) {
293 dev_err(dev, "unable to get reset-gpio\n");
294 goto err_gpio;
295 }
296
297 ret = generic_phy_get_by_name(dev, "pcie-phy", &priv->phy);
298 if (ret) {
299 dev_err(dev, "failed to get pcie phy\n");
300 goto err_phy;
301 }
302
Tim Harvey0b90b8c2024-04-19 08:29:01 -0700303 gpr = ofnode_by_compatible(ofnode_null(), info->gpr);
Sumit Garg3aa50882024-03-21 20:25:03 +0530304 if (ofnode_equal(gpr, ofnode_null())) {
305 dev_err(dev, "unable to find GPR node\n");
306 ret = -ENODEV;
307 goto err_phy;
308 }
309
310 priv->iomuxc_gpr = syscon_node_to_regmap(gpr);
311 if (IS_ERR(priv->iomuxc_gpr)) {
312 dev_err(dev, "unable to find iomuxc registers\n");
313 ret = PTR_ERR(priv->iomuxc_gpr);
314 goto err_phy;
315 }
316
317 /* vpcie-supply regulator is optional */
318 device_get_supply_regulator(dev, "vpcie-supply", &priv->vpcie);
319
320 return 0;
321
322err_phy:
323 dm_gpio_free(dev, &priv->reset_gpio);
324err_gpio:
325 reset_free(&priv->apps_reset);
326err_reset:
327 clk_release_bulk(&priv->clks);
328
329 return ret;
330}
331
332static const struct dm_pci_ops pcie_dw_imx_ops = {
333 .read_config = pcie_dw_read_config,
334 .write_config = pcie_dw_write_config,
335};
336
337static const struct udevice_id pcie_dw_imx_ids[] = {
Tim Harvey0b90b8c2024-04-19 08:29:01 -0700338 { .compatible = "fsl,imx8mm-pcie", .data = (ulong)&imx8mm_chip_info, },
339 { .compatible = "fsl,imx8mp-pcie", .data = (ulong)&imx8mp_chip_info, },
Sumit Garg3aa50882024-03-21 20:25:03 +0530340 { }
341};
342
343U_BOOT_DRIVER(pcie_dw_imx) = {
344 .name = "pcie_dw_imx",
345 .id = UCLASS_PCI,
346 .of_match = pcie_dw_imx_ids,
347 .ops = &pcie_dw_imx_ops,
348 .of_to_plat = pcie_dw_imx_of_to_plat,
349 .probe = pcie_dw_imx_probe,
350 .remove = pcie_dw_imx_remove,
351 .priv_auto = sizeof(struct pcie_dw_imx),
352};