Merge tag 'regulator-fix-v6.9-merge-window' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.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                 writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
114                        imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
115                 val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM;
116                 writel(val | ANA_AUX_RX_TERM_GND_EN,
117                        imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064);
118                 writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL,
119                        imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065);
120         }
121
122         /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
123         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
124                            IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
125                            imx8_phy->clkreq_unused ?
126                            0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
127         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
128                            IMX8MM_GPR_PCIE_AUX_EN,
129                            IMX8MM_GPR_PCIE_AUX_EN);
130         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
131                            IMX8MM_GPR_PCIE_POWER_OFF, 0);
132         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
133                            IMX8MM_GPR_PCIE_SSC_EN, 0);
134
135         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
136                            IMX8MM_GPR_PCIE_REF_CLK_SEL,
137                            pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
138                            IMX8MM_GPR_PCIE_REF_CLK_EXT :
139                            IMX8MM_GPR_PCIE_REF_CLK_PLL);
140         usleep_range(100, 200);
141
142         /* Do the PHY common block reset */
143         regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
144                            IMX8MM_GPR_PCIE_CMN_RST,
145                            IMX8MM_GPR_PCIE_CMN_RST);
146
147         switch (imx8_phy->drvdata->variant) {
148         case IMX8MP:
149                 reset_control_deassert(imx8_phy->perst);
150                 fallthrough;
151         case IMX8MM:
152                 reset_control_deassert(imx8_phy->reset);
153                 usleep_range(200, 500);
154                 break;
155         }
156
157         /* Polling to check the phy is ready or not. */
158         ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075,
159                                  val, val == ANA_PLL_DONE, 10, 20000);
160         return ret;
161 }
162
163 static int imx8_pcie_phy_init(struct phy *phy)
164 {
165         struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
166
167         return clk_prepare_enable(imx8_phy->clk);
168 }
169
170 static int imx8_pcie_phy_exit(struct phy *phy)
171 {
172         struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
173
174         clk_disable_unprepare(imx8_phy->clk);
175
176         return 0;
177 }
178
179 static const struct phy_ops imx8_pcie_phy_ops = {
180         .init           = imx8_pcie_phy_init,
181         .exit           = imx8_pcie_phy_exit,
182         .power_on       = imx8_pcie_phy_power_on,
183         .owner          = THIS_MODULE,
184 };
185
186 static const struct imx8_pcie_phy_drvdata imx8mm_drvdata = {
187         .gpr = "fsl,imx8mm-iomuxc-gpr",
188         .variant = IMX8MM,
189 };
190
191 static const struct imx8_pcie_phy_drvdata imx8mp_drvdata = {
192         .gpr = "fsl,imx8mp-iomuxc-gpr",
193         .variant = IMX8MP,
194 };
195
196 static const struct of_device_id imx8_pcie_phy_of_match[] = {
197         {.compatible = "fsl,imx8mm-pcie-phy", .data = &imx8mm_drvdata, },
198         {.compatible = "fsl,imx8mp-pcie-phy", .data = &imx8mp_drvdata, },
199         { },
200 };
201 MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match);
202
203 static int imx8_pcie_phy_probe(struct platform_device *pdev)
204 {
205         struct phy_provider *phy_provider;
206         struct device *dev = &pdev->dev;
207         struct device_node *np = dev->of_node;
208         struct imx8_pcie_phy *imx8_phy;
209
210         imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL);
211         if (!imx8_phy)
212                 return -ENOMEM;
213
214         imx8_phy->drvdata = of_device_get_match_data(dev);
215
216         /* get PHY refclk pad mode */
217         of_property_read_u32(np, "fsl,refclk-pad-mode",
218                              &imx8_phy->refclk_pad_mode);
219
220         if (of_property_read_u32(np, "fsl,tx-deemph-gen1",
221                                  &imx8_phy->tx_deemph_gen1))
222                 imx8_phy->tx_deemph_gen1 = 0;
223
224         if (of_property_read_u32(np, "fsl,tx-deemph-gen2",
225                                  &imx8_phy->tx_deemph_gen2))
226                 imx8_phy->tx_deemph_gen2 = 0;
227
228         if (of_property_read_bool(np, "fsl,clkreq-unsupported"))
229                 imx8_phy->clkreq_unused = true;
230         else
231                 imx8_phy->clkreq_unused = false;
232
233         imx8_phy->clk = devm_clk_get(dev, "ref");
234         if (IS_ERR(imx8_phy->clk)) {
235                 dev_err(dev, "failed to get imx pcie phy clock\n");
236                 return PTR_ERR(imx8_phy->clk);
237         }
238
239         /* Grab GPR config register range */
240         imx8_phy->iomuxc_gpr =
241                  syscon_regmap_lookup_by_compatible(imx8_phy->drvdata->gpr);
242         if (IS_ERR(imx8_phy->iomuxc_gpr)) {
243                 dev_err(dev, "unable to find iomuxc registers\n");
244                 return PTR_ERR(imx8_phy->iomuxc_gpr);
245         }
246
247         imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy");
248         if (IS_ERR(imx8_phy->reset)) {
249                 dev_err(dev, "Failed to get PCIEPHY reset control\n");
250                 return PTR_ERR(imx8_phy->reset);
251         }
252
253         if (imx8_phy->drvdata->variant == IMX8MP) {
254                 imx8_phy->perst =
255                         devm_reset_control_get_exclusive(dev, "perst");
256                 if (IS_ERR(imx8_phy->perst))
257                         return dev_err_probe(dev, PTR_ERR(imx8_phy->perst),
258                                       "Failed to get PCIE PHY PERST control\n");
259         }
260
261         imx8_phy->base = devm_platform_ioremap_resource(pdev, 0);
262         if (IS_ERR(imx8_phy->base))
263                 return PTR_ERR(imx8_phy->base);
264
265         imx8_phy->phy = devm_phy_create(dev, NULL, &imx8_pcie_phy_ops);
266         if (IS_ERR(imx8_phy->phy))
267                 return PTR_ERR(imx8_phy->phy);
268
269         phy_set_drvdata(imx8_phy->phy, imx8_phy);
270
271         phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
272
273         return PTR_ERR_OR_ZERO(phy_provider);
274 }
275
276 static struct platform_driver imx8_pcie_phy_driver = {
277         .probe  = imx8_pcie_phy_probe,
278         .driver = {
279                 .name   = "imx8-pcie-phy",
280                 .of_match_table = imx8_pcie_phy_of_match,
281         }
282 };
283 module_platform_driver(imx8_pcie_phy_driver);
284
285 MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver");
286 MODULE_LICENSE("GPL v2");