1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2015 Freescale Semiconductor, Inc. 4 * 5 * DWC3 controller driver 6 * 7 * Author: Ramneek Mehresh<ramneek.mehresh@freescale.com> 8 */ 9 10#include <clk.h> 11#include <common.h> 12#include <dm.h> 13#include <generic-phy.h> 14#include <log.h> 15#include <reset.h> 16#include <usb.h> 17#include <dwc3-uboot.h> 18#include <linux/delay.h> 19 20#include <usb/xhci.h> 21#include <asm/io.h> 22#include <linux/usb/dwc3.h> 23#include <linux/usb/otg.h> 24 25struct xhci_dwc3_plat { 26 struct clk_bulk clks; 27 struct phy_bulk phys; 28 struct reset_ctl_bulk resets; 29}; 30 31void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) 32{ 33 clrsetbits_le32(&dwc3_reg->g_ctl, 34 DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), 35 DWC3_GCTL_PRTCAPDIR(mode)); 36} 37 38static void dwc3_phy_reset(struct dwc3 *dwc3_reg) 39{ 40 /* Assert USB3 PHY reset */ 41 setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); 42 43 /* Assert USB2 PHY reset */ 44 setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); 45 46 mdelay(100); 47 48 /* Clear USB3 PHY reset */ 49 clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); 50 51 /* Clear USB2 PHY reset */ 52 clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); 53} 54 55void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) 56{ 57 /* Before Resetting PHY, put Core in Reset */ 58 setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); 59 60 /* reset USB3 phy - if required */ 61 dwc3_phy_reset(dwc3_reg); 62 63 mdelay(100); 64 65 /* After PHYs are stable we can take Core out of reset state */ 66 clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); 67} 68 69int dwc3_core_init(struct dwc3 *dwc3_reg) 70{ 71 u32 reg; 72 u32 revision; 73 unsigned int dwc3_hwparams1; 74 75 revision = readl(&dwc3_reg->g_snpsid); 76 /* This should read as U3 followed by revision number */ 77 if ((revision & DWC3_GSNPSID_MASK) != 0x55330000 && 78 (revision & DWC3_GSNPSID_MASK) != 0x33310000) { 79 puts("this is not a DesignWare USB3 DRD Core\n"); 80 return -1; 81 } 82 83 dwc3_core_soft_reset(dwc3_reg); 84 85 dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); 86 87 reg = readl(&dwc3_reg->g_ctl); 88 reg &= ~DWC3_GCTL_SCALEDOWN_MASK; 89 reg &= ~DWC3_GCTL_DISSCRAMBLE; 90 switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { 91 case DWC3_GHWPARAMS1_EN_PWROPT_CLK: 92 reg &= ~DWC3_GCTL_DSBLCLKGTNG; 93 break; 94 default: 95 debug("No power optimization available\n"); 96 } 97 98 /* 99 * WORKAROUND: DWC3 revisions <1.90a have a bug 100 * where the device can fail to connect at SuperSpeed 101 * and falls back to high-speed mode which causes 102 * the device to enter a Connect/Disconnect loop 103 */ 104 if ((revision & DWC3_REVISION_MASK) < 0x190a) 105 reg |= DWC3_GCTL_U2RSTECN; 106 107 writel(reg, &dwc3_reg->g_ctl); 108 109 return 0; 110} 111 112void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) 113{ 114 setbits_le32(&dwc3_reg->g_fladj, GFLADJ_30MHZ_REG_SEL | 115 GFLADJ_30MHZ(val)); 116} 117 118#if CONFIG_IS_ENABLED(DM_USB) 119static int xhci_dwc3_reset_init(struct udevice *dev, 120 struct xhci_dwc3_plat *plat) 121{ 122 int ret; 123 124 ret = reset_get_bulk(dev, &plat->resets); 125 if (ret == -ENOTSUPP || ret == -ENOENT) 126 return 0; 127 else if (ret) 128 return ret; 129 130 ret = reset_deassert_bulk(&plat->resets); 131 if (ret) { 132 reset_release_bulk(&plat->resets); 133 return ret; 134 } 135 136 return 0; 137} 138 139static int xhci_dwc3_clk_init(struct udevice *dev, 140 struct xhci_dwc3_plat *plat) 141{ 142 int ret; 143 144 ret = clk_get_bulk(dev, &plat->clks); 145 if (ret == -ENOSYS || ret == -ENOENT) 146 return 0; 147 if (ret) 148 return ret; 149 150 ret = clk_enable_bulk(&plat->clks); 151 if (ret) { 152 clk_release_bulk(&plat->clks); 153 return ret; 154 } 155 156 return 0; 157} 158 159static int xhci_dwc3_probe(struct udevice *dev) 160{ 161 struct xhci_hcor *hcor; 162 struct xhci_hccr *hccr; 163 struct dwc3 *dwc3_reg; 164 enum usb_dr_mode dr_mode; 165 struct xhci_dwc3_plat *plat = dev_get_plat(dev); 166 const char *phy; 167 u32 reg; 168 int ret; 169 170 ret = xhci_dwc3_reset_init(dev, plat); 171 if (ret) 172 return ret; 173 174 ret = xhci_dwc3_clk_init(dev, plat); 175 if (ret) 176 return ret; 177 178 hccr = (struct xhci_hccr *)((uintptr_t)dev_remap_addr(dev)); 179 hcor = (struct xhci_hcor *)((uintptr_t)hccr + 180 HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); 181 182 ret = dwc3_setup_phy(dev, &plat->phys); 183 if (ret && (ret != -ENOTSUPP)) 184 return ret; 185 186 dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET); 187 188 dwc3_core_init(dwc3_reg); 189 190 /* Set dwc3 usb2 phy config */ 191 reg = readl(&dwc3_reg->g_usb2phycfg[0]); 192 193 phy = dev_read_string(dev, "phy_type"); 194 if (phy && strcmp(phy, "utmi_wide") == 0) { 195 reg |= DWC3_GUSB2PHYCFG_PHYIF; 196 reg &= ~DWC3_GUSB2PHYCFG_USBTRDTIM_MASK; 197 reg |= DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT; 198 } 199 200 if (dev_read_bool(dev, "snps,dis_enblslpm_quirk")) 201 reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; 202 203 if (dev_read_bool(dev, "snps,dis-u2-freeclk-exists-quirk")) 204 reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; 205 206 if (dev_read_bool(dev, "snps,dis_u2_susphy_quirk")) 207 reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 208 209 writel(reg, &dwc3_reg->g_usb2phycfg[0]); 210 211 dr_mode = usb_get_dr_mode(dev_ofnode(dev)); 212 if (dr_mode == USB_DR_MODE_OTG && 213 dev_read_bool(dev, "usb-role-switch")) { 214 dr_mode = usb_get_role_switch_default_mode(dev_ofnode(dev)); 215 if (dr_mode == USB_DR_MODE_UNKNOWN) 216 dr_mode = USB_DR_MODE_OTG; 217 } 218 if (dr_mode == USB_DR_MODE_UNKNOWN) 219 /* by default set dual role mode to HOST */ 220 dr_mode = USB_DR_MODE_HOST; 221 222 dwc3_set_mode(dwc3_reg, dr_mode); 223 224 return xhci_register(dev, hccr, hcor); 225} 226 227static int xhci_dwc3_remove(struct udevice *dev) 228{ 229 struct xhci_dwc3_plat *plat = dev_get_plat(dev); 230 231 dwc3_shutdown_phy(dev, &plat->phys); 232 233 clk_release_bulk(&plat->clks); 234 235 reset_release_bulk(&plat->resets); 236 237 return xhci_deregister(dev); 238} 239 240static const struct udevice_id xhci_dwc3_ids[] = { 241 { .compatible = "snps,dwc3" }, 242 { } 243}; 244 245U_BOOT_DRIVER(xhci_dwc3) = { 246 .name = "xhci-dwc3", 247 .id = UCLASS_USB, 248 .of_match = xhci_dwc3_ids, 249 .probe = xhci_dwc3_probe, 250 .remove = xhci_dwc3_remove, 251 .ops = &xhci_usb_ops, 252 .priv_auto = sizeof(struct xhci_ctrl), 253 .plat_auto = sizeof(struct xhci_dwc3_plat), 254 .flags = DM_FLAG_ALLOC_PRIV_DMA, 255}; 256#endif 257