/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include /** * USB Hardware registers */ #define PHY_CTRL0_ADDR 0x000 #define PHY_CTRL1_ADDR 0x004 #define PHY_CTRL2_ADDR 0x008 #define PHY_CTRL3_ADDR 0x00C #define PHY_CTRL4_ADDR 0x010 #define PHY_MISC_ADDR 0x024 #define PHY_IPG_ADDR 0x030 #define PHY_CTRL0_VAL 0xA4600015 #define PHY_CTRL1_VAL 0x09500000 #define PHY_CTRL2_VAL 0x00058180 #define PHY_CTRL3_VAL 0x6DB6DCD6 #define PHY_CTRL4_VAL 0x836DB6DB #define PHY_MISC_VAL 0x3803FB0C #define PHY_IPG_VAL 0x47323232 #define USB_HOST3_PHY_BASE ((void *)0x8a00000) #define USB_HOST3_BALDUR_PHY_BASE ((void *)0xa6000) #define GCC_USB3_RST_CTRL ((void *)0x0181E038) #define DWC3_GCTL 0xc110 #define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) #define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) #define DWC3_GCTL_CORESOFTRESET (1 << 11) #define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) #define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_DCTL_CSFTRST (1 << 30) #define DWC3_GSNPSID 0xc120 #define DWC3_DCTL 0xc704 /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GEVTEN 0xc114 #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) #define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0) #define DWC3_GCTL_U2RSTECN (1 << 16) #define DWC3_REVISION_190A 0x5533190a #define USB30_HS_PHY_CTRL 0x00000010 #define SW_SESSVLD (0x01 << 0x1C) #define UTMI_OTG_VBUS_VALID (0x01 << 0x14) #define USB30_SS_PHY_CTRL 0x00000030 #define LANE0_PWR_PRESENT (0x01 << 0x18) static void setup_dwc3(void); /** * Write register. * * @param base - PHY base virtual address. * @param offset - register offset. * @param val - value to write. */ static inline void qscratch_write(void *base, u32 offset, u32 val) { write32(base + offset, val); } /** * Write register and read back masked value to confirm it is written * * @param base - base virtual address. * @param offset - register offset. * @param mask - register bitmask specifying what should be updated * @param val - value to write. */ static inline void qscratch_write_readback(void *base, u32 offset, const u32 mask, u32 val) { u32 write_val, tmp = read32(base + offset); tmp &= ~mask; /* retain other bits */ write_val = tmp | val; write32(base + offset, write_val); /* Read back to see if val was written */ tmp = read32(base + offset); tmp &= mask; /* clear other bits */ if (tmp != val) { printk(BIOS_INFO, "write: %x to QSCRATCH: %x FAILED\n", val, offset); } } static void dwc3_ipq40xx_enable_vbus_valid(void) { /* Enable VBUS valid for HS PHY*/ qscratch_write_readback((void *)0x8af8800, USB30_HS_PHY_CTRL, SW_SESSVLD, SW_SESSVLD); qscratch_write_readback((void *)0x8af8800, USB30_HS_PHY_CTRL, UTMI_OTG_VBUS_VALID, UTMI_OTG_VBUS_VALID); /* Enable VBUS valid for SS PHY*/ qscratch_write_readback((void *)0x8af8800, USB30_SS_PHY_CTRL, LANE0_PWR_PRESENT, LANE0_PWR_PRESENT); } static void qcom_baldur_hs_phy_init(void) { u32 reg; /* assert HS PHY POR reset */ reg = read32(GCC_USB3_RST_CTRL); reg = reg | 0x10; write32(GCC_USB3_RST_CTRL, reg); mdelay(10); /* assert HS PHY SRIF reset */ reg = read32(GCC_USB3_RST_CTRL); reg = reg | 0x4; write32(GCC_USB3_RST_CTRL, reg); mdelay(10); /* deassert HS PHY SRIF reset and program HS PHY registers */ reg = read32(GCC_USB3_RST_CTRL); reg = reg & ~0x4; write32(GCC_USB3_RST_CTRL, reg); mdelay(10); /* perform PHY register writes */ write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL0_ADDR, PHY_CTRL0_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL1_ADDR, PHY_CTRL1_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL2_ADDR, PHY_CTRL2_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL3_ADDR, PHY_CTRL3_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL4_ADDR, PHY_CTRL4_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_MISC_ADDR, PHY_MISC_VAL); write32(USB_HOST3_BALDUR_PHY_BASE + PHY_IPG_ADDR, PHY_IPG_VAL); mdelay(10); /* de-assert USB3 HS PHY POR reset */ reg = read32(GCC_USB3_RST_CTRL); reg = reg & ~0x10; write32(GCC_USB3_RST_CTRL, reg); } static void qcom_uni_ss_phy_init(void) { u32 reg; /* assert SS PHY POR reset */ reg = read32(GCC_USB3_RST_CTRL); reg = reg | 0x20; write32(GCC_USB3_RST_CTRL, reg); mdelay(100); /* deassert SS PHY POR reset */ reg = read32(GCC_USB3_RST_CTRL); reg = reg & ~0x20; write32(GCC_USB3_RST_CTRL, reg); } void setup_dwc3(void) { u32 reg; u32 revision; revision = read32(USB_HOST3_PHY_BASE + DWC3_GSNPSID); /* This should read as U3 followed by revision number */ if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) printk(BIOS_INFO, "Error in reading Version\n"); printk(BIOS_INFO, "Version = %x\n", revision); /* issue device SoftReset too */ write32(USB_HOST3_PHY_BASE + DWC3_DCTL, DWC3_DCTL_CSFTRST); do { reg = read32(USB_HOST3_PHY_BASE + DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) break; udelay(10); } while (true); printk(BIOS_INFO, "software reset done\n"); /* Before Resetting PHY, put Core in Reset */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL); reg |= DWC3_GCTL_CORESOFTRESET; write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg); /* Assert USB3 PHY reset */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0)); reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg); /* Assert USB2 PHY reset */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0)); reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg); qcom_baldur_hs_phy_init(); qcom_uni_ss_phy_init(); mdelay(100); /* Clear USB3 PHY reset */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0)); reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg); /* Clear USB2 PHY reset */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0)); reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg); mdelay(100); /* After PHYs are stable we can take Core out of reset state */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg); #if 0 /* Enable Suspend USB2.0 HS/FS/LS PHY (SusPHY) */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0)); reg |= DWC3_GUSB2PHYCFG_SUSPHY; write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg); /* Enable Suspend USB3.0 SS PHY (Suspend_en) */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0)); reg |= DWC3_GUSB3PIPECTL_SUSPHY; write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg); #endif /* configure controller in Host mode */ reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL); reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(0x1); /* host mode */ write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg); printk(BIOS_INFO, "USB Host mode reg = %x\n", reg); reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; reg &= ~DWC3_GCTL_DISSCRAMBLE; reg &= ~DWC3_GCTL_DSBLCLKGTNG; /* * WORKAROUND: DWC3 revisions <1.90a have a bug * where the device can fail to connect at SuperSpeed * and falls back to high-speed mode which causes * the device to enter a Connect/Disconnect loop */ if (revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg); } void setup_usb_host1(void) { printk(BIOS_INFO, "Setting up USB HOST1 controller.\n"); setup_dwc3(); dwc3_ipq40xx_enable_vbus_valid(); }