blob: a2ee228224b5a8d35c145a8c64498d1caee5efc3 [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
59static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed)
60{
61 dw_pcie_dbi_write_enable(&priv->dw, true);
62
63 clrsetbits_le32(priv->dw.dbi_base + PCIE_LINK_CAPABILITY,
64 TARGET_LINK_SPEED_MASK, cap_speed);
65
66 dw_pcie_dbi_write_enable(&priv->dw, false);
67}
68
69static void imx_pcie_ltssm_enable(struct pcie_dw_imx *priv)
70{
71 reset_deassert(&priv->apps_reset);
72}
73
74static void imx_pcie_ltssm_disable(struct pcie_dw_imx *priv)
75{
76 reset_assert(&priv->apps_reset);
77}
78
79static bool is_link_up(u32 val)
80{
81 return ((val & PCIE_PORT_DEBUG1_LINK_UP) &&
82 (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
83}
84
85static int wait_link_up(struct pcie_dw_imx *priv)
86{
87 u32 val;
88
89 return readl_poll_sleep_timeout(priv->dw.dbi_base + PCIE_PORT_DEBUG1,
90 val, is_link_up(val), 10000, 100000);
91}
92
93static int pcie_link_up(struct pcie_dw_imx *priv, u32 cap_speed)
94{
95 int ret;
96
97 /* DW pre link configurations */
98 pcie_dw_configure(priv, cap_speed);
99
100 /* Initiate link training */
101 imx_pcie_ltssm_enable(priv);
102
103 /* Check that link was established */
104 ret = wait_link_up(priv);
105 if (ret)
106 imx_pcie_ltssm_disable(priv);
107
108 return ret;
109}
110
111static int imx_pcie_assert_core_reset(struct pcie_dw_imx *priv)
112{
113 if (dm_gpio_is_valid(&priv->reset_gpio)) {
114 dm_gpio_set_value(&priv->reset_gpio, 1);
115 mdelay(20);
116 }
117
118 return reset_assert(&priv->apps_reset);
119}
120
121static int imx_pcie_clk_enable(struct pcie_dw_imx *priv)
122{
123 int ret;
124
125 ret = clk_enable_bulk(&priv->clks);
126 if (ret)
127 return ret;
128
129 /*
130 * Set the over ride low and enabled make sure that
131 * REF_CLK is turned on.
132 */
133 regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
134 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE, 0);
135 regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
136 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
137 IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
138
139 /* allow the clocks to stabilize */
140 udelay(500);
141
142 return 0;
143}
144
145static void imx_pcie_deassert_core_reset(struct pcie_dw_imx *priv)
146{
147 if (!dm_gpio_is_valid(&priv->reset_gpio))
148 return;
149
150 mdelay(100);
151 dm_gpio_set_value(&priv->reset_gpio, 0);
152 /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
153 mdelay(100);
154}
155
156static int pcie_dw_imx_probe(struct udevice *dev)
157{
158 struct pcie_dw_imx *priv = dev_get_priv(dev);
159 struct udevice *ctlr = pci_get_controller(dev);
160 struct pci_controller *hose = dev_get_uclass_priv(ctlr);
161 int ret;
162
163 if (priv->vpcie) {
164 ret = regulator_set_enable(priv->vpcie, true);
165 if (ret) {
166 dev_err(dev, "failed to enable vpcie regulator\n");
167 return ret;
168 }
169 }
170
171 ret = imx_pcie_assert_core_reset(priv);
172 if (ret) {
173 dev_err(dev, "failed to assert core reset\n");
174 return ret;
175 }
176
177 ret = imx_pcie_clk_enable(priv);
178 if (ret) {
179 dev_err(dev, "failed to enable clocks\n");
180 goto err_clk;
181 }
182
183 ret = generic_phy_init(&priv->phy);
184 if (ret) {
185 dev_err(dev, "failed to initialize PHY\n");
186 goto err_phy_init;
187 }
188
189 ret = generic_phy_power_on(&priv->phy);
190 if (ret) {
191 dev_err(dev, "failed to power on PHY\n");
192 goto err_phy_power;
193 }
194
195 imx_pcie_deassert_core_reset(priv);
196
197 priv->dw.first_busno = dev_seq(dev);
198 priv->dw.dev = dev;
199 pcie_dw_setup_host(&priv->dw);
200
201 if (pcie_link_up(priv, LINK_SPEED_GEN_1)) {
202 printf("PCIE-%d: Link down\n", dev_seq(dev));
203 ret = -ENODEV;
204 goto err_link;
205 }
206
207 printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
208 pcie_dw_get_link_speed(&priv->dw),
209 pcie_dw_get_link_width(&priv->dw),
210 hose->first_busno);
211
212 pcie_dw_prog_outbound_atu_unroll(&priv->dw, PCIE_ATU_REGION_INDEX0,
213 PCIE_ATU_TYPE_MEM,
214 priv->dw.mem.phys_start,
215 priv->dw.mem.bus_start, priv->dw.mem.size);
216
217 return 0;
218
219err_link:
220 generic_shutdown_phy(&priv->phy);
221err_phy_power:
222 generic_phy_exit(&priv->phy);
223err_phy_init:
224 clk_disable_bulk(&priv->clks);
225err_clk:
226 imx_pcie_deassert_core_reset(priv);
227
228 return ret;
229}
230
231static int pcie_dw_imx_remove(struct udevice *dev)
232{
233 struct pcie_dw_imx *priv = dev_get_priv(dev);
234
235 generic_shutdown_phy(&priv->phy);
236 dm_gpio_free(dev, &priv->reset_gpio);
237 reset_free(&priv->apps_reset);
238 clk_release_bulk(&priv->clks);
239
240 return 0;
241}
242
243static int pcie_dw_imx_of_to_plat(struct udevice *dev)
244{
245 struct pcie_dw_imx *priv = dev_get_priv(dev);
246 ofnode gpr;
247 int ret;
248
249 /* Get the controller base address */
250 priv->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi");
251 if ((fdt_addr_t)priv->dw.dbi_base == FDT_ADDR_T_NONE) {
252 dev_err(dev, "failed to get dbi_base address\n");
253 return -EINVAL;
254 }
255
256 /* Get the config space base address and size */
257 priv->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config",
258 &priv->dw.cfg_size);
259 if ((fdt_addr_t)priv->dw.cfg_base == FDT_ADDR_T_NONE) {
260 dev_err(dev, "failed to get cfg_base address\n");
261 return -EINVAL;
262 }
263
264 ret = clk_get_bulk(dev, &priv->clks);
265 if (ret) {
266 dev_err(dev, "failed to get PCIe clks\n");
267 return ret;
268 }
269
270 ret = reset_get_by_name(dev, "apps", &priv->apps_reset);
271 if (ret) {
272 dev_err(dev,
273 "Failed to get PCIe apps reset control\n");
274 goto err_reset;
275 }
276
277 ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset_gpio,
278 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
279 if (ret) {
280 dev_err(dev, "unable to get reset-gpio\n");
281 goto err_gpio;
282 }
283
284 ret = generic_phy_get_by_name(dev, "pcie-phy", &priv->phy);
285 if (ret) {
286 dev_err(dev, "failed to get pcie phy\n");
287 goto err_phy;
288 }
289
290 gpr = ofnode_by_compatible(ofnode_null(), "fsl,imx8mp-iomuxc-gpr");
291 if (ofnode_equal(gpr, ofnode_null())) {
292 dev_err(dev, "unable to find GPR node\n");
293 ret = -ENODEV;
294 goto err_phy;
295 }
296
297 priv->iomuxc_gpr = syscon_node_to_regmap(gpr);
298 if (IS_ERR(priv->iomuxc_gpr)) {
299 dev_err(dev, "unable to find iomuxc registers\n");
300 ret = PTR_ERR(priv->iomuxc_gpr);
301 goto err_phy;
302 }
303
304 /* vpcie-supply regulator is optional */
305 device_get_supply_regulator(dev, "vpcie-supply", &priv->vpcie);
306
307 return 0;
308
309err_phy:
310 dm_gpio_free(dev, &priv->reset_gpio);
311err_gpio:
312 reset_free(&priv->apps_reset);
313err_reset:
314 clk_release_bulk(&priv->clks);
315
316 return ret;
317}
318
319static const struct dm_pci_ops pcie_dw_imx_ops = {
320 .read_config = pcie_dw_read_config,
321 .write_config = pcie_dw_write_config,
322};
323
324static const struct udevice_id pcie_dw_imx_ids[] = {
325 { .compatible = "fsl,imx8mp-pcie" },
326 { }
327};
328
329U_BOOT_DRIVER(pcie_dw_imx) = {
330 .name = "pcie_dw_imx",
331 .id = UCLASS_PCI,
332 .of_match = pcie_dw_imx_ids,
333 .ops = &pcie_dw_imx_ops,
334 .of_to_plat = pcie_dw_imx_of_to_plat,
335 .probe = pcie_dw_imx_probe,
336 .remove = pcie_dw_imx_remove,
337 .priv_auto = sizeof(struct pcie_dw_imx),
338};