Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2021 NXP * */ #include <asm/io.h> #include <dm.h> #include <errno.h> #include <generic-phy.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/err.h> #include <clk.h> #include <dm/device_compat.h> #include <power/regulator.h> #define PHY_CTRL0 0x0 #define PHY_CTRL0_REF_SSP_EN BIT(2) #define PHY_CTRL0_FSEL_MASK GENMASK(10, 5) #define PHY_CTRL0_FSEL_24M 0x2a #define PHY_CTRL0_FSEL_100M 0x27 #define PHY_CTRL0_SSC_RANGE_MASK GENMASK(23, 21) #define PHY_CTRL0_SSC_RANGE_4003PPM (0x2 << 21) #define PHY_CTRL1 0x4 #define PHY_CTRL1_RESET BIT(0) #define PHY_CTRL1_COMMONONN BIT(1) #define PHY_CTRL1_ATERESET BIT(3) #define PHY_CTRL1_DCDENB BIT(17) #define PHY_CTRL1_CHRGSEL BIT(18) #define PHY_CTRL1_VDATSRCENB0 BIT(19) #define PHY_CTRL1_VDATDETENB0 BIT(20) #define PHY_CTRL2 0x8 #define PHY_CTRL2_TXENABLEN0 BIT(8) #define PHY_CTRL2_OTG_DISABLE BIT(9) #define PHY_CTRL3 0xc #define PHY_CTRL3_COMPDISTUNE_MASK GENMASK(2, 0) #define PHY_CTRL3_TXPREEMP_TUNE_MASK GENMASK(16, 15) #define PHY_CTRL3_TXPREEMP_TUNE_SHIFT 15 #define PHY_CTRL3_TXRISE_TUNE_MASK GENMASK(21, 20) #define PHY_CTRL3_TXRISE_TUNE_SHIFT 20 /* 1111: +24% ... 0000: -6% step: 2% */ #define PHY_CTRL3_TXVREF_TUNE_MASK GENMASK(25, 22) #define PHY_CTRL3_TXVREF_TUNE_SHIFT 22 #define PHY_CTRL3_TX_VBOOST_LEVEL_MASK GENMASK(31, 29) #define PHY_CTRL3_TX_VBOOST_LEVEL_SHIFT 29 #define PHY_CTRL4 0x10 #define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(20, 15) #define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_SHIFT 15 #define PHY_CTRL5 0x14 #define PHY_CTRL5_DMPWD_OVERRIDE_SEL BIT(23) #define PHY_CTRL5_DMPWD_OVERRIDE BIT(22) #define PHY_CTRL5_DPPWD_OVERRIDE_SEL BIT(21) #define PHY_CTRL5_DPPWD_OVERRIDE BIT(20) #define PHY_CTRL5_PCS_TX_SWING_FULL_MASK GENMASK(6, 0) #define PHY_CTRL6 0x18 #define PHY_CTRL6_RXTERM_OVERRIDE_SEL BIT(29) #define PHY_CTRL6_ALT_CLK_EN BIT(1) #define PHY_CTRL6_ALT_CLK_SEL BIT(0) #define PHY_STS0 0x40 #define PHY_STS0_OTGSESSVLD BIT(7) #define PHY_STS0_CHGDET BIT(4) #define PHY_STS0_FSVPLUS BIT(3) #define PHY_STS0_FSVMINUS BIT(2) #define TCA_CLK_RST 0x00 #define TCA_CLK_RST_SW BIT(9) #define TCA_CLK_RST_REF_CLK_EN BIT(1) #define TCA_CLK_RST_SUSPEND_CLK_EN BIT(0) #define TCA_INTR_EN 0x04 #define TCA_INTR_STS 0x08 #define TCA_GCFG 0x10 #define TCA_GCFG_ROLE_HSTDEV BIT(4) #define TCA_GCFG_OP_MODE GENMASK(1, 0) #define TCA_GCFG_OP_MODE_SYSMODE 0 #define TCA_GCFG_OP_MODE_SYNCMODE 1 #define TCA_TCPC 0x14 #define TCA_TCPC_VALID BIT(4) #define TCA_TCPC_LOW_POWER_EN BIT(3) #define TCA_TCPC_ORIENTATION_NORMAL BIT(2) #define TCA_TCPC_MUX_CONTRL GENMASK(1, 0) #define TCA_TCPC_MUX_CONTRL_NO_CONN 0 #define TCA_TCPC_MUX_CONTRL_USB_CONN 1 #define TCA_SYSMODE_CFG 0x18 #define TCA_SYSMODE_TCPC_DISABLE BIT(3) #define TCA_SYSMODE_TCPC_FLIP BIT(2) #define TCA_CTRLSYNCMODE_CFG0 0x20 #define TCA_CTRLSYNCMODE_CFG1 0x20 #define TCA_PSTATE 0x30 #define TCA_PSTATE_CM_STS BIT(4) #define TCA_PSTATE_TX_STS BIT(3) #define TCA_PSTATE_RX_PLL_STS BIT(2) #define TCA_PSTATE_PIPE0_POWER_DOWN GENMASK(1, 0) #define TCA_GEN_STATUS 0x34 #define TCA_GEN_DEV_POR BIT(12) #define TCA_GEN_REF_CLK_SEL BIT(8) #define TCA_GEN_TYPEC_FLIP_INVERT BIT(4) #define TCA_GEN_PHY_TYPEC_DISABLE BIT(3) #define TCA_GEN_PHY_TYPEC_FLIP BIT(2) #define TCA_VBUS_CTRL 0x40 #define TCA_VBUS_STATUS 0x44 #define TCA_INFO 0xfc enum imx8mpq_phy_type { IMX8MQ_PHY, IMX8MP_PHY, IMX95_PHY, }; struct imx8mq_usb_phy { struct clk phy_clk; void __iomem *base; enum imx8mpq_phy_type type; struct udevice *vbus_supply; void __iomem *tca_base; }; static const struct udevice_id imx8mq_usb_phy_of_match[] = { { .compatible = "fsl,imx8mq-usb-phy", .data = IMX8MQ_PHY }, { .compatible = "fsl,imx8mp-usb-phy", .data = IMX8MP_PHY }, { .compatible = "fsl,imx95-usb-phy", .data = IMX95_PHY }, {}, }; static void tca_blk_init(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); void __iomem *base = imx_phy->tca_base; u32 val; /* reset XBar block */ val = readl(base + TCA_CLK_RST); val &= ~TCA_CLK_RST_SW; writel(val, base + TCA_CLK_RST); udelay(100); /* clear reset */ val |= TCA_CLK_RST_SW; writel(val, base + TCA_CLK_RST); /* * use Controller Synced Mode for TCA low power enable and * put PHY to USB safe state. */ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYNCMODE); writel(val, base + TCA_GCFG); val = TCA_TCPC_VALID | TCA_TCPC_LOW_POWER_EN; writel(val, base + TCA_TCPC); /* use System Configuration Mode for TCA mux control. */ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYSMODE); writel(val, base + TCA_GCFG); } static int imx8mq_usb_phy_init(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); u32 value; value = readl(imx_phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | PHY_CTRL1_COMMONONN); value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; writel(value, imx_phy->base + PHY_CTRL1); value = readl(imx_phy->base + PHY_CTRL0); value |= PHY_CTRL0_REF_SSP_EN; value &= ~PHY_CTRL0_SSC_RANGE_MASK; value |= PHY_CTRL0_SSC_RANGE_4003PPM; writel(value, imx_phy->base + PHY_CTRL0); value = readl(imx_phy->base + PHY_CTRL2); value |= PHY_CTRL2_TXENABLEN0; writel(value, imx_phy->base + PHY_CTRL2); value = readl(imx_phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); writel(value, imx_phy->base + PHY_CTRL1); return 0; } static int imx8mp_usb_phy_init(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); u32 value; /* USB3.0 PHY signal fsel for 24M ref */ value = readl(imx_phy->base + PHY_CTRL0); value &= ~PHY_CTRL0_FSEL_MASK; value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M); writel(value, imx_phy->base + PHY_CTRL0); /* Disable alt_clk_en and use internal MPLL clocks */ value = readl(imx_phy->base + PHY_CTRL6); value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN); writel(value, imx_phy->base + PHY_CTRL6); value = readl(imx_phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; writel(value, imx_phy->base + PHY_CTRL1); value = readl(imx_phy->base + PHY_CTRL0); value |= PHY_CTRL0_REF_SSP_EN; writel(value, imx_phy->base + PHY_CTRL0); value = readl(imx_phy->base + PHY_CTRL2); value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE; writel(value, imx_phy->base + PHY_CTRL2); udelay(10); value = readl(imx_phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); writel(value, imx_phy->base + PHY_CTRL1); if (imx_phy->tca_base) tca_blk_init(usb_phy); return 0; } static int imx8mpq_usb_phy_init(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); if (imx_phy->type == IMX8MP_PHY || imx_phy->type == IMX95_PHY) return imx8mp_usb_phy_init(usb_phy); else return imx8mq_usb_phy_init(usb_phy); } static int imx8mq_usb_phy_power_on(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); u32 value; int ret; if (CONFIG_IS_ENABLED(CLK)) { ret = clk_enable(&imx_phy->phy_clk); if (ret) { dev_err(dev, "Failed to enable usb phy clock: %d\n", ret); return ret; } } if (CONFIG_IS_ENABLED(DM_REGULATOR) && imx_phy->vbus_supply) { ret = regulator_set_enable_if_allowed(imx_phy->vbus_supply, true); if (ret && ret != -ENOSYS) { dev_err(dev, "Failed to enable VBUS regulator: %d\n", ret); goto err; } } /* Disable rx term override */ value = readl(imx_phy->base + PHY_CTRL6); value &= ~PHY_CTRL6_RXTERM_OVERRIDE_SEL; writel(value, imx_phy->base + PHY_CTRL6); return 0; err: if (CONFIG_IS_ENABLED(CLK)) clk_disable(&imx_phy->phy_clk); return ret; } static int imx8mq_usb_phy_power_off(struct phy *usb_phy) { struct udevice *dev = usb_phy->dev; struct imx8mq_usb_phy *imx_phy = dev_get_priv(dev); u32 value; int ret; /* Override rx term to be 0 */ value = readl(imx_phy->base + PHY_CTRL6); value |= PHY_CTRL6_RXTERM_OVERRIDE_SEL; writel(value, imx_phy->base + PHY_CTRL6); if (CONFIG_IS_ENABLED(CLK)) clk_disable(&imx_phy->phy_clk); if (CONFIG_IS_ENABLED(DM_REGULATOR) && imx_phy->vbus_supply) { ret = regulator_set_enable_if_allowed(imx_phy->vbus_supply, false); if (ret && ret != -ENOSYS) { dev_err(dev, "Failed to disable VBUS regulator: %d\n", ret); return ret; } } return 0; } struct phy_ops imx8mq_usb_phy_ops = { .init = imx8mpq_usb_phy_init, .power_on = imx8mq_usb_phy_power_on, .power_off = imx8mq_usb_phy_power_off, }; int imx8mq_usb_phy_probe(struct udevice *dev) { struct imx8mq_usb_phy *priv = dev_get_priv(dev); int ret; priv->type = dev_get_driver_data(dev); priv->base = dev_read_addr_ptr(dev); if (!priv->base) return -EINVAL; if (CONFIG_IS_ENABLED(CLK)) { ret = clk_get_by_name(dev, "phy", &priv->phy_clk); if (ret) { dev_err(dev, "Failed to get usb phy clock %d\n", ret); return ret; } } if (CONFIG_IS_ENABLED(DM_REGULATOR)) { ret = device_get_supply_regulator(dev, "vbus-supply", &priv->vbus_supply); if (ret && ret != -ENOENT) { dev_err(dev, "Failed to get VBUS regulator: %d\n", ret); return ret; } } if (priv->type == IMX95_PHY) priv->tca_base = dev_read_addr_index_ptr(dev, 1); return 0; } U_BOOT_DRIVER(nxp_imx8mq_usb_phy) = { .name = "nxp_imx8mq_usb_phy", .id = UCLASS_PHY, .of_match = imx8mq_usb_phy_of_match, .probe = imx8mq_usb_phy_probe, .ops = &imx8mq_usb_phy_ops, .priv_auto = sizeof(struct imx8mq_usb_phy), }; |