phy-ingenic-usb.c (11096B)
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 85struct ingenic_soc_info { 86 void (*usb_phy_init)(struct phy *phy); 87}; 88 89struct ingenic_usb_phy { 90 const struct ingenic_soc_info *soc_info; 91 92 struct phy *phy; 93 void __iomem *base; 94 struct clk *clk; 95 struct regulator *vcc_supply; 96}; 97 98static int ingenic_usb_phy_init(struct phy *phy) 99{ 100 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 101 int err; 102 u32 reg; 103 104 err = clk_prepare_enable(priv->clk); 105 if (err) { 106 dev_err(&phy->dev, "Unable to start clock: %d\n", err); 107 return err; 108 } 109 110 priv->soc_info->usb_phy_init(phy); 111 112 /* Wait for PHY to reset */ 113 usleep_range(30, 300); 114 reg = readl(priv->base + REG_USBPCR_OFFSET); 115 writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET); 116 usleep_range(300, 1000); 117 118 return 0; 119} 120 121static int ingenic_usb_phy_exit(struct phy *phy) 122{ 123 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 124 125 clk_disable_unprepare(priv->clk); 126 regulator_disable(priv->vcc_supply); 127 128 return 0; 129} 130 131static int ingenic_usb_phy_power_on(struct phy *phy) 132{ 133 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 134 int err; 135 136 err = regulator_enable(priv->vcc_supply); 137 if (err) { 138 dev_err(&phy->dev, "Unable to enable VCC: %d\n", err); 139 return err; 140 } 141 142 return 0; 143} 144 145static int ingenic_usb_phy_power_off(struct phy *phy) 146{ 147 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 148 149 regulator_disable(priv->vcc_supply); 150 151 return 0; 152} 153 154static int ingenic_usb_phy_set_mode(struct phy *phy, 155 enum phy_mode mode, int submode) 156{ 157 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 158 u32 reg; 159 160 switch (mode) { 161 case PHY_MODE_USB_HOST: 162 reg = readl(priv->base + REG_USBPCR_OFFSET); 163 u32p_replace_bits(®, 1, USBPCR_USB_MODE); 164 u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXT); 165 u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXTSEL); 166 u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); 167 writel(reg, priv->base + REG_USBPCR_OFFSET); 168 169 break; 170 case PHY_MODE_USB_DEVICE: 171 reg = readl(priv->base + REG_USBPCR_OFFSET); 172 u32p_replace_bits(®, 0, USBPCR_USB_MODE); 173 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); 174 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); 175 u32p_replace_bits(®, 1, USBPCR_OTG_DISABLE); 176 writel(reg, priv->base + REG_USBPCR_OFFSET); 177 178 break; 179 case PHY_MODE_USB_OTG: 180 reg = readl(priv->base + REG_USBPCR_OFFSET); 181 u32p_replace_bits(®, 1, USBPCR_USB_MODE); 182 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); 183 u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); 184 u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); 185 writel(reg, priv->base + REG_USBPCR_OFFSET); 186 187 break; 188 default: 189 return -EINVAL; 190 } 191 192 return 0; 193} 194 195static const struct phy_ops ingenic_usb_phy_ops = { 196 .init = ingenic_usb_phy_init, 197 .exit = ingenic_usb_phy_exit, 198 .power_on = ingenic_usb_phy_power_on, 199 .power_off = ingenic_usb_phy_power_off, 200 .set_mode = ingenic_usb_phy_set_mode, 201 .owner = THIS_MODULE, 202}; 203 204static void jz4770_usb_phy_init(struct phy *phy) 205{ 206 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 207 u32 reg; 208 209 reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_POR | 210 FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_ALWAYS) | 211 FIELD_PREP(USBPCR_COMPDISTUNE_MASK, USBPCR_COMPDISTUNE_DFT) | 212 FIELD_PREP(USBPCR_OTGTUNE_MASK, USBPCR_OTGTUNE_DFT) | 213 FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DFT) | 214 FIELD_PREP(USBPCR_TXFSLSTUNE_MASK, USBPCR_TXFSLSTUNE_DFT) | 215 FIELD_PREP(USBPCR_TXRISETUNE_MASK, USBPCR_TXRISETUNE_DFT) | 216 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_DFT); 217 writel(reg, priv->base + REG_USBPCR_OFFSET); 218} 219 220static void jz4775_usb_phy_init(struct phy *phy) 221{ 222 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 223 u32 reg; 224 225 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | 226 USBPCR1_WORD_IF_16BIT; 227 writel(reg, priv->base + REG_USBPCR1_OFFSET); 228 229 reg = USBPCR_COMMONONN | USBPCR_POR | 230 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_75PPT); 231 writel(reg, priv->base + REG_USBPCR_OFFSET); 232} 233 234static void jz4780_usb_phy_init(struct phy *phy) 235{ 236 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 237 u32 reg; 238 239 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | 240 USBPCR1_WORD_IF_16BIT; 241 writel(reg, priv->base + REG_USBPCR1_OFFSET); 242 243 reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR; 244 writel(reg, priv->base + REG_USBPCR_OFFSET); 245} 246 247static void x1000_usb_phy_init(struct phy *phy) 248{ 249 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 250 u32 reg; 251 252 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT; 253 writel(reg, priv->base + REG_USBPCR1_OFFSET); 254 255 reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | 256 FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DCR_20PCT) | 257 FIELD_PREP(USBPCR_TXHSXVTUNE_MASK, USBPCR_TXHSXVTUNE_DCR_15MV) | 258 FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_25PPT); 259 writel(reg, priv->base + REG_USBPCR_OFFSET); 260} 261 262static void x1830_usb_phy_init(struct phy *phy) 263{ 264 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 265 u32 reg; 266 267 /* rdt */ 268 writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET); 269 270 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT | 271 USBPCR1_DMPD | USBPCR1_DPPD; 272 writel(reg, priv->base + REG_USBPCR1_OFFSET); 273 274 reg = USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | 275 FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); 276 writel(reg, priv->base + REG_USBPCR_OFFSET); 277} 278 279static void x2000_usb_phy_init(struct phy *phy) 280{ 281 struct ingenic_usb_phy *priv = phy_get_drvdata(phy); 282 u32 reg; 283 284 reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_DPPD | USBPCR1_DMPD; 285 writel(reg & ~USBPCR1_PORT_RST, priv->base + REG_USBPCR1_OFFSET); 286 287 reg = USBPCR_POR | FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); 288 writel(reg, priv->base + REG_USBPCR_OFFSET); 289} 290 291static const struct ingenic_soc_info jz4770_soc_info = { 292 .usb_phy_init = jz4770_usb_phy_init, 293}; 294 295static const struct ingenic_soc_info jz4775_soc_info = { 296 .usb_phy_init = jz4775_usb_phy_init, 297}; 298 299static const struct ingenic_soc_info jz4780_soc_info = { 300 .usb_phy_init = jz4780_usb_phy_init, 301}; 302 303static const struct ingenic_soc_info x1000_soc_info = { 304 .usb_phy_init = x1000_usb_phy_init, 305}; 306 307static const struct ingenic_soc_info x1830_soc_info = { 308 .usb_phy_init = x1830_usb_phy_init, 309}; 310 311static const struct ingenic_soc_info x2000_soc_info = { 312 .usb_phy_init = x2000_usb_phy_init, 313}; 314 315static int ingenic_usb_phy_probe(struct platform_device *pdev) 316{ 317 struct ingenic_usb_phy *priv; 318 struct phy_provider *provider; 319 struct device *dev = &pdev->dev; 320 int err; 321 322 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 323 if (!priv) 324 return -ENOMEM; 325 326 priv->soc_info = device_get_match_data(dev); 327 if (!priv->soc_info) { 328 dev_err(dev, "Error: No device match found\n"); 329 return -ENODEV; 330 } 331 332 priv->base = devm_platform_ioremap_resource(pdev, 0); 333 if (IS_ERR(priv->base)) { 334 dev_err(dev, "Failed to map registers\n"); 335 return PTR_ERR(priv->base); 336 } 337 338 priv->clk = devm_clk_get(dev, NULL); 339 if (IS_ERR(priv->clk)) { 340 err = PTR_ERR(priv->clk); 341 if (err != -EPROBE_DEFER) 342 dev_err(dev, "Failed to get clock\n"); 343 return err; 344 } 345 346 priv->vcc_supply = devm_regulator_get(dev, "vcc"); 347 if (IS_ERR(priv->vcc_supply)) { 348 err = PTR_ERR(priv->vcc_supply); 349 if (err != -EPROBE_DEFER) 350 dev_err(dev, "Failed to get regulator\n"); 351 return err; 352 } 353 354 priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops); 355 if (IS_ERR(priv->phy)) 356 return PTR_ERR(priv->phy); 357 358 phy_set_drvdata(priv->phy, priv); 359 360 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 361 362 return PTR_ERR_OR_ZERO(provider); 363} 364 365static const struct of_device_id ingenic_usb_phy_of_matches[] = { 366 { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info }, 367 { .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info }, 368 { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info }, 369 { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info }, 370 { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info }, 371 { .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info }, 372 { /* sentinel */ } 373}; 374MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches); 375 376static struct platform_driver ingenic_usb_phy_driver = { 377 .probe = ingenic_usb_phy_probe, 378 .driver = { 379 .name = "ingenic-usb-phy", 380 .of_match_table = ingenic_usb_phy_of_matches, 381 }, 382}; 383module_platform_driver(ingenic_usb_phy_driver); 384 385MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>"); 386MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>"); 387MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 388MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver"); 389MODULE_LICENSE("GPL");