Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6378d95f authored by Mayank Rana's avatar Mayank Rana
Browse files

USB: dwc3: Implement revised initialization sequence



Revise initialization and power-on routines to
be more consistent with recommended sequences.
Since some of the same sequences are repeated during
USB cable reconnection and exiting from power collapse,
consolidate these into common routines to avoid duplicated code.

Change-Id: I99856ac2ff01daa75b7e6d5a584c1b314402aa03
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent c5564588
Loading
Loading
Loading
Loading
+61 −62
Original line number Diff line number Diff line
@@ -111,20 +111,16 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
}

/**
 * dwc3_core_soft_reset_after_phy_init - Issues core soft reset
 * and PHY reset for HW versions which require core reset after
 * PHY initialization and reset
 * Peforms initialization of HS and SS PHYs.
 * If used as a part of POR or init sequence it is recommended
 * that we should perform hard reset of the PHYs prior to invoking
 * this function.
 * @dwc: pointer to our context structure
*/
static int dwc3_core_soft_reset_after_phy_init(struct dwc3 *dwc)
static int dwc3_init_usb_phys(struct dwc3 *dwc)
{
	u32		reg;
	int		ret;

	/* Reset PHYs */
	usb_phy_reset(dwc->usb3_phy);
	usb_phy_reset(dwc->usb2_phy);

	/* Bring up PHYs */
	ret = usb_phy_init(dwc->usb2_phy);
	if (ret) {
@@ -139,85 +135,90 @@ static int dwc3_core_soft_reset_after_phy_init(struct dwc3 *dwc)
		return ret;
	}

	/* Put Core in Reset */
	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
	reg |= DWC3_GCTL_CORESOFTRESET;
	dwc3_writel(dwc->regs, DWC3_GCTL, reg);

	dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT);

	/* Take Core out of reset state */
	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
	reg &= ~DWC3_GCTL_CORESOFTRESET;
	dwc3_writel(dwc->regs, DWC3_GCTL, reg);

	dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT);

	return 0;
}

/**
 * dwc3_core_soft_reset - Issues core soft reset and PHY reset
 * Peforms core soft reset and PHY soft reset of HS and SS PHYs.
 * If used as a part of POR or init sequence it is recommended
 * that we should perform hard reset and init of the PHYs prior
 * to invoking this function.
 * @dwc: pointer to our context structure
*/
static int dwc3_core_soft_reset(struct dwc3 *dwc)
static void dwc3_core_and_phy_soft_reset(struct dwc3 *dwc)
{
	u32		reg;
	int		ret;

	if (dwc->core_reset_after_phy_init)
		return dwc3_core_soft_reset_after_phy_init(dwc);

	/* Before Resetting PHY, put Core in Reset */
	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
	reg |= DWC3_GCTL_CORESOFTRESET;
	dwc3_writel(dwc->regs, DWC3_GCTL, reg);

	/* Bring up PHYs */
	ret = usb_phy_init(dwc->usb2_phy);
	if (ret) {
		pr_err("%s: usb_phy_init(dwc->usb2_phy) returned %d\n",
				__func__, ret);
		return ret;
	}
	ret = usb_phy_init(dwc->usb3_phy);
	if (ret) {
		pr_err("%s: usb_phy_init(dwc->usb3_phy) returned %d\n",
				__func__, ret);
		return ret;
	}

	dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT);

	/* Assert USB3 PHY reset */
	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
	reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);

	/* Assert USB2 PHY reset */
	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
	reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);

	mdelay(100);
	msleep(100);

	/* Clear USB3 PHY reset */
	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
	reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);

	if (dwc->revision >= DWC3_REVISION_270A) {
		reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
		reg |= DWC3_GUSB3PIPECTL_DELAYP1TRANS;
		dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
	}

	/* Assert USB2 PHY reset */
	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
	reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);

	msleep(100);

	/* Clear USB2 PHY reset */
	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
	reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);

	mdelay(100);
	msleep(100);

	/* After PHYs are stable we can take Core out of reset state */
	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
	reg &= ~DWC3_GCTL_CORESOFTRESET;
	dwc3_writel(dwc->regs, DWC3_GCTL, reg);

	msleep(100);
}

/**
 * dwc3_core_soft_reset - Issues core soft reset and PHY reset
 * @dwc: pointer to our context structure
 */
static int dwc3_core_reset(struct dwc3 *dwc)
{
	int		ret;

	/* Reset PHYs */
	usb_phy_reset(dwc->usb2_phy);
	usb_phy_reset(dwc->usb3_phy);

	/* Initialize PHYs */
	ret = dwc3_init_usb_phys(dwc);
	if (ret) {
		pr_err("%s: dwc3_init_phys returned %d\n",
				__func__, ret);
		return ret;
	}

	dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT);

	/* Perform core and PHY soft reset */
	dwc3_core_and_phy_soft_reset(dwc);

	dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT);

	return 0;
@@ -400,7 +401,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 *
 * Returns 0 on success otherwise negative errno.
 */
static int dwc3_core_init(struct dwc3 *dwc)
int dwc3_core_init(struct dwc3 *dwc)
{
	unsigned long		timeout;
	u32			reg;
@@ -415,6 +416,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
	}
	dwc->revision = reg;


	ret = dwc3_core_reset(dwc);
	if (ret)
		goto err0;

	/* issue device SoftReset too */
	timeout = jiffies + msecs_to_jiffies(500);
	dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -432,10 +438,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
		cpu_relax();
	} while (true);

	ret = dwc3_core_soft_reset(dwc);
	if (ret)
		goto err0;

	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
	reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
	reg &= ~DWC3_GCTL_DISSCRAMBLE;
@@ -619,9 +621,6 @@ static int dwc3_probe(struct platform_device *pdev)
		return -ENOMEM;
	}

	dwc->core_reset_after_phy_init =
		of_property_read_bool(node, "snps,core-reset-after-phy-init");

	dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
	host_only_mode = of_property_read_bool(node, "snps,host-only-mode");
	dwc->ssphy_clear_auto_suspend_on_disconnect =
+7 −1
Original line number Diff line number Diff line
@@ -165,6 +165,10 @@

/* Bit fields */

/* Global SoC Bus Configuration Register 1 */
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT_MASK	(0x0f << 8)
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n)	((n) << 8)

/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n)	((n) << 19)
#define DWC3_GCTL_PWRDNSCALEMASK (0xFFF80000)
@@ -205,6 +209,7 @@
#define DWC3_GUSB3PIPECTL_PHYSOFTRST	(1 << 31)
#define DWC3_GUSB3PIPECTL_SUSPHY	(1 << 17)
#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3	(7 << 19)
#define DWC3_GUSB3PIPECTL_DELAYP1TRANS  (1 << 18)
#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET (1 << 22)
#define DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE	(1 << 0)

@@ -845,6 +850,7 @@ struct dwc3 {
#define DWC3_REVISION_230A	0x5533230a
#define DWC3_REVISION_240A	0x5533240a
#define DWC3_REVISION_250A	0x5533250a
#define DWC3_REVISION_270A	0x5533270a

	unsigned		is_selfpowered:1;
	unsigned		three_stage_setup:1;
@@ -891,7 +897,6 @@ struct dwc3 {
	bool			tx_fifo_reduced;

	bool			nominal_elastic_buffer;
	bool			core_reset_after_phy_init;
	bool			err_evt_seen;
	bool			ssphy_clear_auto_suspend_on_disconnect;
	bool			usb3_u1u2_disable;
@@ -1098,6 +1103,7 @@ static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend) { }
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */

void dwc3_gadget_restart(struct dwc3 *dwc);
int dwc3_core_init(struct dwc3 *dwc);
void dwc3_post_host_reset_core_init(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);

+23 −40
Original line number Diff line number Diff line
@@ -1028,6 +1028,8 @@ static int dwc3_msm_link_clk_reset(struct dwc3_msm *mdwc, bool assert)
		dev_dbg(mdwc->dev, "block_reset ASSERT\n");
		clk_disable_unprepare(mdwc->ref_clk);
		clk_disable_unprepare(mdwc->iface_clk);
		clk_disable_unprepare(mdwc->utmi_clk);
		clk_disable_unprepare(mdwc->sleep_clk);
		clk_disable_unprepare(mdwc->core_clk);
		ret = clk_reset(mdwc->core_clk, CLK_RESET_ASSERT);
		if (ret)
@@ -1037,6 +1039,8 @@ static int dwc3_msm_link_clk_reset(struct dwc3_msm *mdwc, bool assert)
		ret = clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
		ndelay(200);
		clk_prepare_enable(mdwc->core_clk);
		clk_prepare_enable(mdwc->sleep_clk);
		clk_prepare_enable(mdwc->utmi_clk);
		clk_prepare_enable(mdwc->ref_clk);
		clk_prepare_enable(mdwc->iface_clk);
		if (ret)
@@ -1450,46 +1454,8 @@ static void dwc3_start_chg_det(struct dwc3_charger *charger, bool start)

static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
{
	u32		reg;
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);

	/* Put Core in Reset */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GCTL);
	reg |= DWC3_GCTL_CORESOFTRESET;
	dwc3_msm_write_reg(mdwc->base, DWC3_GCTL, reg);

	usb_phy_init(dwc->usb2_phy);

	/* Assert USB3 PHY reset */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(0));
	reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(0), reg);

	/* Assert USB2 PHY reset */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0));
	reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0), reg);

	udelay(100);

	/* Clear USB3 PHY reset */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB3PIPECTL(0));
	reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB3PIPECTL(0), reg);

	/* Clear USB2 PHY reset */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0));
	reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0), reg);

	udelay(100);
	/* After PHYs are stable we can take Core out of reset state */
	reg = dwc3_msm_read_reg(mdwc->base, DWC3_GCTL);
	reg &= ~DWC3_GCTL_CORESOFTRESET;
	dwc3_msm_write_reg(mdwc->base, DWC3_GCTL, reg);

	udelay(100);

	dwc3_core_init(dwc);
	/* Re-configure event buffers */
	dwc3_event_buffers_setup(dwc);
}
@@ -1714,6 +1680,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
		mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
		dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
		dwc3_msm_config_gdsc(mdwc, 0);
		clk_disable_unprepare(mdwc->sleep_clk);
	}

	clk_disable_unprepare(mdwc->iface_clk);
@@ -1832,7 +1799,6 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
			clk_reset(mdwc->phy_com_reset, CLK_RESET_DEASSERT);
	}

	clk_prepare_enable(mdwc->utmi_clk);

	if (mdwc->lpm_flags & MDWC3_PHY_REF_CLK_OFF) {
		clk_prepare_enable(mdwc->ref_clk);
@@ -1848,6 +1814,11 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
		mdwc->lpm_flags &= ~MDWC3_CORECLK_OFF;
	}

	clk_prepare_enable(mdwc->utmi_clk);

	if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE)
		clk_prepare_enable(mdwc->sleep_clk);

	usb_phy_set_suspend(mdwc->hs_phy, 0);

	/* Recover from controller power collapse */
@@ -1862,6 +1833,13 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
			return ret;
		}

		/* Reset SS PHY */
		ret = usb_phy_reset(mdwc->ss_phy);
		if (ret) {
			dev_err(mdwc->dev, "ssphy reset failed\n");
			return ret;
		}

		ret = dwc3_msm_restore_sec_config(mdwc->scm_dev_id);
		if (ret)
			return ret;
@@ -3146,6 +3124,11 @@ static int dwc3_msm_probe(struct platform_device *pdev)
		}
	}

	/* Perform controller GCC reset */
	dwc3_msm_link_clk_reset(mdwc, 1);
	msleep(20);
	dwc3_msm_link_clk_reset(mdwc, 0);

	ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
	if (ret) {
		dev_err(&pdev->dev,
+10 −0
Original line number Diff line number Diff line
@@ -2107,6 +2107,16 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
	}
	dwc3_writel(dwc->regs, DWC3_DCFG, reg);

	/* Programs the number of outstanding pipelined transfer requests
	 * the AXI master pushes to the AXI slave.
	 */
	if (dwc->revision >= DWC3_REVISION_270A) {
		reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
		reg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT_MASK;
		reg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(0xe);
		dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, reg);
	}

	dwc->start_config_issued = false;

	/* Start with SuperSpeed Default */