blob: 2418388cb3c23dd01edd00b7d18cb191b1e5c3ee [file] [log] [blame]
Sumit Gargc214ebc2024-03-21 20:25:02 +05301// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2024 Linaro Ltd.
4 *
5 * Derived from Linux counterpart driver
6 */
7
8#include <asm/io.h>
9#include <clk.h>
10#include <dm.h>
11#include <dm/device_compat.h>
12#include <generic-phy.h>
13#include <linux/bitfield.h>
14#include <linux/clk-provider.h>
15#include <linux/iopoll.h>
16#include <syscon.h>
17#include <regmap.h>
18#include <reset.h>
19
20#include <dt-bindings/phy/phy-imx8-pcie.h>
21
22#define IMX8MM_PCIE_PHY_CMN_REG061 0x184
23#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0)
24#define IMX8MM_PCIE_PHY_CMN_REG062 0x188
25#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3)
26#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C
27#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6)
28#define IMX8MM_PCIE_PHY_CMN_REG064 0x190
29#define ANA_AUX_RX_TX_SEL_TX BIT(7)
30#define ANA_AUX_RX_TERM_GND_EN BIT(3)
31#define ANA_AUX_TX_TERM BIT(2)
32#define IMX8MM_PCIE_PHY_CMN_REG065 0x194
33#define ANA_AUX_RX_TERM (BIT(7) | BIT(4))
34#define ANA_AUX_TX_LVL GENMASK(3, 0)
35#define IMX8MM_PCIE_PHY_CMN_REG075 0x1D4
36#define ANA_PLL_DONE 0x3
37#define PCIE_PHY_TRSV_REG5 0x414
38#define PCIE_PHY_TRSV_REG6 0x418
39
40#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24)
41#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3)
42#define IMX8MM_GPR_PCIE_REF_CLK_EXT FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2)
43#define IMX8MM_GPR_PCIE_AUX_EN BIT(19)
44#define IMX8MM_GPR_PCIE_CMN_RST BIT(18)
45#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17)
46#define IMX8MM_GPR_PCIE_SSC_EN BIT(16)
47#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9)
48
49#define IOMUXC_GPR14_OFFSET 0x38
50
51enum imx8_pcie_phy_type {
52 IMX8MM,
53 IMX8MP,
54};
55
56struct imx8_pcie_phy_drvdata {
57 const char *gpr;
58 enum imx8_pcie_phy_type variant;
59};
60
61struct imx8_pcie_phy {
62 ulong base;
63 struct clk hsio_clk;
64 struct regmap *iomuxc_gpr;
65 struct reset_ctl perst;
66 struct reset_ctl reset;
67 u32 refclk_pad_mode;
68 u32 tx_deemph_gen1;
69 u32 tx_deemph_gen2;
70 bool clkreq_unused;
71 const struct imx8_pcie_phy_drvdata *drvdata;
72};
73
74static int imx8_pcie_phy_power_on(struct phy *phy)
75{
76 int ret;
77 u32 val, pad_mode;
78 struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev);
79
80 pad_mode = imx8_phy->refclk_pad_mode;
81 switch (imx8_phy->drvdata->variant) {
82 case IMX8MM:
83 reset_assert(&imx8_phy->reset);
84
85 /* Tune PHY de-emphasis setting to pass PCIe compliance. */
86 if (imx8_phy->tx_deemph_gen1)
87 writel(imx8_phy->tx_deemph_gen1,
88 imx8_phy->base + PCIE_PHY_TRSV_REG5);
89 if (imx8_phy->tx_deemph_gen2)
90 writel(imx8_phy->tx_deemph_gen2,
91 imx8_phy->base + PCIE_PHY_TRSV_REG6);
92 break;
93 case IMX8MP: /* Do nothing. */
94 break;
95 }
96
97 if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ||
98 pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
99 /* Configure the pad as input */
100 val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
101 writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
102 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
103 } else {
104 /* Configure the PHY to output the refclock via pad */
105 writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
106 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
107 }
108
109 if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT ||
110 pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
111 /* Source clock from SoC internal PLL */
112 writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL,
113 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062);
114 writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
115 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
116 val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM;
117 writel(val | ANA_AUX_RX_TERM_GND_EN,
118 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064);
119 writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL,
120 imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065);
121 }
122
123 /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
124 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
125 IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
126 imx8_phy->clkreq_unused ?
127 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
128 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
129 IMX8MM_GPR_PCIE_AUX_EN,
130 IMX8MM_GPR_PCIE_AUX_EN);
131 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
132 IMX8MM_GPR_PCIE_POWER_OFF, 0);
133 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
134 IMX8MM_GPR_PCIE_SSC_EN, 0);
135
136 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
137 IMX8MM_GPR_PCIE_REF_CLK_SEL,
138 pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
139 IMX8MM_GPR_PCIE_REF_CLK_EXT :
140 IMX8MM_GPR_PCIE_REF_CLK_PLL);
141 udelay(200);
142
143 /* Do the PHY common block reset */
144 regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
145 IMX8MM_GPR_PCIE_CMN_RST,
146 IMX8MM_GPR_PCIE_CMN_RST);
147
148 switch (imx8_phy->drvdata->variant) {
149 case IMX8MP:
150 reset_deassert(&imx8_phy->perst);
151 fallthrough;
152 case IMX8MM:
153 reset_deassert(&imx8_phy->reset);
154 udelay(500);
155 break;
156 }
157
158 /* Polling to check the phy is ready or not. */
159 ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075,
160 val, val == ANA_PLL_DONE, 20000);
161 return ret;
162}
163
164static int imx8_pcie_phy_init(struct phy *phy)
165{
166 struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev);
167
168 return clk_enable(&imx8_phy->hsio_clk);
169}
170
171static int imx8_pcie_phy_exit(struct phy *phy)
172{
173 struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev);
174
175 return clk_disable(&imx8_phy->hsio_clk);
176}
177
178static const struct phy_ops imx8_pcie_phy_ops = {
179 .init = imx8_pcie_phy_init,
180 .exit = imx8_pcie_phy_exit,
181 .power_on = imx8_pcie_phy_power_on,
182};
183
184static const struct imx8_pcie_phy_drvdata imx8mm_drvdata = {
185 .gpr = "fsl,imx8mm-iomuxc-gpr",
186 .variant = IMX8MM,
187};
188
189static const struct imx8_pcie_phy_drvdata imx8mp_drvdata = {
190 .gpr = "fsl,imx8mp-iomuxc-gpr",
191 .variant = IMX8MP,
192};
193
194static const struct udevice_id imx8_pcie_phy_of_match[] = {
195 {.compatible = "fsl,imx8mm-pcie-phy", .data = (ulong)&imx8mm_drvdata, },
196 {.compatible = "fsl,imx8mp-pcie-phy", .data = (ulong)&imx8mp_drvdata, },
197 { },
198};
199
200static int imx8_pcie_phy_probe(struct udevice *dev)
201{
202 struct imx8_pcie_phy *imx8_phy = dev_get_priv(dev);
203 ofnode gpr;
204 int ret = 0;
205
206 imx8_phy->drvdata = (void *)dev_get_driver_data(dev);
207 imx8_phy->base = dev_read_addr(dev);
208 if (!imx8_phy->base)
209 return -EINVAL;
210
211 /* get PHY refclk pad mode */
212 dev_read_u32(dev, "fsl,refclk-pad-mode", &imx8_phy->refclk_pad_mode);
213
214 imx8_phy->tx_deemph_gen1 = dev_read_u32_default(dev,
215 "fsl,tx-deemph-gen1",
216 0);
217 imx8_phy->tx_deemph_gen2 = dev_read_u32_default(dev,
218 "fsl,tx-deemph-gen2",
219 0);
220 imx8_phy->clkreq_unused = dev_read_bool(dev, "fsl,clkreq-unsupported");
221
222 /* Grab GPR config register range */
223 gpr = ofnode_by_compatible(ofnode_null(), imx8_phy->drvdata->gpr);
224 if (ofnode_equal(gpr, ofnode_null())) {
225 dev_err(dev, "unable to find GPR node\n");
226 return -ENODEV;
227 }
228
229 imx8_phy->iomuxc_gpr = syscon_node_to_regmap(gpr);
230 if (IS_ERR(imx8_phy->iomuxc_gpr)) {
231 dev_err(dev, "unable to find iomuxc registers\n");
232 return PTR_ERR(imx8_phy->iomuxc_gpr);
233 }
234
235 ret = clk_get_by_name(dev, "ref", &imx8_phy->hsio_clk);
236 if (ret) {
237 dev_err(dev, "Failed to get PCIEPHY ref clock\n");
238 return ret;
239 }
240
241 ret = reset_get_by_name(dev, "pciephy", &imx8_phy->reset);
242 if (ret) {
243 dev_err(dev, "Failed to get PCIEPHY reset control\n");
244 return ret;
245 }
246
247 if (imx8_phy->drvdata->variant == IMX8MP) {
248 ret = reset_get_by_name(dev, "perst", &imx8_phy->perst);
249 if (ret) {
250 dev_err(dev,
251 "Failed to get PCIEPHY PHY PERST control\n");
252 goto err_perst;
253 }
254 }
255
256 return 0;
257
258err_perst:
259 reset_free(&imx8_phy->reset);
260 return ret;
261}
262
263static int imx8_pcie_phy_remove(struct udevice *dev)
264{
265 struct imx8_pcie_phy *imx8_phy = dev_get_priv(dev);
266
267 if (imx8_phy->drvdata->variant == IMX8MP)
268 reset_free(&imx8_phy->perst);
269
270 reset_free(&imx8_phy->reset);
271
272 return 0;
273}
274
275U_BOOT_DRIVER(nxp_imx8_pcie_phy) = {
276 .name = "nxp_imx8_pcie_phy",
277 .id = UCLASS_PHY,
278 .of_match = imx8_pcie_phy_of_match,
279 .probe = imx8_pcie_phy_probe,
280 .remove = imx8_pcie_phy_remove,
281 .ops = &imx8_pcie_phy_ops,
282 .priv_auto = sizeof(struct imx8_pcie_phy),
283};