1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (c) 2017 NXP. */
4 #include <linux/bitfield.h>
6 #include <linux/delay.h>
8 #include <linux/module.h>
10 #include <linux/phy/phy.h>
11 #include <linux/platform_device.h>
12 #include <linux/regulator/consumer.h>
15 #define PHY_CTRL0_REF_SSP_EN BIT(2)
16 #define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
17 #define PHY_CTRL0_FSEL_24M 0x2a
20 #define PHY_CTRL1_RESET BIT(0)
21 #define PHY_CTRL1_COMMONONN BIT(1)
22 #define PHY_CTRL1_ATERESET BIT(3)
23 #define PHY_CTRL1_VDATSRCENB0 BIT(19)
24 #define PHY_CTRL1_VDATDETENB0 BIT(20)
27 #define PHY_CTRL2_TXENABLEN0 BIT(8)
28 #define PHY_CTRL2_OTG_DISABLE BIT(9)
31 #define PHY_CTRL3_COMPDISTUNE_MASK GENMASK(2, 0)
32 #define PHY_CTRL3_TXPREEMP_TUNE_MASK GENMASK(16, 15)
33 #define PHY_CTRL3_TXRISE_TUNE_MASK GENMASK(21, 20)
34 #define PHY_CTRL3_TXVREF_TUNE_MASK GENMASK(25, 22)
35 #define PHY_CTRL3_TX_VBOOST_LEVEL_MASK GENMASK(31, 29)
37 #define PHY_CTRL4 0x10
38 #define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(20, 15)
40 #define PHY_CTRL5 0x14
41 #define PHY_CTRL5_DMPWD_OVERRIDE_SEL BIT(23)
42 #define PHY_CTRL5_DMPWD_OVERRIDE BIT(22)
43 #define PHY_CTRL5_DPPWD_OVERRIDE_SEL BIT(21)
44 #define PHY_CTRL5_DPPWD_OVERRIDE BIT(20)
45 #define PHY_CTRL5_PCS_TX_SWING_FULL_MASK GENMASK(6, 0)
47 #define PHY_CTRL6 0x18
48 #define PHY_CTRL6_ALT_CLK_EN BIT(1)
49 #define PHY_CTRL6_ALT_CLK_SEL BIT(0)
51 #define PHY_TUNE_DEFAULT 0xffffffff
53 struct imx8mq_usb_phy
{
57 struct regulator
*vbus
;
58 u32 pcs_tx_swing_full
;
59 u32 pcs_tx_deemph_3p5db
;
62 u32 tx_preemp_amp_tune
;
67 static u32
phy_tx_vref_tune_from_property(u32 percent
)
69 percent
= clamp(percent
, 94U, 124U);
71 return DIV_ROUND_CLOSEST(percent
- 94U, 2);
74 static u32
phy_tx_rise_tune_from_property(u32 percent
)
88 static u32
phy_tx_preemp_amp_tune_from_property(u32 microamp
)
90 microamp
= min(microamp
, 1800U);
92 return microamp
/ 600;
95 static u32
phy_tx_vboost_level_from_property(u32 microvolt
)
107 static u32
phy_pcs_tx_deemph_3p5db_from_property(u32 decibel
)
109 return min(decibel
, 36U);
112 static u32
phy_comp_dis_tune_from_property(u32 percent
)
133 static u32
phy_pcs_tx_swing_full_from_property(u32 percent
)
135 percent
= min(percent
, 100U);
137 return (percent
* 127) / 100;
140 static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy
*imx_phy
)
142 struct device
*dev
= imx_phy
->phy
->dev
.parent
;
144 if (device_property_read_u32(dev
, "fsl,phy-tx-vref-tune-percent",
145 &imx_phy
->tx_vref_tune
))
146 imx_phy
->tx_vref_tune
= PHY_TUNE_DEFAULT
;
148 imx_phy
->tx_vref_tune
=
149 phy_tx_vref_tune_from_property(imx_phy
->tx_vref_tune
);
151 if (device_property_read_u32(dev
, "fsl,phy-tx-rise-tune-percent",
152 &imx_phy
->tx_rise_tune
))
153 imx_phy
->tx_rise_tune
= PHY_TUNE_DEFAULT
;
155 imx_phy
->tx_rise_tune
=
156 phy_tx_rise_tune_from_property(imx_phy
->tx_rise_tune
);
158 if (device_property_read_u32(dev
, "fsl,phy-tx-preemp-amp-tune-microamp",
159 &imx_phy
->tx_preemp_amp_tune
))
160 imx_phy
->tx_preemp_amp_tune
= PHY_TUNE_DEFAULT
;
162 imx_phy
->tx_preemp_amp_tune
=
163 phy_tx_preemp_amp_tune_from_property(imx_phy
->tx_preemp_amp_tune
);
165 if (device_property_read_u32(dev
, "fsl,phy-tx-vboost-level-microvolt",
166 &imx_phy
->tx_vboost_level
))
167 imx_phy
->tx_vboost_level
= PHY_TUNE_DEFAULT
;
169 imx_phy
->tx_vboost_level
=
170 phy_tx_vboost_level_from_property(imx_phy
->tx_vboost_level
);
172 if (device_property_read_u32(dev
, "fsl,phy-comp-dis-tune-percent",
173 &imx_phy
->comp_dis_tune
))
174 imx_phy
->comp_dis_tune
= PHY_TUNE_DEFAULT
;
176 imx_phy
->comp_dis_tune
=
177 phy_comp_dis_tune_from_property(imx_phy
->comp_dis_tune
);
179 if (device_property_read_u32(dev
, "fsl,phy-pcs-tx-deemph-3p5db-attenuation-db",
180 &imx_phy
->pcs_tx_deemph_3p5db
))
181 imx_phy
->pcs_tx_deemph_3p5db
= PHY_TUNE_DEFAULT
;
183 imx_phy
->pcs_tx_deemph_3p5db
=
184 phy_pcs_tx_deemph_3p5db_from_property(imx_phy
->pcs_tx_deemph_3p5db
);
186 if (device_property_read_u32(dev
, "fsl,phy-pcs-tx-swing-full-percent",
187 &imx_phy
->pcs_tx_swing_full
))
188 imx_phy
->pcs_tx_swing_full
= PHY_TUNE_DEFAULT
;
190 imx_phy
->pcs_tx_swing_full
=
191 phy_pcs_tx_swing_full_from_property(imx_phy
->pcs_tx_swing_full
);
194 static void imx8m_phy_tune(struct imx8mq_usb_phy
*imx_phy
)
199 if (imx_phy
->pcs_tx_deemph_3p5db
!= PHY_TUNE_DEFAULT
) {
200 value
= readl(imx_phy
->base
+ PHY_CTRL4
);
201 value
&= ~PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK
;
202 value
|= FIELD_PREP(PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK
,
203 imx_phy
->pcs_tx_deemph_3p5db
);
204 writel(value
, imx_phy
->base
+ PHY_CTRL4
);
207 if (imx_phy
->pcs_tx_swing_full
!= PHY_TUNE_DEFAULT
) {
208 value
= readl(imx_phy
->base
+ PHY_CTRL5
);
209 value
|= FIELD_PREP(PHY_CTRL5_PCS_TX_SWING_FULL_MASK
,
210 imx_phy
->pcs_tx_swing_full
);
211 writel(value
, imx_phy
->base
+ PHY_CTRL5
);
214 if ((imx_phy
->tx_vref_tune
& imx_phy
->tx_rise_tune
&
215 imx_phy
->tx_preemp_amp_tune
& imx_phy
->comp_dis_tune
&
216 imx_phy
->tx_vboost_level
) == PHY_TUNE_DEFAULT
)
217 /* If all are the default values, no need update. */
220 value
= readl(imx_phy
->base
+ PHY_CTRL3
);
222 if (imx_phy
->tx_vref_tune
!= PHY_TUNE_DEFAULT
) {
223 value
&= ~PHY_CTRL3_TXVREF_TUNE_MASK
;
224 value
|= FIELD_PREP(PHY_CTRL3_TXVREF_TUNE_MASK
,
225 imx_phy
->tx_vref_tune
);
228 if (imx_phy
->tx_rise_tune
!= PHY_TUNE_DEFAULT
) {
229 value
&= ~PHY_CTRL3_TXRISE_TUNE_MASK
;
230 value
|= FIELD_PREP(PHY_CTRL3_TXRISE_TUNE_MASK
,
231 imx_phy
->tx_rise_tune
);
234 if (imx_phy
->tx_preemp_amp_tune
!= PHY_TUNE_DEFAULT
) {
235 value
&= ~PHY_CTRL3_TXPREEMP_TUNE_MASK
;
236 value
|= FIELD_PREP(PHY_CTRL3_TXPREEMP_TUNE_MASK
,
237 imx_phy
->tx_preemp_amp_tune
);
240 if (imx_phy
->comp_dis_tune
!= PHY_TUNE_DEFAULT
) {
241 value
&= ~PHY_CTRL3_COMPDISTUNE_MASK
;
242 value
|= FIELD_PREP(PHY_CTRL3_COMPDISTUNE_MASK
,
243 imx_phy
->comp_dis_tune
);
246 if (imx_phy
->tx_vboost_level
!= PHY_TUNE_DEFAULT
) {
247 value
&= ~PHY_CTRL3_TX_VBOOST_LEVEL_MASK
;
248 value
|= FIELD_PREP(PHY_CTRL3_TX_VBOOST_LEVEL_MASK
,
249 imx_phy
->tx_vboost_level
);
252 writel(value
, imx_phy
->base
+ PHY_CTRL3
);
255 static int imx8mq_usb_phy_init(struct phy
*phy
)
257 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
260 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
261 value
&= ~(PHY_CTRL1_VDATSRCENB0
| PHY_CTRL1_VDATDETENB0
|
262 PHY_CTRL1_COMMONONN
);
263 value
|= PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
;
264 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
266 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
267 value
|= PHY_CTRL0_REF_SSP_EN
;
268 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
270 value
= readl(imx_phy
->base
+ PHY_CTRL2
);
271 value
|= PHY_CTRL2_TXENABLEN0
;
272 writel(value
, imx_phy
->base
+ PHY_CTRL2
);
274 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
275 value
&= ~(PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
);
276 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
281 static int imx8mp_usb_phy_init(struct phy
*phy
)
283 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
286 /* USB3.0 PHY signal fsel for 24M ref */
287 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
288 value
&= ~PHY_CTRL0_FSEL_MASK
;
289 value
|= FIELD_PREP(PHY_CTRL0_FSEL_MASK
, PHY_CTRL0_FSEL_24M
);
290 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
292 /* Disable alt_clk_en and use internal MPLL clocks */
293 value
= readl(imx_phy
->base
+ PHY_CTRL6
);
294 value
&= ~(PHY_CTRL6_ALT_CLK_SEL
| PHY_CTRL6_ALT_CLK_EN
);
295 writel(value
, imx_phy
->base
+ PHY_CTRL6
);
297 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
298 value
&= ~(PHY_CTRL1_VDATSRCENB0
| PHY_CTRL1_VDATDETENB0
);
299 value
|= PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
;
300 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
302 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
303 value
|= PHY_CTRL0_REF_SSP_EN
;
304 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
306 value
= readl(imx_phy
->base
+ PHY_CTRL2
);
307 value
|= PHY_CTRL2_TXENABLEN0
| PHY_CTRL2_OTG_DISABLE
;
308 writel(value
, imx_phy
->base
+ PHY_CTRL2
);
312 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
313 value
&= ~(PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
);
314 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
316 imx8m_phy_tune(imx_phy
);
321 static int imx8mq_phy_power_on(struct phy
*phy
)
323 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
326 ret
= regulator_enable(imx_phy
->vbus
);
330 return clk_prepare_enable(imx_phy
->clk
);
333 static int imx8mq_phy_power_off(struct phy
*phy
)
335 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
337 clk_disable_unprepare(imx_phy
->clk
);
338 regulator_disable(imx_phy
->vbus
);
343 static const struct phy_ops imx8mq_usb_phy_ops
= {
344 .init
= imx8mq_usb_phy_init
,
345 .power_on
= imx8mq_phy_power_on
,
346 .power_off
= imx8mq_phy_power_off
,
347 .owner
= THIS_MODULE
,
350 static const struct phy_ops imx8mp_usb_phy_ops
= {
351 .init
= imx8mp_usb_phy_init
,
352 .power_on
= imx8mq_phy_power_on
,
353 .power_off
= imx8mq_phy_power_off
,
354 .owner
= THIS_MODULE
,
357 static const struct of_device_id imx8mq_usb_phy_of_match
[] = {
358 {.compatible
= "fsl,imx8mq-usb-phy",
359 .data
= &imx8mq_usb_phy_ops
,},
360 {.compatible
= "fsl,imx8mp-usb-phy",
361 .data
= &imx8mp_usb_phy_ops
,},
364 MODULE_DEVICE_TABLE(of
, imx8mq_usb_phy_of_match
);
366 static int imx8mq_usb_phy_probe(struct platform_device
*pdev
)
368 struct phy_provider
*phy_provider
;
369 struct device
*dev
= &pdev
->dev
;
370 struct imx8mq_usb_phy
*imx_phy
;
371 const struct phy_ops
*phy_ops
;
373 imx_phy
= devm_kzalloc(dev
, sizeof(*imx_phy
), GFP_KERNEL
);
377 imx_phy
->clk
= devm_clk_get(dev
, "phy");
378 if (IS_ERR(imx_phy
->clk
)) {
379 dev_err(dev
, "failed to get imx8mq usb phy clock\n");
380 return PTR_ERR(imx_phy
->clk
);
383 imx_phy
->base
= devm_platform_ioremap_resource(pdev
, 0);
384 if (IS_ERR(imx_phy
->base
))
385 return PTR_ERR(imx_phy
->base
);
387 phy_ops
= of_device_get_match_data(dev
);
391 imx_phy
->phy
= devm_phy_create(dev
, NULL
, phy_ops
);
392 if (IS_ERR(imx_phy
->phy
))
393 return PTR_ERR(imx_phy
->phy
);
395 imx_phy
->vbus
= devm_regulator_get(dev
, "vbus");
396 if (IS_ERR(imx_phy
->vbus
))
397 return dev_err_probe(dev
, PTR_ERR(imx_phy
->vbus
), "failed to get vbus\n");
399 phy_set_drvdata(imx_phy
->phy
, imx_phy
);
401 imx8m_get_phy_tuning_data(imx_phy
);
403 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
405 return PTR_ERR_OR_ZERO(phy_provider
);
408 static struct platform_driver imx8mq_usb_phy_driver
= {
409 .probe
= imx8mq_usb_phy_probe
,
411 .name
= "imx8mq-usb-phy",
412 .of_match_table
= imx8mq_usb_phy_of_match
,
415 module_platform_driver(imx8mq_usb_phy_driver
);
417 MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");
418 MODULE_LICENSE("GPL");