1 // SPDX-License-Identifier: GPL-2.0+
6 #include <linux/bitfield.h>
8 #include <linux/delay.h>
10 #include <linux/iopoll.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
13 #include <linux/module.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/reset.h>
20 #include <dt-bindings/phy/phy-imx8-pcie.h>
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
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)
49 enum imx8_pcie_phy_type
{
54 struct imx8_pcie_phy_drvdata
{
56 enum imx8_pcie_phy_type variant
;
59 struct imx8_pcie_phy
{
63 struct regmap
*iomuxc_gpr
;
64 struct reset_control
*perst
;
65 struct reset_control
*reset
;
70 const struct imx8_pcie_phy_drvdata
*drvdata
;
73 static int imx8_pcie_phy_power_on(struct phy
*phy
)
77 struct imx8_pcie_phy
*imx8_phy
= phy_get_drvdata(phy
);
79 pad_mode
= imx8_phy
->refclk_pad_mode
;
80 switch (imx8_phy
->drvdata
->variant
) {
82 reset_control_assert(imx8_phy
->reset
);
84 /* Tune PHY de-emphasis setting to pass PCIe compliance. */
85 if (imx8_phy
->tx_deemph_gen1
)
86 writel(imx8_phy
->tx_deemph_gen1
,
87 imx8_phy
->base
+ PCIE_PHY_TRSV_REG5
);
88 if (imx8_phy
->tx_deemph_gen2
)
89 writel(imx8_phy
->tx_deemph_gen2
,
90 imx8_phy
->base
+ PCIE_PHY_TRSV_REG6
);
92 case IMX8MP
: /* Do nothing. */
96 if (pad_mode
== IMX8_PCIE_REFCLK_PAD_INPUT
||
97 pad_mode
== IMX8_PCIE_REFCLK_PAD_UNUSED
) {
98 /* Configure the pad as input */
99 val
= readl(imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG061
);
100 writel(val
& ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN
,
101 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG061
);
103 /* Configure the PHY to output the refclock via pad */
104 writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN
,
105 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG061
);
108 if (pad_mode
== IMX8_PCIE_REFCLK_PAD_OUTPUT
||
109 pad_mode
== IMX8_PCIE_REFCLK_PAD_UNUSED
) {
110 /* Source clock from SoC internal PLL */
111 writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL
,
112 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG062
);
113 if (imx8_phy
->drvdata
->variant
!= IMX8MM
) {
114 writel(AUX_PLL_REFCLK_SEL_SYS_PLL
,
115 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG063
);
117 val
= ANA_AUX_RX_TX_SEL_TX
| ANA_AUX_TX_TERM
;
118 writel(val
| ANA_AUX_RX_TERM_GND_EN
,
119 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG064
);
120 writel(ANA_AUX_RX_TERM
| ANA_AUX_TX_LVL
,
121 imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG065
);
124 /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
125 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
126 IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE
,
127 imx8_phy
->clkreq_unused
?
128 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE
);
129 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
130 IMX8MM_GPR_PCIE_AUX_EN
,
131 IMX8MM_GPR_PCIE_AUX_EN
);
132 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
133 IMX8MM_GPR_PCIE_POWER_OFF
, 0);
134 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
135 IMX8MM_GPR_PCIE_SSC_EN
, 0);
137 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
138 IMX8MM_GPR_PCIE_REF_CLK_SEL
,
139 pad_mode
== IMX8_PCIE_REFCLK_PAD_INPUT
?
140 IMX8MM_GPR_PCIE_REF_CLK_EXT
:
141 IMX8MM_GPR_PCIE_REF_CLK_PLL
);
142 usleep_range(100, 200);
144 switch (imx8_phy
->drvdata
->variant
) {
146 reset_control_deassert(imx8_phy
->perst
);
149 reset_control_deassert(imx8_phy
->reset
);
150 usleep_range(200, 500);
154 /* Do the PHY common block reset */
155 regmap_update_bits(imx8_phy
->iomuxc_gpr
, IOMUXC_GPR14
,
156 IMX8MM_GPR_PCIE_CMN_RST
,
157 IMX8MM_GPR_PCIE_CMN_RST
);
159 /* Polling to check the phy is ready or not. */
160 ret
= readl_poll_timeout(imx8_phy
->base
+ IMX8MM_PCIE_PHY_CMN_REG075
,
161 val
, val
== ANA_PLL_DONE
, 10, 20000);
165 static int imx8_pcie_phy_init(struct phy
*phy
)
167 struct imx8_pcie_phy
*imx8_phy
= phy_get_drvdata(phy
);
169 return clk_prepare_enable(imx8_phy
->clk
);
172 static int imx8_pcie_phy_exit(struct phy
*phy
)
174 struct imx8_pcie_phy
*imx8_phy
= phy_get_drvdata(phy
);
176 clk_disable_unprepare(imx8_phy
->clk
);
181 static const struct phy_ops imx8_pcie_phy_ops
= {
182 .init
= imx8_pcie_phy_init
,
183 .exit
= imx8_pcie_phy_exit
,
184 .power_on
= imx8_pcie_phy_power_on
,
185 .owner
= THIS_MODULE
,
188 static const struct imx8_pcie_phy_drvdata imx8mm_drvdata
= {
189 .gpr
= "fsl,imx8mm-iomuxc-gpr",
193 static const struct imx8_pcie_phy_drvdata imx8mp_drvdata
= {
194 .gpr
= "fsl,imx8mp-iomuxc-gpr",
198 static const struct of_device_id imx8_pcie_phy_of_match
[] = {
199 {.compatible
= "fsl,imx8mm-pcie-phy", .data
= &imx8mm_drvdata
, },
200 {.compatible
= "fsl,imx8mp-pcie-phy", .data
= &imx8mp_drvdata
, },
203 MODULE_DEVICE_TABLE(of
, imx8_pcie_phy_of_match
);
205 static int imx8_pcie_phy_probe(struct platform_device
*pdev
)
207 struct phy_provider
*phy_provider
;
208 struct device
*dev
= &pdev
->dev
;
209 struct device_node
*np
= dev
->of_node
;
210 struct imx8_pcie_phy
*imx8_phy
;
212 imx8_phy
= devm_kzalloc(dev
, sizeof(*imx8_phy
), GFP_KERNEL
);
216 imx8_phy
->drvdata
= of_device_get_match_data(dev
);
218 /* get PHY refclk pad mode */
219 of_property_read_u32(np
, "fsl,refclk-pad-mode",
220 &imx8_phy
->refclk_pad_mode
);
222 if (of_property_read_u32(np
, "fsl,tx-deemph-gen1",
223 &imx8_phy
->tx_deemph_gen1
))
224 imx8_phy
->tx_deemph_gen1
= 0;
226 if (of_property_read_u32(np
, "fsl,tx-deemph-gen2",
227 &imx8_phy
->tx_deemph_gen2
))
228 imx8_phy
->tx_deemph_gen2
= 0;
230 if (of_property_read_bool(np
, "fsl,clkreq-unsupported"))
231 imx8_phy
->clkreq_unused
= true;
233 imx8_phy
->clkreq_unused
= false;
235 imx8_phy
->clk
= devm_clk_get(dev
, "ref");
236 if (IS_ERR(imx8_phy
->clk
)) {
237 dev_err(dev
, "failed to get imx pcie phy clock\n");
238 return PTR_ERR(imx8_phy
->clk
);
241 /* Grab GPR config register range */
242 imx8_phy
->iomuxc_gpr
=
243 syscon_regmap_lookup_by_compatible(imx8_phy
->drvdata
->gpr
);
244 if (IS_ERR(imx8_phy
->iomuxc_gpr
)) {
245 dev_err(dev
, "unable to find iomuxc registers\n");
246 return PTR_ERR(imx8_phy
->iomuxc_gpr
);
249 imx8_phy
->reset
= devm_reset_control_get_exclusive(dev
, "pciephy");
250 if (IS_ERR(imx8_phy
->reset
)) {
251 dev_err(dev
, "Failed to get PCIEPHY reset control\n");
252 return PTR_ERR(imx8_phy
->reset
);
255 if (imx8_phy
->drvdata
->variant
== IMX8MP
) {
257 devm_reset_control_get_exclusive(dev
, "perst");
258 if (IS_ERR(imx8_phy
->perst
))
259 return dev_err_probe(dev
, PTR_ERR(imx8_phy
->perst
),
260 "Failed to get PCIE PHY PERST control\n");
263 imx8_phy
->base
= devm_platform_ioremap_resource(pdev
, 0);
264 if (IS_ERR(imx8_phy
->base
))
265 return PTR_ERR(imx8_phy
->base
);
267 imx8_phy
->phy
= devm_phy_create(dev
, NULL
, &imx8_pcie_phy_ops
);
268 if (IS_ERR(imx8_phy
->phy
))
269 return PTR_ERR(imx8_phy
->phy
);
271 phy_set_drvdata(imx8_phy
->phy
, imx8_phy
);
273 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
275 return PTR_ERR_OR_ZERO(phy_provider
);
278 static struct platform_driver imx8_pcie_phy_driver
= {
279 .probe
= imx8_pcie_phy_probe
,
281 .name
= "imx8-pcie-phy",
282 .of_match_table
= imx8_pcie_phy_of_match
,
285 module_platform_driver(imx8_pcie_phy_driver
);
287 MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver");
288 MODULE_LICENSE("GPL v2");