1dc9cdf85SRamneek Mehresh /* 2dc9cdf85SRamneek Mehresh * Copyright 2015 Freescale Semiconductor, Inc. 3dc9cdf85SRamneek Mehresh * 4dc9cdf85SRamneek Mehresh * DWC3 controller driver 5dc9cdf85SRamneek Mehresh * 6dc9cdf85SRamneek Mehresh * Author: Ramneek Mehresh<ramneek.mehresh@freescale.com> 7dc9cdf85SRamneek Mehresh * 8dc9cdf85SRamneek Mehresh * SPDX-License-Identifier: GPL-2.0+ 9dc9cdf85SRamneek Mehresh */ 10dc9cdf85SRamneek Mehresh 11dc9cdf85SRamneek Mehresh #include <common.h> 12b7c1c7d2SPatrice Chotard #include <dm.h> 13f56db163SPatrice Chotard #include <fdtdec.h> 14f56db163SPatrice Chotard #include <generic-phy.h> 15b7c1c7d2SPatrice Chotard #include <usb.h> 16b7c1c7d2SPatrice Chotard 17b7c1c7d2SPatrice Chotard #include "xhci.h" 18dc9cdf85SRamneek Mehresh #include <asm/io.h> 19dc9cdf85SRamneek Mehresh #include <linux/usb/dwc3.h> 20576e3cc7SPatrice Chotard #include <linux/usb/otg.h> 21dc9cdf85SRamneek Mehresh 22b7c1c7d2SPatrice Chotard DECLARE_GLOBAL_DATA_PTR; 23b7c1c7d2SPatrice Chotard 24f56db163SPatrice Chotard struct xhci_dwc3_platdata { 25*3cdbc057SNeil Armstrong struct phy *usb_phys; 26*3cdbc057SNeil Armstrong int num_phys; 27f56db163SPatrice Chotard }; 28f56db163SPatrice Chotard 29dc9cdf85SRamneek Mehresh void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) 30dc9cdf85SRamneek Mehresh { 31dc9cdf85SRamneek Mehresh clrsetbits_le32(&dwc3_reg->g_ctl, 32dc9cdf85SRamneek Mehresh DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), 33dc9cdf85SRamneek Mehresh DWC3_GCTL_PRTCAPDIR(mode)); 34dc9cdf85SRamneek Mehresh } 35dc9cdf85SRamneek Mehresh 36121a4d13SMasahiro Yamada static void dwc3_phy_reset(struct dwc3 *dwc3_reg) 37dc9cdf85SRamneek Mehresh { 38dc9cdf85SRamneek Mehresh /* Assert USB3 PHY reset */ 39dc9cdf85SRamneek Mehresh setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); 40dc9cdf85SRamneek Mehresh 41dc9cdf85SRamneek Mehresh /* Assert USB2 PHY reset */ 42dc9cdf85SRamneek Mehresh setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); 43dc9cdf85SRamneek Mehresh 44dc9cdf85SRamneek Mehresh mdelay(100); 45dc9cdf85SRamneek Mehresh 46dc9cdf85SRamneek Mehresh /* Clear USB3 PHY reset */ 47dc9cdf85SRamneek Mehresh clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); 48dc9cdf85SRamneek Mehresh 49dc9cdf85SRamneek Mehresh /* Clear USB2 PHY reset */ 50dc9cdf85SRamneek Mehresh clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); 51dc9cdf85SRamneek Mehresh } 52dc9cdf85SRamneek Mehresh 53dc9cdf85SRamneek Mehresh void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) 54dc9cdf85SRamneek Mehresh { 55dc9cdf85SRamneek Mehresh /* Before Resetting PHY, put Core in Reset */ 56dc9cdf85SRamneek Mehresh setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); 57dc9cdf85SRamneek Mehresh 58dc9cdf85SRamneek Mehresh /* reset USB3 phy - if required */ 59dc9cdf85SRamneek Mehresh dwc3_phy_reset(dwc3_reg); 60dc9cdf85SRamneek Mehresh 615955bb93SRajesh Bhagat mdelay(100); 625955bb93SRajesh Bhagat 63dc9cdf85SRamneek Mehresh /* After PHYs are stable we can take Core out of reset state */ 64dc9cdf85SRamneek Mehresh clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); 65dc9cdf85SRamneek Mehresh } 66dc9cdf85SRamneek Mehresh 67dc9cdf85SRamneek Mehresh int dwc3_core_init(struct dwc3 *dwc3_reg) 68dc9cdf85SRamneek Mehresh { 69dc9cdf85SRamneek Mehresh u32 reg; 70dc9cdf85SRamneek Mehresh u32 revision; 71dc9cdf85SRamneek Mehresh unsigned int dwc3_hwparams1; 72dc9cdf85SRamneek Mehresh 73dc9cdf85SRamneek Mehresh revision = readl(&dwc3_reg->g_snpsid); 74dc9cdf85SRamneek Mehresh /* This should read as U3 followed by revision number */ 75dc9cdf85SRamneek Mehresh if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { 76dc9cdf85SRamneek Mehresh puts("this is not a DesignWare USB3 DRD Core\n"); 77dc9cdf85SRamneek Mehresh return -1; 78dc9cdf85SRamneek Mehresh } 79dc9cdf85SRamneek Mehresh 80dc9cdf85SRamneek Mehresh dwc3_core_soft_reset(dwc3_reg); 81dc9cdf85SRamneek Mehresh 82dc9cdf85SRamneek Mehresh dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); 83dc9cdf85SRamneek Mehresh 84dc9cdf85SRamneek Mehresh reg = readl(&dwc3_reg->g_ctl); 85dc9cdf85SRamneek Mehresh reg &= ~DWC3_GCTL_SCALEDOWN_MASK; 86dc9cdf85SRamneek Mehresh reg &= ~DWC3_GCTL_DISSCRAMBLE; 87dc9cdf85SRamneek Mehresh switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { 88dc9cdf85SRamneek Mehresh case DWC3_GHWPARAMS1_EN_PWROPT_CLK: 89dc9cdf85SRamneek Mehresh reg &= ~DWC3_GCTL_DSBLCLKGTNG; 90dc9cdf85SRamneek Mehresh break; 91dc9cdf85SRamneek Mehresh default: 92dc9cdf85SRamneek Mehresh debug("No power optimization available\n"); 93dc9cdf85SRamneek Mehresh } 94dc9cdf85SRamneek Mehresh 95dc9cdf85SRamneek Mehresh /* 96dc9cdf85SRamneek Mehresh * WORKAROUND: DWC3 revisions <1.90a have a bug 97dc9cdf85SRamneek Mehresh * where the device can fail to connect at SuperSpeed 98dc9cdf85SRamneek Mehresh * and falls back to high-speed mode which causes 99dc9cdf85SRamneek Mehresh * the device to enter a Connect/Disconnect loop 100dc9cdf85SRamneek Mehresh */ 101dc9cdf85SRamneek Mehresh if ((revision & DWC3_REVISION_MASK) < 0x190a) 102dc9cdf85SRamneek Mehresh reg |= DWC3_GCTL_U2RSTECN; 103dc9cdf85SRamneek Mehresh 104dc9cdf85SRamneek Mehresh writel(reg, &dwc3_reg->g_ctl); 105dc9cdf85SRamneek Mehresh 106dc9cdf85SRamneek Mehresh return 0; 107dc9cdf85SRamneek Mehresh } 108667f4dd9SNikhil Badola 109667f4dd9SNikhil Badola void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) 110667f4dd9SNikhil Badola { 111667f4dd9SNikhil Badola setbits_le32(&dwc3_reg->g_fladj, GFLADJ_30MHZ_REG_SEL | 112667f4dd9SNikhil Badola GFLADJ_30MHZ(val)); 113667f4dd9SNikhil Badola } 114b7c1c7d2SPatrice Chotard 115623b7acaSPatrice Chotard #ifdef CONFIG_DM_USB 116*3cdbc057SNeil Armstrong static int xhci_dwc3_setup_phy(struct udevice *dev, int count) 117ba870c53SVignesh R { 118*3cdbc057SNeil Armstrong struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); 119*3cdbc057SNeil Armstrong int i, ret; 120ba870c53SVignesh R 121*3cdbc057SNeil Armstrong if (!count) 122*3cdbc057SNeil Armstrong return 0; 123*3cdbc057SNeil Armstrong 124*3cdbc057SNeil Armstrong plat->usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), 125*3cdbc057SNeil Armstrong GFP_KERNEL); 126*3cdbc057SNeil Armstrong if (!plat->usb_phys) 127*3cdbc057SNeil Armstrong return -ENOMEM; 128*3cdbc057SNeil Armstrong 129*3cdbc057SNeil Armstrong for (i = 0; i < count; i++) { 130*3cdbc057SNeil Armstrong ret = generic_phy_get_by_index(dev, i, &plat->usb_phys[i]); 131*3cdbc057SNeil Armstrong if (ret && ret != -ENOENT) { 132*3cdbc057SNeil Armstrong pr_err("Failed to get USB PHY%d for %s\n", 133*3cdbc057SNeil Armstrong i, dev->name); 134ba870c53SVignesh R return ret; 135ba870c53SVignesh R } 136*3cdbc057SNeil Armstrong 137*3cdbc057SNeil Armstrong ++plat->num_phys; 138ba870c53SVignesh R } 139*3cdbc057SNeil Armstrong 140*3cdbc057SNeil Armstrong for (i = 0; i < plat->num_phys; i++) { 141*3cdbc057SNeil Armstrong ret = generic_phy_init(&plat->usb_phys[i]); 142ba870c53SVignesh R if (ret) { 143*3cdbc057SNeil Armstrong pr_err("Can't init USB PHY%d for %s\n", 144*3cdbc057SNeil Armstrong i, dev->name); 145*3cdbc057SNeil Armstrong goto phys_init_err; 146*3cdbc057SNeil Armstrong } 147*3cdbc057SNeil Armstrong } 148*3cdbc057SNeil Armstrong 149*3cdbc057SNeil Armstrong for (i = 0; i < plat->num_phys; i++) { 150*3cdbc057SNeil Armstrong ret = generic_phy_power_on(&plat->usb_phys[i]); 151*3cdbc057SNeil Armstrong if (ret) { 152*3cdbc057SNeil Armstrong pr_err("Can't power USB PHY%d for %s\n", 153*3cdbc057SNeil Armstrong i, dev->name); 154*3cdbc057SNeil Armstrong goto phys_poweron_err; 155ba870c53SVignesh R } 156ba870c53SVignesh R } 157ba870c53SVignesh R 158ba870c53SVignesh R return 0; 159*3cdbc057SNeil Armstrong 160*3cdbc057SNeil Armstrong 161*3cdbc057SNeil Armstrong phys_poweron_err: 162*3cdbc057SNeil Armstrong for (; i >= 0; i--) 163*3cdbc057SNeil Armstrong generic_phy_power_off(&plat->usb_phys[i]); 164*3cdbc057SNeil Armstrong 165*3cdbc057SNeil Armstrong for (i = 0; i < plat->num_phys; i++) 166*3cdbc057SNeil Armstrong generic_phy_exit(&plat->usb_phys[i]); 167*3cdbc057SNeil Armstrong 168*3cdbc057SNeil Armstrong return ret; 169*3cdbc057SNeil Armstrong 170*3cdbc057SNeil Armstrong phys_init_err: 171*3cdbc057SNeil Armstrong for (; i >= 0; i--) 172*3cdbc057SNeil Armstrong generic_phy_exit(&plat->usb_phys[i]); 173*3cdbc057SNeil Armstrong 174*3cdbc057SNeil Armstrong return ret; 175ba870c53SVignesh R } 176ba870c53SVignesh R 177*3cdbc057SNeil Armstrong static int xhci_dwc3_shutdown_phy(struct udevice *dev) 178ba870c53SVignesh R { 179*3cdbc057SNeil Armstrong struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); 180*3cdbc057SNeil Armstrong int i, ret; 181ba870c53SVignesh R 182*3cdbc057SNeil Armstrong for (i = 0; i < plat->num_phys; i++) { 183*3cdbc057SNeil Armstrong if (!generic_phy_valid(&plat->usb_phys[i])) 184*3cdbc057SNeil Armstrong continue; 185ba870c53SVignesh R 186*3cdbc057SNeil Armstrong ret = generic_phy_power_off(&plat->usb_phys[i]); 187*3cdbc057SNeil Armstrong ret |= generic_phy_exit(&plat->usb_phys[i]); 188*3cdbc057SNeil Armstrong if (ret) { 189*3cdbc057SNeil Armstrong pr_err("Can't shutdown USB PHY%d for %s\n", 190*3cdbc057SNeil Armstrong i, dev->name); 191*3cdbc057SNeil Armstrong } 192ba870c53SVignesh R } 193ba870c53SVignesh R 194ba870c53SVignesh R return 0; 195ba870c53SVignesh R } 196ba870c53SVignesh R 197b7c1c7d2SPatrice Chotard static int xhci_dwc3_probe(struct udevice *dev) 198b7c1c7d2SPatrice Chotard { 199b7c1c7d2SPatrice Chotard struct xhci_hcor *hcor; 200b7c1c7d2SPatrice Chotard struct xhci_hccr *hccr; 201b7c1c7d2SPatrice Chotard struct dwc3 *dwc3_reg; 202576e3cc7SPatrice Chotard enum usb_dr_mode dr_mode; 203f56db163SPatrice Chotard int ret; 204b7c1c7d2SPatrice Chotard 205d38a8ea1SPatrice Chotard hccr = (struct xhci_hccr *)((uintptr_t)dev_read_addr(dev)); 206d38a8ea1SPatrice Chotard hcor = (struct xhci_hcor *)((uintptr_t)hccr + 207b7c1c7d2SPatrice Chotard HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); 208b7c1c7d2SPatrice Chotard 209*3cdbc057SNeil Armstrong ret = xhci_dwc3_setup_phy(dev, dev_count_phandle_with_args( 210*3cdbc057SNeil Armstrong dev, "phys", "#phy-cells")); 211*3cdbc057SNeil Armstrong if (ret) 212f56db163SPatrice Chotard return ret; 213a5a589c8SVignesh R 214b7c1c7d2SPatrice Chotard dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET); 215b7c1c7d2SPatrice Chotard 216b7c1c7d2SPatrice Chotard dwc3_core_init(dwc3_reg); 217b7c1c7d2SPatrice Chotard 218576e3cc7SPatrice Chotard dr_mode = usb_get_dr_mode(dev_of_offset(dev)); 219576e3cc7SPatrice Chotard if (dr_mode == USB_DR_MODE_UNKNOWN) 220576e3cc7SPatrice Chotard /* by default set dual role mode to HOST */ 221576e3cc7SPatrice Chotard dr_mode = USB_DR_MODE_HOST; 222576e3cc7SPatrice Chotard 223576e3cc7SPatrice Chotard dwc3_set_mode(dwc3_reg, dr_mode); 224576e3cc7SPatrice Chotard 225b7c1c7d2SPatrice Chotard return xhci_register(dev, hccr, hcor); 226b7c1c7d2SPatrice Chotard } 227b7c1c7d2SPatrice Chotard 228b7c1c7d2SPatrice Chotard static int xhci_dwc3_remove(struct udevice *dev) 229b7c1c7d2SPatrice Chotard { 230*3cdbc057SNeil Armstrong xhci_dwc3_shutdown_phy(dev); 231f56db163SPatrice Chotard 232b7c1c7d2SPatrice Chotard return xhci_deregister(dev); 233b7c1c7d2SPatrice Chotard } 234b7c1c7d2SPatrice Chotard 235b7c1c7d2SPatrice Chotard static const struct udevice_id xhci_dwc3_ids[] = { 236b7c1c7d2SPatrice Chotard { .compatible = "snps,dwc3" }, 237b7c1c7d2SPatrice Chotard { } 238b7c1c7d2SPatrice Chotard }; 239b7c1c7d2SPatrice Chotard 240b7c1c7d2SPatrice Chotard U_BOOT_DRIVER(xhci_dwc3) = { 241b7c1c7d2SPatrice Chotard .name = "xhci-dwc3", 242b7c1c7d2SPatrice Chotard .id = UCLASS_USB, 243b7c1c7d2SPatrice Chotard .of_match = xhci_dwc3_ids, 244b7c1c7d2SPatrice Chotard .probe = xhci_dwc3_probe, 245b7c1c7d2SPatrice Chotard .remove = xhci_dwc3_remove, 246b7c1c7d2SPatrice Chotard .ops = &xhci_usb_ops, 247b7c1c7d2SPatrice Chotard .priv_auto_alloc_size = sizeof(struct xhci_ctrl), 248b7c1c7d2SPatrice Chotard .platdata_auto_alloc_size = sizeof(struct xhci_dwc3_platdata), 249b7c1c7d2SPatrice Chotard .flags = DM_FLAG_ALLOC_PRIV_DMA, 250b7c1c7d2SPatrice Chotard }; 251623b7acaSPatrice Chotard #endif 252