GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / phy / freescale / phy-fsl-imx8m-pcie.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021 NXP
4  */
5
6 #include <linux/bitfield.h>
7 #include <linux/clk.h>
8 #include <linux/delay.h>
9 #include <linux/io.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>
14 #include <linux/of.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/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 enum imx8_pcie_phy_type {
50         IMX8MM,
51         IMX8MP,
52 };
53
54 struct imx8_pcie_phy_drvdata {
55         const   char                    *gpr;
56         enum    imx8_pcie_phy_type      variant;
57 };
58
59 struct imx8_pcie_phy {
60         void __iomem            *base;
61         struct clk              *clk;
62         struct phy              *phy;
63         struct regmap           *iomuxc_gpr;
64         struct reset_control    *perst;
65         struct reset_control    *reset;
66         u32                     refclk_pad_mode;
67         u32                     tx_deemph_gen1;
68         u32                     tx_deemph_gen2;
69         bool                    clkreq_unused;
70         const struct imx8_pcie_phy_drvdata      *drvdata;
71 };
72
73 static int imx8_pcie_phy_power_on(struct phy *phy)
74 {
75         int ret;
76         u32 val, pad_mode;
77         struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
78
79         pad_mode = imx8_phy->refclk_pad_mode;
80         switch (imx8_phy->drvdata->variant) {
81         case IMX8MM:
82                 reset_control_assert(imx8_phy->reset);
83
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);
91                 break;
92         case IMX8MP: /* Do nothing. */
93                 break;
94         }
95
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);
102         } else {
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);
106         }
107
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);
116                 }
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);
122         }
123
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);
136
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);
143
144         /* Do the PHY common block reset */
145         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
146                            IMX8MM_GPR_PCIE_CMN_RST,
147                            IMX8MM_GPR_PCIE_CMN_RST);
148
149         switch (imx8_phy->drvdata->variant) {
150         case IMX8MP:
151                 reset_control_deassert(imx8_phy->perst);
152                 fallthrough;
153         case IMX8MM:
154                 reset_control_deassert(imx8_phy->reset);
155                 usleep_range(200, 500);
156                 break;
157         }
158
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);
162         return ret;
163 }
164
165 static int imx8_pcie_phy_init(struct phy *phy)
166 {
167         struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
168
169         return clk_prepare_enable(imx8_phy->clk);
170 }
171
172 static int imx8_pcie_phy_exit(struct phy *phy)
173 {
174         struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
175
176         clk_disable_unprepare(imx8_phy->clk);
177
178         return 0;
179 }
180
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,
186 };
187
188 static const struct imx8_pcie_phy_drvdata imx8mm_drvdata = {
189         .gpr = "fsl,imx8mm-iomuxc-gpr",
190         .variant = IMX8MM,
191 };
192
193 static const struct imx8_pcie_phy_drvdata imx8mp_drvdata = {
194         .gpr = "fsl,imx8mp-iomuxc-gpr",
195         .variant = IMX8MP,
196 };
197
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, },
201         { },
202 };
203 MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match);
204
205 static int imx8_pcie_phy_probe(struct platform_device *pdev)
206 {
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;
211
212         imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL);
213         if (!imx8_phy)
214                 return -ENOMEM;
215
216         imx8_phy->drvdata = of_device_get_match_data(dev);
217
218         /* get PHY refclk pad mode */
219         of_property_read_u32(np, "fsl,refclk-pad-mode",
220                              &imx8_phy->refclk_pad_mode);
221
222         if (of_property_read_u32(np, "fsl,tx-deemph-gen1",
223                                  &imx8_phy->tx_deemph_gen1))
224                 imx8_phy->tx_deemph_gen1 = 0;
225
226         if (of_property_read_u32(np, "fsl,tx-deemph-gen2",
227                                  &imx8_phy->tx_deemph_gen2))
228                 imx8_phy->tx_deemph_gen2 = 0;
229
230         if (of_property_read_bool(np, "fsl,clkreq-unsupported"))
231                 imx8_phy->clkreq_unused = true;
232         else
233                 imx8_phy->clkreq_unused = false;
234
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);
239         }
240
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);
247         }
248
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);
253         }
254
255         if (imx8_phy->drvdata->variant == IMX8MP) {
256                 imx8_phy->perst =
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");
261         }
262
263         imx8_phy->base = devm_platform_ioremap_resource(pdev, 0);
264         if (IS_ERR(imx8_phy->base))
265                 return PTR_ERR(imx8_phy->base);
266
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);
270
271         phy_set_drvdata(imx8_phy->phy, imx8_phy);
272
273         phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
274
275         return PTR_ERR_OR_ZERO(phy_provider);
276 }
277
278 static struct platform_driver imx8_pcie_phy_driver = {
279         .probe  = imx8_pcie_phy_probe,
280         .driver = {
281                 .name   = "imx8-pcie-phy",
282                 .of_match_table = imx8_pcie_phy_of_match,
283         }
284 };
285 module_platform_driver(imx8_pcie_phy_driver);
286
287 MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver");
288 MODULE_LICENSE("GPL v2");