Skip to content

Commit 63c85ad

Browse files
Junlisuzhouvinodkoul
authored andcommitted
phy: fsl-imx8mp-usb: add support for phy tuning
Add USB PHY parameter tuning for USB certifications. Reviewed-by: Haibo Chen <haibo.chen@nxp.com> Signed-off-by: Li Jun <jun.li@nxp.com> [j.zink: ported to v6.3-rc1 from NXP downstream repo + cleanups] Signed-off-by: Johannes Zink <j.zink@pengutronix.de> Link: https://lore.kernel.org/r/20230516-lustige-usb-phy-dinge-v2-2-3383a0de34ac@pengutronix.de Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent b2e7556 commit 63c85ad

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

drivers/phy/freescale/phy-fsl-imx8mq-usb.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,231 @@
2727
#define PHY_CTRL2_TXENABLEN0 BIT(8)
2828
#define PHY_CTRL2_OTG_DISABLE BIT(9)
2929

30+
#define PHY_CTRL3 0xc
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)
36+
37+
#define PHY_CTRL4 0x10
38+
#define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(20, 15)
39+
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)
46+
3047
#define PHY_CTRL6 0x18
3148
#define PHY_CTRL6_ALT_CLK_EN BIT(1)
3249
#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
3350

51+
#define PHY_TUNE_DEFAULT 0xffffffff
52+
3453
struct imx8mq_usb_phy {
3554
struct phy *phy;
3655
struct clk *clk;
3756
void __iomem *base;
3857
struct regulator *vbus;
58+
u32 pcs_tx_swing_full;
59+
u32 pcs_tx_deemph_3p5db;
60+
u32 tx_vref_tune;
61+
u32 tx_rise_tune;
62+
u32 tx_preemp_amp_tune;
63+
u32 tx_vboost_level;
64+
u32 comp_dis_tune;
3965
};
4066

67+
static u32 phy_tx_vref_tune_from_property(u32 percent)
68+
{
69+
percent = clamp(percent, 94U, 124U);
70+
71+
return DIV_ROUND_CLOSEST(percent - 94U, 2);
72+
}
73+
74+
static u32 phy_tx_rise_tune_from_property(u32 percent)
75+
{
76+
switch (percent) {
77+
case 0 ... 98:
78+
return 3;
79+
case 99:
80+
return 2;
81+
case 100 ... 101:
82+
return 1;
83+
default:
84+
return 0;
85+
}
86+
}
87+
88+
static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp)
89+
{
90+
microamp = min(microamp, 1800U);
91+
92+
return microamp / 600;
93+
}
94+
95+
static u32 phy_tx_vboost_level_from_property(u32 microvolt)
96+
{
97+
switch (microvolt) {
98+
case 0 ... 960:
99+
return 0;
100+
case 961 ... 1160:
101+
return 2;
102+
default:
103+
return 3;
104+
}
105+
}
106+
107+
static u32 phy_pcs_tx_deemph_3p5db_from_property(u32 decibel)
108+
{
109+
return min(decibel, 36U);
110+
}
111+
112+
static u32 phy_comp_dis_tune_from_property(u32 percent)
113+
{
114+
switch (percent) {
115+
case 0 ... 92:
116+
return 0;
117+
case 93 ... 95:
118+
return 1;
119+
case 96 ... 97:
120+
return 2;
121+
case 98 ... 102:
122+
return 3;
123+
case 103 ... 105:
124+
return 4;
125+
case 106 ... 109:
126+
return 5;
127+
case 110 ... 113:
128+
return 6;
129+
default:
130+
return 7;
131+
}
132+
}
133+
static u32 phy_pcs_tx_swing_full_from_property(u32 percent)
134+
{
135+
percent = min(percent, 100U);
136+
137+
return (percent * 127) / 100;
138+
}
139+
140+
static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)
141+
{
142+
struct device *dev = imx_phy->phy->dev.parent;
143+
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;
147+
else
148+
imx_phy->tx_vref_tune =
149+
phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune);
150+
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;
154+
else
155+
imx_phy->tx_rise_tune =
156+
phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune);
157+
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;
161+
else
162+
imx_phy->tx_preemp_amp_tune =
163+
phy_tx_preemp_amp_tune_from_property(imx_phy->tx_preemp_amp_tune);
164+
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;
168+
else
169+
imx_phy->tx_vboost_level =
170+
phy_tx_vboost_level_from_property(imx_phy->tx_vboost_level);
171+
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;
175+
else
176+
imx_phy->comp_dis_tune =
177+
phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune);
178+
179+
if (device_property_read_u32(dev, "fsl,pcs-tx-deemph-3p5db-attenuation-db",
180+
&imx_phy->pcs_tx_deemph_3p5db))
181+
imx_phy->pcs_tx_deemph_3p5db = PHY_TUNE_DEFAULT;
182+
else
183+
imx_phy->pcs_tx_deemph_3p5db =
184+
phy_pcs_tx_deemph_3p5db_from_property(imx_phy->pcs_tx_deemph_3p5db);
185+
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;
189+
else
190+
imx_phy->pcs_tx_swing_full =
191+
phy_pcs_tx_swing_full_from_property(imx_phy->pcs_tx_swing_full);
192+
}
193+
194+
static void imx8m_phy_tune(struct imx8mq_usb_phy *imx_phy)
195+
{
196+
u32 value;
197+
198+
/* PHY tuning */
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);
205+
}
206+
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);
212+
}
213+
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. */
218+
return;
219+
220+
value = readl(imx_phy->base + PHY_CTRL3);
221+
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);
226+
}
227+
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);
232+
}
233+
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);
238+
}
239+
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);
244+
}
245+
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);
250+
}
251+
252+
writel(value, imx_phy->base + PHY_CTRL3);
253+
}
254+
41255
static int imx8mq_usb_phy_init(struct phy *phy)
42256
{
43257
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
@@ -99,6 +313,8 @@ static int imx8mp_usb_phy_init(struct phy *phy)
99313
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
100314
writel(value, imx_phy->base + PHY_CTRL1);
101315

316+
imx8m_phy_tune(imx_phy);
317+
102318
return 0;
103319
}
104320

@@ -182,6 +398,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
182398

183399
phy_set_drvdata(imx_phy->phy, imx_phy);
184400

401+
imx8m_get_phy_tuning_data(imx_phy);
402+
185403
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
186404

187405
return PTR_ERR_OR_ZERO(phy_provider);

0 commit comments

Comments
 (0)