| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <device/mmio.h> |
| #include <console/console.h> |
| #include <delay.h> |
| #include <soc/clock.h> |
| #include <soc/iomap.h> |
| #include <soc/usb.h> |
| |
| /** |
| * 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(); |
| } |