1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Ingenic SoCs USB PHY driver 4 * Copyright (c) Paul Cercueil <paul@crapouillou.net> 5 * Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com> 6 * Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> 7 */ 8 9 #include <linux/bitfield.h> 10 #include <linux/clk.h> 11 #include <linux/delay.h> 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/phy/phy.h> 15 #include <linux/platform_device.h> 16 #include <linux/regulator/consumer.h> 17 18 /* OTGPHY register offsets */ 19 #define REG_USBPCR_OFFSET 0x00 20 #define REG_USBRDT_OFFSET 0x04 21 #define REG_USBVBFIL_OFFSET 0x08 22 #define REG_USBPCR1_OFFSET 0x0c 23 24 /* bits within the USBPCR register */ 25 #define USBPCR_USB_MODE BIT(31) 26 #define USBPCR_AVLD_REG BIT(30) 27 #define USBPCR_COMMONONN BIT(25) 28 #define USBPCR_VBUSVLDEXT BIT(24) 29 #define USBPCR_VBUSVLDEXTSEL BIT(23) 30 #define USBPCR_POR BIT(22) 31 #define USBPCR_SIDDQ BIT(21) 32 #define USBPCR_OTG_DISABLE BIT(20) 33 #define USBPCR_TXPREEMPHTUNE BIT(6) 34 35 #define USBPCR_IDPULLUP_MASK GENMASK(29, 28) 36 #define USBPCR_IDPULLUP_ALWAYS 0x2 37 #define USBPCR_IDPULLUP_SUSPEND 0x1 38 #define USBPCR_IDPULLUP_OTG 0x0 39 40 #define USBPCR_COMPDISTUNE_MASK GENMASK(19, 17) 41 #define USBPCR_COMPDISTUNE_DFT 0x4 42 43 #define USBPCR_OTGTUNE_MASK GENMASK(16, 14) 44 #define USBPCR_OTGTUNE_DFT 0x4 45 46 #define USBPCR_SQRXTUNE_MASK GENMASK(13, 11) 47 #define USBPCR_SQRXTUNE_DCR_20PCT 0x7 48 #define USBPCR_SQRXTUNE_DFT 0x3 49 50 #define USBPCR_TXFSLSTUNE_MASK GENMASK(10, 7) 51 #define USBPCR_TXFSLSTUNE_DCR_50PPT 0xf 52 #define USBPCR_TXFSLSTUNE_DCR_25PPT 0x7 53 #define USBPCR_TXFSLSTUNE_DFT 0x3 54 #define USBPCR_TXFSLSTUNE_INC_25PPT 0x1 55 #define USBPCR_TXFSLSTUNE_INC_50PPT 0x0 56 57 #define USBPCR_TXHSXVTUNE_MASK GENMASK(5, 4) 58 #define USBPCR_TXHSXVTUNE_DFT 0x3 59 #define USBPCR_TXHSXVTUNE_DCR_15MV 0x1 60 61 #define USBPCR_TXRISETUNE_MASK GENMASK(5, 4) 62 #define USBPCR_TXRISETUNE_DFT 0x3 63 64 #define USBPCR_TXVREFTUNE_MASK GENMASK(3, 0) 65 #define USBPCR_TXVREFTUNE_INC_75PPT 0xb 66 #define USBPCR_TXVREFTUNE_INC_25PPT 0x7 67 #define USBPCR_TXVREFTUNE_DFT 0x5 68 69 /* bits within the USBRDTR register */ 70 #define USBRDT_UTMI_RST BIT(27) 71 #define USBRDT_HB_MASK BIT(26) 72 #define USBRDT_VBFIL_LD_EN BIT(25) 73 #define USBRDT_IDDIG_EN BIT(24) 74 #define USBRDT_IDDIG_REG BIT(23) 75 #define USBRDT_VBFIL_EN BIT(2) 76 77 /* bits within the USBPCR1 register */ 78 #define USBPCR1_BVLD_REG BIT(31) 79 #define USBPCR1_DPPD BIT(29) 80 #define USBPCR1_DMPD BIT(28) 81 #define USBPCR1_USB_SEL BIT(28) 82 #define USBPCR1_PORT_RST BIT(21) 83 #define USBPCR1_WORD_IF_16BIT BIT(19) 84 85 enum ingenic_usb_phy_version { 86 ID_JZ4770, 87 ID_JZ4775, 88 ID_JZ4780, 89 ID_X1000, 90 ID_X1830, 91 ID_X2000, 92 }; 93 94 struct ingenic_soc_info { 95 enum ingenic_usb_phy_version version; 96 97 void (*usb_phy_init)(struct phy *phy); 98 }; 99 100 struct ingenic_usb_phy { 101 const struct ingenic_soc_info *soc_info; 102 103 struct phy *phy; 104 void __iomem *base; 105 struct clk *clk; 106 struct regulator *vcc_supply; 107 }; 108 109 static int ingenic_usb_phy_init(struct phy *phy) 110 { 111 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 112 int err; 113 u32 reg; 114 115 err = clk_prepare_enable(priv->clk); 116 if (err) { 117 dev_err(&phy->dev, "Unable to start clock: %d\n", err); 118 return err; 119 } 120 121 priv->soc_info->usb_phy_init(phy); 122 123 /* Wait for PHY to reset */ 124 usleep_range(30, 300); 125 reg = readl(priv->base + REG_USBPCR_OFFSET); 126 writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET); 127 usleep_range(300, 1000); 128 129 return 0; 130 } 131 132 static int ingenic_usb_phy_exit(struct phy *phy) 133 { 134 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 135 136 clk_disable_unprepare(priv->clk); 137 regulator_disable(priv->vcc_supply); 138 139 return 0; 140 } 141 142 static int ingenic_usb_phy_power_on(struct phy *phy) 143 { 144 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 145 int err; 146 147 err = regulator_enable(priv->vcc_supply); 148 if (err) { 149 dev_err(&phy->dev, "Unable to enable VCC: %d\n", err); 150 return err; 151 } 152 153 return 0; 154 } 155 156 static int ingenic_usb_phy_power_off(struct phy *phy) 157 { 158 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 159 160 regulator_disable(priv->vcc_supply); 161 162 return 0; 163 } 164 165 static int ingenic_usb_phy_set_mode(struct phy *phy, 166 enum phy_mode mode, int submode) 167 { 168 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 169 u32 reg; 170 171 switch (mode) { 172 case PHY_MODE_USB_HOST: 173 reg = readl(priv->base + REG_USBPCR_OFFSET); 174 u32p_replace_bits(®, 1, USBPCR_USB_MODE); 175 u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXT); 176 u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXTSEL); 177 u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); 178 writel(reg, priv->base + REG_USBPCR_OFFSET); 179 180 break; 181 case PHY_MODE_USB_DEVICE: 182 reg = readl(priv->base + REG_USBPCR_OFFSET); 183 u32p_replace_bits(®, 0, USBPCR_USB_MODE); 184 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); 185 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); 186 u32p_replace_bits(®, 1, USBPCR_OTG_DISABLE); 187 writel(reg, priv->base + REG_USBPCR_OFFSET); 188 189 break; 190 case PHY_MODE_USB_OTG: 191 reg = readl(priv->base + REG_USBPCR_OFFSET); 192 u32p_replace_bits(®, 1, USBPCR_USB_MODE); 193 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); 194 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); 195 u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); 196 writel(reg, priv->base + REG_USBPCR_OFFSET); 197 198 break; 199 default: 200 return -EINVAL; 201 } 202 203 return 0; 204 } 205 206 static const struct phy_ops ingenic_usb_phy_ops = { 207 .init = ingenic_usb_phy_init, 208 .exit = ingenic_usb_phy_exit, 209 .power_on = ingenic_usb_phy_power_on, 210 .power_off = ingenic_usb_phy_power_off, 211 .set_mode = ingenic_usb_phy_set_mode, 212 .owner = THIS_MODULE, 213 }; 214 215 static void jz4770_usb_phy_init(struct phy *phy) 216 { 217 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 218 u32 reg; 219 220 reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_POR | 221 FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_ALWAYS) | 222 FIELD_PREP(USBPCR_COMPDISTUNE_MASK, USBPCR_COMPDISTUNE_DFT) | 223 FIELD_PREP(USBPCR_OTGTUNE_MASK, USBPCR_OTGTUNE_DFT) | 224 FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DFT) | 225 FIELD_PREP(USBPCR_TXFSLSTUNE_MASK, USBPCR_TXFSLSTUNE_DFT) | 226 FIELD_PREP(USBPCR_TXRISETUNE_MASK, USBPCR_TXRISETUNE_DFT) | 227 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_DFT); 228 writel(reg, priv->base + REG_USBPCR_OFFSET); 229 } 230 231 static void jz4775_usb_phy_init(struct phy *phy) 232 { 233 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 234 u32 reg; 235 236 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | 237 USBPCR1_WORD_IF_16BIT; 238 writel(reg, priv->base + REG_USBPCR1_OFFSET); 239 240 reg = USBPCR_COMMONONN | USBPCR_POR | 241 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_75PPT); 242 writel(reg, priv->base + REG_USBPCR_OFFSET); 243 } 244 245 static void jz4780_usb_phy_init(struct phy *phy) 246 { 247 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 248 u32 reg; 249 250 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | 251 USBPCR1_WORD_IF_16BIT; 252 writel(reg, priv->base + REG_USBPCR1_OFFSET); 253 254 reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR; 255 writel(reg, priv->base + REG_USBPCR_OFFSET); 256 } 257 258 static void x1000_usb_phy_init(struct phy *phy) 259 { 260 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 261 u32 reg; 262 263 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT; 264 writel(reg, priv->base + REG_USBPCR1_OFFSET); 265 266 reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | 267 FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DCR_20PCT) | 268 FIELD_PREP(USBPCR_TXHSXVTUNE_MASK, USBPCR_TXHSXVTUNE_DCR_15MV) | 269 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_25PPT); 270 writel(reg, priv->base + REG_USBPCR_OFFSET); 271 } 272 273 static void x1830_usb_phy_init(struct phy *phy) 274 { 275 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 276 u32 reg; 277 278 /* rdt */ 279 writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET); 280 281 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT | 282 USBPCR1_DMPD | USBPCR1_DPPD; 283 writel(reg, priv->base + REG_USBPCR1_OFFSET); 284 285 reg = USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | 286 FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); 287 writel(reg, priv->base + REG_USBPCR_OFFSET); 288 } 289 290 static void x2000_usb_phy_init(struct phy *phy) 291 { 292 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 293 u32 reg; 294 295 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_DPPD | USBPCR1_DMPD; 296 writel(reg & ~USBPCR1_PORT_RST, priv->base + REG_USBPCR1_OFFSET); 297 298 reg = USBPCR_POR | FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); 299 writel(reg, priv->base + REG_USBPCR_OFFSET); 300 } 301 302 static const struct ingenic_soc_info jz4770_soc_info = { 303 .version = ID_JZ4770, 304 305 .usb_phy_init = jz4770_usb_phy_init, 306 }; 307 308 static const struct ingenic_soc_info jz4775_soc_info = { 309 .version = ID_JZ4775, 310 311 .usb_phy_init = jz4775_usb_phy_init, 312 }; 313 314 static const struct ingenic_soc_info jz4780_soc_info = { 315 .version = ID_JZ4780, 316 317 .usb_phy_init = jz4780_usb_phy_init, 318 }; 319 320 static const struct ingenic_soc_info x1000_soc_info = { 321 .version = ID_X1000, 322 323 .usb_phy_init = x1000_usb_phy_init, 324 }; 325 326 static const struct ingenic_soc_info x1830_soc_info = { 327 .version = ID_X1830, 328 329 .usb_phy_init = x1830_usb_phy_init, 330 }; 331 332 static const struct ingenic_soc_info x2000_soc_info = { 333 .version = ID_X2000, 334 335 .usb_phy_init = x2000_usb_phy_init, 336 }; 337 338 static int ingenic_usb_phy_probe(struct platform_device *pdev) 339 { 340 struct ingenic_usb_phy *priv; 341 struct phy_provider *provider; 342 struct device *dev = &pdev->dev; 343 int err; 344 345 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 346 if (!priv) 347 return -ENOMEM; 348 349 priv->soc_info = device_get_match_data(dev); 350 if (!priv->soc_info) { 351 dev_err(dev, "Error: No device match found\n"); 352 return -ENODEV; 353 } 354 355 priv->base = devm_platform_ioremap_resource(pdev, 0); 356 if (IS_ERR(priv->base)) { 357 dev_err(dev, "Failed to map registers\n"); 358 return PTR_ERR(priv->base); 359 } 360 361 priv->clk = devm_clk_get(dev, NULL); 362 if (IS_ERR(priv->clk)) { 363 err = PTR_ERR(priv->clk); 364 if (err != -EPROBE_DEFER) 365 dev_err(dev, "Failed to get clock\n"); 366 return err; 367 } 368 369 priv->vcc_supply = devm_regulator_get(dev, "vcc"); 370 if (IS_ERR(priv->vcc_supply)) { 371 err = PTR_ERR(priv->vcc_supply); 372 if (err != -EPROBE_DEFER) 373 dev_err(dev, "Failed to get regulator\n"); 374 return err; 375 } 376 377 priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops); 378 if (IS_ERR(priv)) 379 return PTR_ERR(priv); 380 381 phy_set_drvdata(priv->phy, priv); 382 383 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 384 385 return PTR_ERR_OR_ZERO(provider); 386 } 387 388 static const struct of_device_id ingenic_usb_phy_of_matches[] = { 389 { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info }, 390 { .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info }, 391 { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info }, 392 { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info }, 393 { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info }, 394 { .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info }, 395 { /* sentinel */ } 396 }; 397 MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches); 398 399 static struct platform_driver ingenic_usb_phy_driver = { 400 .probe = ingenic_usb_phy_probe, 401 .driver = { 402 .name = "ingenic-usb-phy", 403 .of_match_table = ingenic_usb_phy_of_matches, 404 }, 405 }; 406 module_platform_driver(ingenic_usb_phy_driver); 407 408 MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>"); 409 MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>"); 410 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 411 MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver"); 412 MODULE_LICENSE("GPL"); 413