Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / phy / phy-imx8mq-usb.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021 NXP
4  *
5  */
6
7 #include <common.h>
8 #include <asm/io.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <generic-phy.h>
12 #include <linux/bitfield.h>
13 #include <linux/bitops.h>
14 #include <linux/delay.h>
15 #include <linux/err.h>
16 #include <clk.h>
17 #include <dm/device_compat.h>
18 #include <power/regulator.h>
19
20 #define PHY_CTRL0                       0x0
21 #define PHY_CTRL0_REF_SSP_EN            BIT(2)
22 #define PHY_CTRL0_FSEL_MASK             GENMASK(10, 5)
23 #define PHY_CTRL0_FSEL_24M              0x2a
24 #define PHY_CTRL0_FSEL_100M             0x27
25 #define PHY_CTRL0_SSC_RANGE_MASK        GENMASK(23, 21)
26 #define PHY_CTRL0_SSC_RANGE_4003PPM     (0x2 << 21)
27
28 #define PHY_CTRL1                       0x4
29 #define PHY_CTRL1_RESET                 BIT(0)
30 #define PHY_CTRL1_COMMONONN             BIT(1)
31 #define PHY_CTRL1_ATERESET              BIT(3)
32 #define PHY_CTRL1_DCDENB                BIT(17)
33 #define PHY_CTRL1_CHRGSEL               BIT(18)
34 #define PHY_CTRL1_VDATSRCENB0           BIT(19)
35 #define PHY_CTRL1_VDATDETENB0           BIT(20)
36
37 #define PHY_CTRL2                       0x8
38 #define PHY_CTRL2_TXENABLEN0            BIT(8)
39 #define PHY_CTRL2_OTG_DISABLE           BIT(9)
40
41 #define PHY_CTRL3                       0xc
42 #define PHY_CTRL3_COMPDISTUNE_MASK      GENMASK(2, 0)
43 #define PHY_CTRL3_TXPREEMP_TUNE_MASK    GENMASK(16, 15)
44 #define PHY_CTRL3_TXPREEMP_TUNE_SHIFT   15
45 #define PHY_CTRL3_TXRISE_TUNE_MASK      GENMASK(21, 20)
46 #define PHY_CTRL3_TXRISE_TUNE_SHIFT     20
47 /* 1111: +24% ... 0000: -6% step: 2% */
48 #define PHY_CTRL3_TXVREF_TUNE_MASK      GENMASK(25, 22)
49 #define PHY_CTRL3_TXVREF_TUNE_SHIFT     22
50 #define PHY_CTRL3_TX_VBOOST_LEVEL_MASK  GENMASK(31, 29)
51 #define PHY_CTRL3_TX_VBOOST_LEVEL_SHIFT 29
52
53 #define PHY_CTRL4                       0x10
54 #define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK      GENMASK(20, 15)
55 #define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_SHIFT     15
56
57 #define PHY_CTRL5                       0x14
58 #define PHY_CTRL5_DMPWD_OVERRIDE_SEL    BIT(23)
59 #define PHY_CTRL5_DMPWD_OVERRIDE        BIT(22)
60 #define PHY_CTRL5_DPPWD_OVERRIDE_SEL    BIT(21)
61 #define PHY_CTRL5_DPPWD_OVERRIDE        BIT(20)
62 #define PHY_CTRL5_PCS_TX_SWING_FULL_MASK        GENMASK(6, 0)
63
64 #define PHY_CTRL6                       0x18
65 #define PHY_CTRL6_RXTERM_OVERRIDE_SEL   BIT(29)
66 #define PHY_CTRL6_ALT_CLK_EN            BIT(1)
67 #define PHY_CTRL6_ALT_CLK_SEL           BIT(0)
68
69 #define PHY_STS0                        0x40
70 #define PHY_STS0_OTGSESSVLD             BIT(7)
71 #define PHY_STS0_CHGDET                 BIT(4)
72 #define PHY_STS0_FSVPLUS                BIT(3)
73 #define PHY_STS0_FSVMINUS               BIT(2)
74
75 enum imx8mpq_phy_type {
76         IMX8MQ_PHY,
77         IMX8MP_PHY,
78 };
79
80 struct imx8mq_usb_phy {
81         struct clk phy_clk;
82         void __iomem *base;
83         enum imx8mpq_phy_type type;
84         struct udevice *vbus_supply;
85 };
86
87 static const struct udevice_id imx8mq_usb_phy_of_match[] = {
88         { .compatible = "fsl,imx8mq-usb-phy", .data = IMX8MQ_PHY },
89         { .compatible = "fsl,imx8mp-usb-phy", .data = IMX8MP_PHY },
90         {},
91 };
92
93 static int imx8mq_usb_phy_init(struct phy *usb_phy)
94 {
95         struct udevice *dev = usb_phy->dev;
96         struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev);
97         u32 value;
98
99         value = readl(imx_phy->base + PHY_CTRL1);
100         value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
101                    PHY_CTRL1_COMMONONN);
102         value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
103         writel(value, imx_phy->base + PHY_CTRL1);
104
105         value = readl(imx_phy->base + PHY_CTRL0);
106         value |= PHY_CTRL0_REF_SSP_EN;
107         value &= ~PHY_CTRL0_SSC_RANGE_MASK;
108         value |= PHY_CTRL0_SSC_RANGE_4003PPM;
109         writel(value, imx_phy->base + PHY_CTRL0);
110
111         value = readl(imx_phy->base + PHY_CTRL2);
112         value |= PHY_CTRL2_TXENABLEN0;
113         writel(value, imx_phy->base + PHY_CTRL2);
114
115         value = readl(imx_phy->base + PHY_CTRL1);
116         value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
117         writel(value, imx_phy->base + PHY_CTRL1);
118
119         return 0;
120 }
121
122 static int imx8mp_usb_phy_init(struct phy *usb_phy)
123 {
124         struct udevice *dev = usb_phy->dev;
125         struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev);
126         u32 value;
127
128         /* USB3.0 PHY signal fsel for 24M ref */
129         value = readl(imx_phy->base + PHY_CTRL0);
130         value &= ~PHY_CTRL0_FSEL_MASK;
131         value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
132         writel(value, imx_phy->base + PHY_CTRL0);
133
134         /* Disable alt_clk_en and use internal MPLL clocks */
135         value = readl(imx_phy->base + PHY_CTRL6);
136         value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);
137         writel(value, imx_phy->base + PHY_CTRL6);
138
139         value = readl(imx_phy->base + PHY_CTRL1);
140         value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);
141         value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
142         writel(value, imx_phy->base + PHY_CTRL1);
143
144         value = readl(imx_phy->base + PHY_CTRL0);
145         value |= PHY_CTRL0_REF_SSP_EN;
146         writel(value, imx_phy->base + PHY_CTRL0);
147
148         value = readl(imx_phy->base + PHY_CTRL2);
149         value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;
150         writel(value, imx_phy->base + PHY_CTRL2);
151
152         udelay(10);
153
154         value = readl(imx_phy->base + PHY_CTRL1);
155         value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
156         writel(value, imx_phy->base + PHY_CTRL1);
157
158         return 0;
159 }
160
161 static int imx8mpq_usb_phy_init(struct phy *usb_phy)
162 {
163         struct udevice *dev = usb_phy->dev;
164         struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev);
165
166         if (imx_phy->type == IMX8MP_PHY)
167                 return imx8mp_usb_phy_init(usb_phy);
168         else
169                 return imx8mq_usb_phy_init(usb_phy);
170 }
171
172 static int imx8mq_usb_phy_power_on(struct phy *usb_phy)
173 {
174         struct udevice *dev = usb_phy->dev;
175         struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev);
176         u32 value;
177         int ret;
178
179         if (CONFIG_IS_ENABLED(CLK)) {
180                 ret = clk_enable(&imx_phy->phy_clk);
181                 if (ret) {
182                         dev_err(dev, "Failed to enable usb phy clock: %d\n", ret);
183                         return ret;
184                 }
185         }
186
187         if (CONFIG_IS_ENABLED(DM_REGULATOR) && imx_phy->vbus_supply) {
188                 ret = regulator_set_enable_if_allowed(imx_phy->vbus_supply, true);
189                 if (ret && ret != -ENOSYS) {
190                         dev_err(dev, "Failed to enable VBUS regulator: %d\n", ret);
191                         goto err;
192                 }
193         }
194
195         /* Disable rx term override */
196         value = readl(imx_phy->base + PHY_CTRL6);
197         value &= ~PHY_CTRL6_RXTERM_OVERRIDE_SEL;
198         writel(value, imx_phy->base + PHY_CTRL6);
199
200         return 0;
201
202 err:
203         if (CONFIG_IS_ENABLED(CLK))
204                 clk_disable(&imx_phy->phy_clk);
205         return ret;
206 }
207
208 static int imx8mq_usb_phy_power_off(struct phy *usb_phy)
209 {
210         struct udevice *dev = usb_phy->dev;
211         struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev);
212         u32 value;
213         int ret;
214
215         /* Override rx term to be 0 */
216         value = readl(imx_phy->base + PHY_CTRL6);
217         value |= PHY_CTRL6_RXTERM_OVERRIDE_SEL;
218         writel(value, imx_phy->base + PHY_CTRL6);
219
220         if (CONFIG_IS_ENABLED(CLK))
221                 clk_disable(&imx_phy->phy_clk);
222
223         if (CONFIG_IS_ENABLED(DM_REGULATOR) && imx_phy->vbus_supply) {
224                 ret = regulator_set_enable_if_allowed(imx_phy->vbus_supply, false);
225                 if (ret && ret != -ENOSYS) {
226                         dev_err(dev, "Failed to disable VBUS regulator: %d\n", ret);
227                         return ret;
228                 }
229         }
230
231         return 0;
232 }
233
234 static int imx8mq_usb_phy_exit(struct phy *usb_phy)
235 {
236         return imx8mq_usb_phy_power_off(usb_phy);
237 }
238
239 struct phy_ops imx8mq_usb_phy_ops = {
240         .init = imx8mpq_usb_phy_init,
241         .power_on = imx8mq_usb_phy_power_on,
242         .power_off = imx8mq_usb_phy_power_off,
243         .exit = imx8mq_usb_phy_exit,
244 };
245
246 int imx8mq_usb_phy_probe(struct udevice *dev)
247 {
248         struct imx8mq_usb_phy *priv = dev_get_priv(dev);
249         int ret;
250
251         priv->type = dev_get_driver_data(dev);
252         priv->base = dev_read_addr_ptr(dev);
253
254         if (!priv->base)
255                 return -EINVAL;
256
257         if (CONFIG_IS_ENABLED(CLK)) {
258                 ret = clk_get_by_name(dev, "phy", &priv->phy_clk);
259                 if (ret) {
260                         dev_err(dev, "Failed to get usb phy clock %d\n", ret);
261                         return ret;
262                 }
263         }
264
265         if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
266                 ret = device_get_supply_regulator(dev, "vbus-supply",
267                                                   &priv->vbus_supply);
268                 if (ret && ret != -ENOENT) {
269                         dev_err(dev, "Failed to get VBUS regulator: %d\n", ret);
270                         return ret;
271                 }
272         }
273
274         return 0;
275 }
276
277 U_BOOT_DRIVER(nxp_imx8mq_usb_phy) = {
278         .name = "nxp_imx8mq_usb_phy",
279         .id = UCLASS_PHY,
280         .of_match = imx8mq_usb_phy_of_match,
281         .probe = imx8mq_usb_phy_probe,
282         .ops = &imx8mq_usb_phy_ops,
283         .priv_auto      = sizeof(struct imx8mq_usb_phy),
284 };