|
27 | 27 | #define PHY_CTRL2_TXENABLEN0 BIT(8) |
28 | 28 | #define PHY_CTRL2_OTG_DISABLE BIT(9) |
29 | 29 |
|
| 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 | + |
30 | 47 | #define PHY_CTRL6 0x18 |
31 | 48 | #define PHY_CTRL6_ALT_CLK_EN BIT(1) |
32 | 49 | #define PHY_CTRL6_ALT_CLK_SEL BIT(0) |
33 | 50 |
|
| 51 | +#define PHY_TUNE_DEFAULT 0xffffffff |
| 52 | + |
34 | 53 | struct imx8mq_usb_phy { |
35 | 54 | struct phy *phy; |
36 | 55 | struct clk *clk; |
37 | 56 | void __iomem *base; |
38 | 57 | 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; |
39 | 65 | }; |
40 | 66 |
|
| 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 | + |
41 | 255 | static int imx8mq_usb_phy_init(struct phy *phy) |
42 | 256 | { |
43 | 257 | struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); |
@@ -99,6 +313,8 @@ static int imx8mp_usb_phy_init(struct phy *phy) |
99 | 313 | value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); |
100 | 314 | writel(value, imx_phy->base + PHY_CTRL1); |
101 | 315 |
|
| 316 | + imx8m_phy_tune(imx_phy); |
| 317 | + |
102 | 318 | return 0; |
103 | 319 | } |
104 | 320 |
|
@@ -182,6 +398,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) |
182 | 398 |
|
183 | 399 | phy_set_drvdata(imx_phy->phy, imx_phy); |
184 | 400 |
|
| 401 | + imx8m_get_phy_tuning_data(imx_phy); |
| 402 | + |
185 | 403 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
186 | 404 |
|
187 | 405 | return PTR_ERR_OR_ZERO(phy_provider); |
|
0 commit comments