summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc2/core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-04 17:55:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-04 17:55:35 -0700
commitac9053d2dcb9e8c3fa35ce458dfca8fddc141680 (patch)
tree3ffa30d58dac22ee0a80e2dd32f41b71da91132b /drivers/usb/dwc2/core.c
parentf9ca6a561d40115696a54f16085c4edb17effc74 (diff)
parent5267c5e09c25e2ee6242b37833a9bdf9d97f54a2 (diff)
downloadtalos-op-linux-ac9053d2dcb9e8c3fa35ce458dfca8fddc141680.tar.gz
talos-op-linux-ac9053d2dcb9e8c3fa35ce458dfca8fddc141680.zip
Merge tag 'usb-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH: "Here is the big set of USB and PHY driver patches for 4.17-rc1. Lots of USB typeC work happened this round, with code moving from the staging directory into the "real" part of the kernel, as well as new infrastructure being added to be able to handle the different types of "roles" that typeC requires. There is also the normal huge set of USB gadget controller and driver updates, along with XHCI changes, and a raft of other tiny fixes all over the USB tree. And the PHY driver updates are merged in here as well as they interacted with the USB drivers in some places. All of these have been in linux-next for a while with no reported issues" * tag 'usb-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (250 commits) Revert "USB: serial: ftdi_sio: add Id for Physik Instrumente E-870" usb: musb: gadget: misplaced out of bounds check usb: chipidea: imx: Fix ULPI on imx53 usb: chipidea: imx: Cleanup ci_hdrc_imx_platform_flag usb: chipidea: usbmisc: small clean up usb: chipidea: usbmisc: evdo can be set e/o reset usb: chipidea: usbmisc: evdo is only specific to OTG port USB: serial: ftdi_sio: add Id for Physik Instrumente E-870 usb: dwc3: gadget: never call ->complete() from ->ep_queue() usb: gadget: udc: core: update usb_ep_queue() documentation usb: host: Remove the deprecated ATH79 USB host config options usb: roles: Fix return value check in intel_xhci_usb_probe() USB: gadget: f_midi: fixing a possible double-free in f_midi usb: core: Add USB_QUIRK_DELAY_CTRL_MSG to usbcore quirks usb: core: Copy parameter string correctly and remove superfluous null check USB: announce bcdDevice as well as idVendor, idProduct. USB:fix USB3 devices behind USB3 hubs not resuming at hibernate thaw usb: hub: Reduce warning to notice on power loss USB: serial: ftdi_sio: add support for Harman FirmwareHubEmulator USB: serial: cp210x: add ELDAT Easywave RX09 id ...
Diffstat (limited to 'drivers/usb/dwc2/core.c')
-rw-r--r--drivers/usb/dwc2/core.c395
1 files changed, 291 insertions, 104 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 82a7d98c3436..18a0a1771289 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -64,10 +64,11 @@
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
+
+ dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup global regs */
gr = &hsotg->gr_backup;
@@ -78,10 +79,11 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
+ gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
+ gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
+ gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
gr->valid = true;
return 0;
@@ -94,10 +96,9 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
@@ -117,26 +118,27 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
- dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
+ dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1);
+ dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG);
+ dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL);
return 0;
}
/**
- * dwc2_exit_hibernation() - Exit controller from Partial Power Down.
+ * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
return -ENOTSUPP;
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
@@ -167,7 +169,7 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
return ret;
}
} else {
- ret = dwc2_restore_device_registers(hsotg);
+ ret = dwc2_restore_device_registers(hsotg, 0);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
@@ -180,16 +182,16 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
}
/**
- * dwc2_enter_hibernation() - Put controller in Partial Power Down.
+ * dwc2_enter_partial_power_down() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (!hsotg->params.power_down)
return -ENOTSUPP;
/* Backup all registers */
@@ -218,7 +220,7 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
/*
* Clear any pending interrupts since dwc2 will not be able to
- * clear them after entering hibernation.
+ * clear them after entering partial_power_down.
*/
dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
@@ -240,6 +242,142 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_restore_essential_regs() - Restore essiential regs of core.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rmode: Restore mode, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
+ int is_host)
+{
+ u32 pcgcctl;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_dregs_backup *dr;
+ struct dwc2_hregs_backup *hr;
+
+ gr = &hsotg->gr_backup;
+ dr = &hsotg->dr_backup;
+ hr = &hsotg->hr_backup;
+
+ dev_dbg(hsotg->dev, "%s: restoring essential regs\n", __func__);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl = (gr->pcgcctl & 0xffffc000);
+ /* If High Speed */
+ if (is_host) {
+ if (!(pcgcctl & PCGCTL_P2HD_PRT_SPD_MASK))
+ pcgcctl |= BIT(17);
+ } else {
+ if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK))
+ pcgcctl |= BIT(17);
+ }
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+
+ /* Umnask global Interrupt in GAHBCFG and restore it */
+ dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Unmask restore done interrupt */
+ dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK);
+
+ /* Restore GUSBCFG and HCFG/DCFG */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+
+ if (is_host) {
+ dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+ if (rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ } else {
+ dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ if (!rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ }
+}
+
+/**
+ * dwc2_hib_restore_common() - Common part of restore routine.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int is_host)
+{
+ u32 gpwrdn;
+
+ /* Switch-on voltage to the core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable restore from PMU */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(50);
+
+ if (!is_host && rem_wakeup)
+ udelay(70);
+
+ /* Deassert reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Set Restore Essential Regs bit in PCGCCTL register */
+ dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host);
+
+ /*
+ * Wait For Restore_done Interrupt. This mechanism of polling the
+ * interrupt is introduced to avoid any possible race conditions
+ */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
+ 20000)) {
+ dev_dbg(hsotg->dev,
+ "%s: Restore Done wan't generated here\n",
+ __func__);
+ } else {
+ dev_dbg(hsotg->dev, "restore done generated here\n");
+ }
+}
+
+/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
@@ -311,13 +449,50 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
}
/*
+ * dwc2_enter_hibernation() - Common function to enter hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)
+{
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION)
+ return -ENOTSUPP;
+
+ if (is_host)
+ return dwc2_host_enter_hibernation(hsotg);
+ else
+ return dwc2_gadget_enter_hibernation(hsotg);
+}
+
+/*
+ * dwc2_exit_hibernation() - Common function to exit from hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @reset: Enabled in case of restore with reset.
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset, int is_host)
+{
+ if (is_host)
+ return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset);
+ else
+ return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset);
+}
+
+/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset;
- int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -346,29 +521,19 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! Soft Reset GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (greset & GRSTCTL_CSFTRST);
+
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
+ __func__);
+ return -EBUSY;
+ }
/* Wait for AHB master IDLE state */
- count = 0;
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! AHB Idle GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (!(greset & GRSTCTL_AHBIDLE));
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n",
+ __func__);
+ return -EBUSY;
+ }
if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
@@ -376,14 +541,14 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
return 0;
}
-/*
- * Force the mode of the controller.
+/**
+ * dwc2_force_mode() - Force the mode of the controller.
*
* Forcing the mode is needed for two cases:
*
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
* controller to stay in a particular mode regardless of ID pin
- * changes. We do this usually after a core reset.
+ * changes. We do this once during probe.
*
* 2) During probe we want to read reset values of the hw
* configuration registers that are only available in either host or
@@ -400,7 +565,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
* the filter is configured and enabled. We poll the current mode of
* the controller to account for this delay.
*/
-static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
u32 gusbcfg;
u32 set;
@@ -412,17 +577,17 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* Force mode has no effect if the hardware is not OTG.
*/
if (!dwc2_hw_is_otg(hsotg))
- return false;
+ return;
/*
* If dr_mode is either peripheral or host only, there is no
* need to ever force the mode to the opposite mode.
*/
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
- return false;
+ return;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
- return false;
+ return;
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
@@ -434,7 +599,7 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
dwc2_wait_for_mode(hsotg, host);
- return true;
+ return;
}
/**
@@ -446,10 +611,15 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/
-void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
+ if (!dwc2_hw_is_otg(hsotg))
+ return;
+
+ dev_dbg(hsotg->dev, "Clearing force mode bits\n");
+
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
@@ -464,16 +634,13 @@ void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
*/
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
- bool ret;
-
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
- ret = dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
*/
- if (!ret)
+ if (!dwc2_hw_is_otg(hsotg))
msleep(50);
break;
@@ -491,22 +658,17 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
}
/*
- * Do core a soft reset of the core. Be careful with this because it
- * resets all the internal state machines of the core.
- *
- * Additionally this will apply force mode as per the hsotg->dr_mode
- * parameter.
+ * dwc2_enable_acg - enable active clock gating feature
*/
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg)
{
- int retval;
+ if (hsotg->params.acg_enable) {
+ u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
- retval = dwc2_core_reset(hsotg, false);
- if (retval)
- return retval;
-
- dwc2_force_dr_mode(hsotg);
- return 0;
+ dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n");
+ pcgcctl1 |= PCGCCTL1_GATEEN;
+ dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1);
+ }
}
/**
@@ -683,25 +845,21 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_TXFFLSH;
greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev,
- "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
- __func__, greset,
- dwc2_readl(hsotg->regs + GNPTXSTS));
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_TXFFLSH);
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
@@ -715,43 +873,26 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_RXFFLSH;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n",
- __func__, greset);
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_RXFFLSH);
+ /* Wait for RxFIFO flush done */
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
-/*
- * Forces either host or device mode if the controller is not
- * currently in that mode.
- *
- * Returns true if the mode was forced.
- */
-bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
-{
- if (host && dwc2_is_host_mode(hsotg))
- return false;
- else if (!host && dwc2_is_device_mode(hsotg))
- return false;
-
- return dwc2_force_mode(hsotg, host);
-}
-
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
@@ -825,6 +966,52 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
}
+/**
+ * dwc2_hsotg_wait_bit_set - Waits for bit to be set.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (dwc2_readl(hsotg->regs + offset) & mask)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (!(dwc2_readl(hsotg->regs + offset) & mask))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
OpenPOWER on IntegriCloud