diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-03-23 13:33:09 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-03-23 13:33:09 +0100 |
commit | 6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6 (patch) | |
tree | c2552531d33b5b4aa333015b5225be5c1e100d63 /drivers/usb | |
parent | 6555ad13a01952c16485c82a52ad1f3e07e34b3a (diff) | |
parent | aaeab02ddcc830e31c33cdb72a3c117b2d499ae2 (diff) | |
download | blackbird-op-linux-6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6.tar.gz blackbird-op-linux-6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6.zip |
Merge tag 'usb-for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
Felipe writes:
usb: changes for v4.17 merge window
Quite a lot happened in this cycle, with a total of 95 non-merge
commits. The most interesting parts are listed below:
Synopsys has been adding better support for USB 3.1 to dwc3. The same
series also sets g_mass_storage's max speed to SSP.
Roger Quadros (TI) added support for dual-role using the OTG block
available in some dwc3 implementations, this makes sure that AM437x
can swap roles in runtime.
We have a new SoC supported in dwc3 now - Amlogic Meson GX - thanks to
the work of Martin Blumenstingl.
We also have a ton of changes in dwc2 (51% of all changes, in
fact). The most interesting part there is the support for
Hibernation (a Synopsys PM feature).
Apart from these, we have our regular set of non-critical fixes all
over the place.
Diffstat (limited to 'drivers/usb')
34 files changed, 2400 insertions, 786 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"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index cd77af3b1565..d83be5651f87 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -217,7 +217,7 @@ struct dwc2_hsotg_ep { unsigned char dir_in; unsigned char index; unsigned char mc; - unsigned char interval; + u16 interval; unsigned int halted:1; unsigned int periodic:1; @@ -408,7 +408,7 @@ enum dwc2_ep0_state { * @ahbcfg: This field allows the default value of the GAHBCFG * register to be overridden * -1 - GAHBCFG value will be set to 0x06 - * (INCR4, default) + * (INCR, default) * all others - GAHBCFG value will be overridden with * this value * Not all bits can be controlled like this, the @@ -421,12 +421,26 @@ enum dwc2_ep0_state { * case. * 0 - No (default) * 1 - Yes - * @hibernation: Specifies whether the controller support hibernation. - * If hibernation is enabled, the controller will enter - * hibernation in both peripheral and host mode when + * @power_down: Specifies whether the controller support power_down. + * If power_down is enabled, the controller will enter + * power_down in both peripheral and host mode when * needed. * 0 - No (default) + * 1 - Partial power down + * 2 - Hibernation + * @lpm: Enable LPM support. + * 0 - No * 1 - Yes + * @lpm_clock_gating: Enable core PHY clock gating. + * 0 - No + * 1 - Yes + * @besl: Enable LPM Errata support. + * 0 - No + * 1 - Yes + * @hird_threshold_en: HIRD or HIRD Threshold enable. + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO * register. * 0 - Deactivate the transceiver (default) @@ -479,12 +493,23 @@ struct dwc2_core_params { bool enable_dynamic_fifo; bool en_multiple_tx_fifo; bool i2c_enable; + bool acg_enable; bool ulpi_fs_ls; bool ts_dline; bool reload_ctl; bool uframe_sched; bool external_id_pin_ctl; - bool hibernation; + + int power_down; +#define DWC2_POWER_DOWN_PARAM_NONE 0 +#define DWC2_POWER_DOWN_PARAM_PARTIAL 1 +#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2 + + bool lpm; + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; + u8 hird_threshold; bool activate_stm_fs_transceiver; u16 max_packet_count; u32 max_transfer_size; @@ -560,6 +585,7 @@ struct dwc2_core_params { * 2 - FS pins shared with UTMI+ pins * 3 - FS pins shared with ULPI pins * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @hibernation Is hibernation enabled? * @utmi_phy_data_width UTMI+ PHY data width * 0 - 8 bits * 1 - 16 bits @@ -587,12 +613,15 @@ struct dwc2_hw_params { unsigned hs_phy_type:2; unsigned fs_phy_type:2; unsigned i2c_enable:1; + unsigned acg_enable:1; unsigned num_dev_ep:4; unsigned num_dev_in_eps : 4; unsigned num_dev_perio_in_ep:4; unsigned total_fifo_size:16; unsigned power_optimized:1; + unsigned hibernation:1; unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; u32 snpsid; u32 dev_ep_dirs; u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; @@ -611,9 +640,8 @@ struct dwc2_hw_params { * @grxfsiz: Backup of GRXFSIZ register * @gnptxfsiz: Backup of GNPTXFSIZ register * @gi2cctl: Backup of GI2CCTL register - * @hptxfsiz: Backup of HPTXFSIZ register + * @glpmcfg: Backup of GLPMCFG register * @gdfifocfg: Backup of GDFIFOCFG register - * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint * @gpwrdn: Backup of GPWRDN register */ struct dwc2_gregs_backup { @@ -624,10 +652,10 @@ struct dwc2_gregs_backup { u32 grxfsiz; u32 gnptxfsiz; u32 gi2cctl; - u32 hptxfsiz; + u32 glpmcfg; u32 pcgcctl; + u32 pcgcctl1; u32 gdfifocfg; - u32 dtxfsiz[MAX_EPS_CHANNELS]; u32 gpwrdn; bool valid; }; @@ -646,6 +674,7 @@ struct dwc2_gregs_backup { * @doepctl: Backup of DOEPCTL register * @doeptsiz: Backup of DOEPTSIZ register * @doepdma: Backup of DOEPDMA register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint */ struct dwc2_dregs_backup { u32 dcfg; @@ -659,6 +688,7 @@ struct dwc2_dregs_backup { u32 doepctl[MAX_EPS_CHANNELS]; u32 doeptsiz[MAX_EPS_CHANNELS]; u32 doepdma[MAX_EPS_CHANNELS]; + u32 dtxfsiz[MAX_EPS_CHANNELS]; bool valid; }; @@ -670,6 +700,7 @@ struct dwc2_dregs_backup { * @hcintmsk: Backup of HCINTMSK register * @hptr0: Backup of HPTR0 register * @hfir: Backup of HFIR register + * @hptxfsiz: Backup of HPTXFSIZ register */ struct dwc2_hregs_backup { u32 hcfg; @@ -677,6 +708,7 @@ struct dwc2_hregs_backup { u32 hcintmsk[MAX_EPS_CHANNELS]; u32 hprt0; u32 hfir; + u32 hptxfsiz; bool valid; }; @@ -780,12 +812,14 @@ struct dwc2_hregs_backup { * @hcd_enabled Host mode sub-driver initialization indicator. * @gadget_enabled Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled Status of low-level hardware resources. + * @hibernated: True if core is hibernated * @phy: The otg phy transceiver structure for phy control. * @uphy: The otg phy transceiver structure for old USB phy * control. * @plat: The platform specific configuration data. This can be * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies + * @vbus_supply: Regulator supplying vbus. * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd @@ -897,6 +931,8 @@ struct dwc2_hregs_backup { * @ctrl_req: Request for EP0 control packets. * @ep0_state: EP0 control transfers state * @test_mode: USB test mode requested by the host + * @remote_wakeup_allowed: True if device is allowed to wake-up host by + * remote-wakeup signalling * @setup_desc_dma: EP0 setup stage desc chain DMA address * @setup_desc: EP0 setup stage desc chain pointer * @ctrl_in_desc_dma: EP0 IN data phase desc chain DMA address @@ -917,11 +953,13 @@ struct dwc2_hsotg { unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; + unsigned int hibernated:1; struct phy *phy; struct usb_phy *uphy; struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; + struct regulator *vbus_supply; u32 phyif; spinlock_t lock; @@ -947,6 +985,7 @@ struct dwc2_hsotg { /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_80a 0x4f54280a #define DWC2_CORE_REV_2_90a 0x4f54290a #define DWC2_CORE_REV_2_91a 0x4f54291a #define DWC2_CORE_REV_2_92a 0x4f54292a @@ -956,6 +995,11 @@ struct dwc2_hsotg { #define DWC2_FS_IOT_REV_1_00a 0x5531100a #define DWC2_HS_IOT_REV_1_00a 0x5532100a + /* DWC OTG HW Core ID */ +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { u32 d32; @@ -1016,24 +1060,6 @@ struct dwc2_hsotg { struct kmem_cache *desc_gen_cache; struct kmem_cache *desc_hsisoc_cache; -#ifdef DEBUG - u32 frrem_samples; - u64 frrem_accum; - - u32 hfnum_7_samples_a; - u64 hfnum_7_frrem_accum_a; - u32 hfnum_0_samples_a; - u64 hfnum_0_frrem_accum_a; - u32 hfnum_other_samples_a; - u64 hfnum_other_frrem_accum_a; - - u32 hfnum_7_samples_b; - u64 hfnum_7_frrem_accum_b; - u32 hfnum_0_samples_b; - u64 hfnum_0_frrem_accum_b; - u32 hfnum_other_samples_b; - u64 hfnum_other_frrem_accum_b; -#endif #endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */ #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ @@ -1062,6 +1088,7 @@ struct dwc2_hsotg { struct usb_gadget gadget; unsigned int enabled:1; unsigned int connected:1; + unsigned int remote_wakeup_allowed:1; struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ @@ -1106,12 +1133,13 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg) * and the DWC_otg controller */ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait); -int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg); -int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); -int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); +int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host); +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, + int reset, int is_host); -bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host); -void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg); +void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host); void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg); bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg); @@ -1128,6 +1156,13 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg); void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd); void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd); +void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, + int is_host); +int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg); + +void dwc2_enable_acg(struct dwc2_hsotg *hsotg); + /* This function should be called on every hardware interrupt. */ irqreturn_t dwc2_handle_common_intr(int irq, void *dev); @@ -1137,6 +1172,11 @@ extern const struct of_device_id dwc2_of_match_table[]; int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); +/* Common polling functions */ +int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, + u32 timeout); +int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, + u32 timeout); /* Parameters */ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); int dwc2_init_params(struct dwc2_hsotg *hsotg); @@ -1180,7 +1220,7 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg); int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg); int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2); int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); -int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); +int dwc2_gadget_init(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); @@ -1188,10 +1228,14 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); #define dwc2_is_device_connected(hsotg) (hsotg->connected) int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg); -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup); +int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg); +int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); +void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1199,7 +1243,7 @@ static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2) { return 0; } static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2) { return 0; } -static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) +static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} @@ -1211,7 +1255,13 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, #define dwc2_is_device_connected(hsotg) (0) static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) { return 0; } -static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, + int remote_wakeup) +{ return 0; } +static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) { return 0; } static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } @@ -1219,6 +1269,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } +static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1227,8 +1278,12 @@ int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us); void dwc2_hcd_connect(struct dwc2_hsotg *hsotg); void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); +int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); +int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); +int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset); #else static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } @@ -1239,12 +1294,19 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} +static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) +{ return 0; } static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) { return 0; } +static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) +{ return 0; } #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index ab3fa1630853..2982a155734d 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -321,10 +321,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { if (hsotg->lx_state == DWC2_L2) { - ret = dwc2_exit_hibernation(hsotg, true); + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, - "exit hibernation failed\n"); + "exit power_down failed\n"); } /* @@ -335,6 +335,57 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) } } +/** + * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state + * + * @hsotg: Programming view of DWC_otg controller + * + */ +static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) +{ + u32 glpmcfg; + u32 i = 0; + + if (hsotg->lx_state != DWC2_L1) { + dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n"); + return; + } + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "Exit from L1 state\n"); + glpmcfg &= ~GLPMCFG_ENBLSLPM; + glpmcfg &= ~GLPMCFG_HIRD_THRES_EN; + dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG); + + do { + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK | + GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS))) + break; + + udelay(1); + } while (++i < 200); + + if (i == 200) { + dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n"); + return; + } + dwc2_gadget_init_lpm(hsotg); + } else { + /* TODO */ + dev_err(hsotg->dev, "Host side LPM is not supported.\n"); + return; + } + + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; + + /* Inform gadget to exit from L1 */ + call_gadget(hsotg, resume); +} + /* * This interrupt indicates that the DWC_otg controller has detected a * resume or remote wakeup sequence. If the DWC_otg controller is in @@ -352,6 +403,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); + if (hsotg->lx_state == DWC2_L1) { + dwc2_wakeup_from_lpm_l1(hsotg); + return; + } + if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dwc2_readl(hsotg->regs + DSTS)); @@ -361,16 +417,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; dwc2_writel(dctl, hsotg->regs + DCTL); - ret = dwc2_exit_hibernation(hsotg, true); + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit hibernation failed\n"); + dev_err(hsotg->dev, "exit power_down failed\n"); call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; } else { - if (hsotg->params.hibernation) + if (hsotg->params.power_down) return; if (hsotg->lx_state != DWC2_L1) { @@ -428,32 +484,44 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) * state is active */ dsts = dwc2_readl(hsotg->regs + DSTS); - dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts); + dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts); dev_dbg(hsotg->dev, - "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", + "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n", !!(dsts & DSTS_SUSPSTS), - hsotg->hw_params.power_optimized); - if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { - /* Ignore suspend request before enumeration */ - if (!dwc2_is_device_connected(hsotg)) { - dev_dbg(hsotg->dev, - "ignore suspend request before enumeration\n"); - return; + hsotg->hw_params.power_optimized, + hsotg->hw_params.hibernation); + + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + return; + } + if (dsts & DSTS_SUSPSTS) { + if (hsotg->hw_params.power_optimized) { + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "%s: enter partial_power_down failed\n", + __func__); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); } - ret = dwc2_enter_hibernation(hsotg); - if (ret) { - if (ret != -ENOTSUPP) + if (hsotg->hw_params.hibernation) { + ret = dwc2_enter_hibernation(hsotg, 0); + if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, - "enter hibernation failed\n"); - goto skip_power_saving; + "%s: enter hibernation failed\n", + __func__); } - - udelay(100); - - /* Ask phy to be suspended */ - if (!IS_ERR_OR_NULL(hsotg->uphy)) - usb_phy_set_suspend(hsotg->uphy, true); skip_power_saving: /* * Change to L2 (suspend) state before releasing @@ -479,10 +547,75 @@ skip_power_saving: } } +/** + * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler + * + * @hsotg: Programming view of DWC_otg controller + * + */ +static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg) +{ + u32 glpmcfg; + u32 pcgcctl; + u32 hird; + u32 hird_thres; + u32 hird_thres_en; + u32 enslpm; + + /* Clear interrupt */ + dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS); + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (!(glpmcfg & GLPMCFG_LPMCAP)) { + dev_err(hsotg->dev, "Unexpected LPM interrupt\n"); + return; + } + + hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT; + hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK & + ~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT; + hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN; + enslpm = glpmcfg & GLPMCFG_ENBLSLPM; + + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en); + + if (hird_thres_en && hird >= hird_thres) { + dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n"); + } else if (enslpm) { + dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n"); + } else { + dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n"); + + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_ENBL_SLEEP_GATING; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + } + /** + * Examine prt_sleep_sts after TL1TokenTetry period max (10 us) + */ + udelay(10); + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (glpmcfg & GLPMCFG_SLPSTS) { + /* Save the current state */ + hsotg->lx_state = DWC2_L1; + dev_dbg(hsotg->dev, + "Core is in L1 sleep glpmcfg=%08x\n", glpmcfg); + + /* Inform gadget that we are in L1 state */ + call_gadget(hsotg, suspend); + } + } +} + #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \ GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \ GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \ - GINTSTS_USBSUSP | GINTSTS_PRTINT) + GINTSTS_USBSUSP | GINTSTS_PRTINT | \ + GINTSTS_LPMTRANRCVD) /* * This function returns the Core Interrupt register @@ -510,6 +643,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) } /* + * GPWRDN interrupt handler. + * + * The GPWRDN interrupts are those that occur in both Host and + * Device mode while core is in hibernated state. + */ +static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) +{ + u32 gpwrdn; + int linestate; + + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + /* clear all interrupt */ + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT; + dev_dbg(hsotg->dev, + "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__, + gpwrdn); + + if ((gpwrdn & GPWRDN_DISCONN_DET) && + (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) { + u32 gpwrdn_tmp; + + dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); + + /* Switch-on voltage to the core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Reset core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable Power Down Clamp */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Deassert reset core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp |= GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable PMU interrupt */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + + /* De-assert Wakeup Logic */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + + hsotg->hibernated = 0; + + if (gpwrdn & GPWRDN_IDSTS) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); + } else { + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } + } + + if ((gpwrdn & GPWRDN_LNSTSCHG) && + (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) { + dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__); + if (hsotg->hw_params.hibernation && + hsotg->hibernated) { + if (gpwrdn & GPWRDN_IDSTS) { + dwc2_exit_hibernation(hsotg, 0, 0, 0); + call_gadget(hsotg, resume); + } else { + dwc2_exit_hibernation(hsotg, 1, 0, 1); + } + } + } + if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) { + dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__); + if (!linestate && (gpwrdn & GPWRDN_BSESSVLD)) + dwc2_exit_hibernation(hsotg, 0, 1, 0); + } + if ((gpwrdn & GPWRDN_STS_CHGINT) && + (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) { + dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); + if (hsotg->hw_params.hibernation && + hsotg->hibernated) { + if (gpwrdn & GPWRDN_IDSTS) { + dwc2_exit_hibernation(hsotg, 0, 0, 0); + call_gadget(hsotg, resume); + } else { + dwc2_exit_hibernation(hsotg, 1, 0, 1); + } + } + } +} + +/* * Common interrupt handler * * The common interrupts are those that occur in both Host and Device mode. @@ -539,6 +782,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) if (gintsts & ~GINTSTS_PRTINT) retval = IRQ_HANDLED; + /* In case of hibernated state gintsts must not work */ + if (hsotg->hibernated) { + dwc2_handle_gpwrdn_intr(hsotg); + retval = IRQ_HANDLED; + goto out; + } + if (gintsts & GINTSTS_MODEMIS) dwc2_handle_mode_mismatch_intr(hsotg); if (gintsts & GINTSTS_OTGINT) @@ -553,6 +803,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) dwc2_handle_wakeup_detected_intr(hsotg); if (gintsts & GINTSTS_USBSUSP) dwc2_handle_usb_suspend_intr(hsotg); + if (gintsts & GINTSTS_LPMTRANRCVD) + dwc2_handle_lpm_intr(hsotg); if (gintsts & GINTSTS_PRTINT) { /* diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 5e0d7f2bd2af..58c691f882a8 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -718,7 +718,12 @@ static int params_show(struct seq_file *seq, void *v) print_param_hex(seq, p, ahbcfg); print_param(seq, p, uframe_sched); print_param(seq, p, external_id_pin_ctl); - print_param(seq, p, hibernation); + print_param(seq, p, power_down); + print_param(seq, p, lpm); + print_param(seq, p, lpm_clock_gating); + print_param(seq, p, besl); + print_param(seq, p, hird_threshold_en); + print_param(seq, p, hird_threshold); print_param(seq, p, host_dma); print_param(seq, p, g_dma); print_param(seq, p, g_dma_desc); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5bcad1d869b5..6c32bf26e48e 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -47,12 +47,12 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) return container_of(gadget, struct dwc2_hsotg, gadget); } -static inline void __orr32(void __iomem *ptr, u32 val) +static inline void dwc2_set_bit(void __iomem *ptr, u32 val) { dwc2_writel(dwc2_readl(ptr) | val, ptr); } -static inline void __bic32(void __iomem *ptr, u32 val) +static inline void dwc2_clear_bit(void __iomem *ptr, u32 val) { dwc2_writel(dwc2_readl(ptr) & ~val, ptr); } @@ -116,10 +116,10 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) { hs_ep->target_frame += hs_ep->interval; if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { - hs_ep->frame_overrun = 1; + hs_ep->frame_overrun = true; hs_ep->target_frame &= DSTS_SOFFN_LIMIT; } else { - hs_ep->frame_overrun = 0; + hs_ep->frame_overrun = false; } } @@ -252,6 +252,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) unsigned int ep; unsigned int addr; int timeout; + u32 val; u32 *txfsz = hsotg->params.g_tx_fifo_size; @@ -1296,8 +1297,8 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->zero, req->short_not_ok); /* Prevent new request submission when controller is suspended */ - if (hs->lx_state == DWC2_L2) { - dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + if (hs->lx_state != DWC2_L0) { + dev_dbg(hs->dev, "%s: submit request only in active state\n", __func__); return -EAGAIN; } @@ -1639,6 +1640,10 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, switch (recip) { case USB_RECIP_DEVICE: switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + hsotg->remote_wakeup_allowed = 1; + break; + case USB_DEVICE_TEST_MODE: if ((wIndex & 0xff) != 0) return -EINVAL; @@ -2495,30 +2500,13 @@ bad_mps: */ static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) { - int timeout; - int val; - dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, hsotg->regs + GRSTCTL); /* wait until the fifo is flushed */ - timeout = 100; - - while (1) { - val = dwc2_readl(hsotg->regs + GRSTCTL); - - if ((val & (GRSTCTL_TXFFLSH)) == 0) - break; - - if (--timeout == 0) { - dev_err(hsotg->dev, - "%s: timeout flushing fifo (GRSTCTL=%08x)\n", - __func__, val); - break; - } - - udelay(1); - } + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100)) + dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n", + __func__); } /** @@ -3253,7 +3241,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_init_fifo(hsotg); if (!is_usb_reset) - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); dcfg |= DCFG_EPMISCNT(1); @@ -3282,7 +3270,8 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_USBRST | GINTSTS_RESETDET | GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT; + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_LPMTRANRCVD; if (!using_desc_dma(hsotg)) intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; @@ -3294,12 +3283,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, if (using_dma(hsotg)) { dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | - (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT), + hsotg->params.ahbcfg, hsotg->regs + GAHBCFG); /* Set DDMA mode support in the core if needed */ if (using_desc_dma(hsotg)) - __orr32(hsotg->regs + DCFG, DCFG_DESCDMA_EN); + dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN); } else { dwc2_writel(((hsotg->dedicated_fifos) ? @@ -3332,7 +3321,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* Enable BNA interrupt for DDMA */ if (using_desc_dma(hsotg)) - __orr32(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); + dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); dwc2_writel(0, hsotg->regs + DAINTMSK); @@ -3356,9 +3345,9 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1); if (!is_usb_reset) { - __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); udelay(10); /* see openiboot */ - __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); } dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL)); @@ -3385,7 +3374,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; if (!is_usb_reset) val |= DCTL_SFTDISCON; - __orr32(hsotg->regs + DCTL, val); + dwc2_set_bit(hsotg->regs + DCTL, val); + + /* configure the core to support LPM */ + dwc2_gadget_init_lpm(hsotg); /* must be at-least 3ms to allow bus to see disconnect */ mdelay(3); @@ -3402,13 +3394,13 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); } void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); } /** @@ -3428,14 +3420,21 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) { struct dwc2_hsotg_ep *hs_ep; u32 epctrl; + u32 daintmsk; u32 idx; dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n"); + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_in[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; @@ -3464,16 +3463,24 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) { u32 gintsts; u32 gintmsk; + u32 daintmsk; u32 epctrl; struct dwc2_hsotg_ep *hs_ep; int idx; dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { /* Unmask GOUTNAKEFF interrupt */ gintmsk = dwc2_readl(hsotg->regs + GINTMSK); @@ -3481,8 +3488,10 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) dwc2_writel(gintmsk, hsotg->regs + GINTMSK); gintsts = dwc2_readl(hsotg->regs + GINTSTS); - if (!(gintsts & GINTSTS_GOUTNAKEFF)) - __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + if (!(gintsts & GINTSTS_GOUTNAKEFF)) { + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); + break; + } } } @@ -3522,7 +3531,7 @@ irq_retry: /* This event must be used only if controller is suspended */ if (hsotg->lx_state == DWC2_L2) { - dwc2_exit_hibernation(hsotg, true); + dwc2_exit_partial_power_down(hsotg, true); hsotg->lx_state = DWC2_L0; } } @@ -3541,7 +3550,7 @@ irq_retry: dwc2_hsotg_disconnect(hsotg); /* Reset device address to zero */ - __bic32(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); + dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); if (usb_status & GOTGCTL_BSESVLD && connected) dwc2_hsotg_core_init_disconnected(hsotg, true); @@ -3627,8 +3636,11 @@ irq_retry: u8 idx; u32 epctrl; u32 gintmsk; + u32 daintmsk; struct dwc2_hsotg_ep *hs_ep; + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; /* Mask this interrupt */ gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_GOUTNAKEFF; @@ -3637,9 +3649,13 @@ irq_retry: dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) { + if (epctrl & DXEPCTL_EPENA) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx)); @@ -3652,7 +3668,7 @@ irq_retry: if (gintsts & GINTSTS_GINNAKEFF) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); dwc2_hsotg_dump(hsotg); } @@ -3676,20 +3692,6 @@ irq_retry: return IRQ_HANDLED; } -static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, - u32 bit, u32 timeout) -{ - u32 i; - - for (i = 0; i < timeout; i++) { - if (dwc2_readl(hs_otg->regs + reg) & bit) - return 0; - udelay(1); - } - - return -ETIMEDOUT; -} - static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *hs_ep) { @@ -3706,7 +3708,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, if (hs_ep->dir_in) { if (hsotg->dedicated_fifos || hs_ep->periodic) { - __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); + dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_INEPNAKEFF, 100)) @@ -3714,7 +3716,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DIEPINT.NAKEFF\n", __func__); } else { - __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_GINNAKEFF, 100)) @@ -3724,7 +3726,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, } } else { if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF)) - __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); /* Wait for global nak to take effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, @@ -3734,7 +3736,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, } /* Disable ep */ - __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); /* Wait for ep to be disabled */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100)) @@ -3742,7 +3744,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DOEPCTL.EPDisable\n", __func__); /* Clear EPDISBLD interrupt */ - __orr32(hsotg->regs + epint_reg, DXEPINT_EPDISBLD); + dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD); if (hs_ep->dir_in) { unsigned short fifo_index; @@ -3757,11 +3759,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ if (!hsotg->dedicated_fifos && !hs_ep->periodic) - __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); } else { /* Remove global NAKs */ - __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK); } } @@ -4183,7 +4185,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_writel(0, hsotg->regs + DAINTMSK); /* Be in disconnected state until gadget is registered */ - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); /* setup fifos */ @@ -4205,7 +4207,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); if (using_dma(hsotg)) - __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); + dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); } /** @@ -4352,6 +4354,8 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) if (is_on) { hsotg->enabled = 1; dwc2_hsotg_core_init_disconnected(hsotg, false); + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); } else { dwc2_hsotg_core_disconnect(hsotg); @@ -4374,18 +4378,21 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); /* - * If controller is hibernated, it must exit from hibernation + * If controller is hibernated, it must exit from power_down * before being initialized / de-initialized */ if (hsotg->lx_state == DWC2_L2) - dwc2_exit_hibernation(hsotg, false); + dwc2_exit_partial_power_down(hsotg, false); if (is_active) { hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_hsotg_core_init_disconnected(hsotg, false); - if (hsotg->enabled) + if (hsotg->enabled) { + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); + } } else { dwc2_hsotg_core_disconnect(hsotg); dwc2_hsotg_disconnect(hsotg); @@ -4606,9 +4613,8 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) /** * dwc2_gadget_init - init function for gadget * @dwc2: The data structure for the DWC2 driver. - * @irq: The IRQ number for the controller. */ -int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) +int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { struct device *dev = hsotg->dev; int epnum; @@ -4622,6 +4628,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); + hsotg->remote_wakeup_allowed = 0; + + if (hsotg->params.lpm) + hsotg->gadget.lpm_capable = true; + if (hsotg->dr_mode == USB_DR_MODE_OTG) hsotg->gadget.is_otg = 1; else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) @@ -4649,8 +4660,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) return ret; } - ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); + ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq, + IRQF_SHARED, dev_name(hsotg->dev), hsotg); if (ret < 0) { dev_err(dev, "cannot claim IRQ for gadget\n"); return ret; @@ -4751,8 +4762,11 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); dwc2_hsotg_core_init_disconnected(hsotg, false); - if (hsotg->enabled) + if (hsotg->enabled) { + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); + } spin_unlock_irqrestore(&hsotg->lock, flags); } @@ -4806,6 +4820,7 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i)); dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i)); + dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); } dr->valid = true; return 0; @@ -4817,11 +4832,13 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) * if controller power were disabled. * * @hsotg: Programming view of the DWC_otg controller + * @remote_wakeup: Indicates whether resume is initiated by Device or Host. + * + * Return: 0 if successful, negative error code otherwise */ -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) { struct dwc2_dregs_backup *dr; - u32 dctl; int i; dev_dbg(hsotg->dev, "%s\n", __func__); @@ -4835,28 +4852,240 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) } dr->valid = false; - dwc2_writel(dr->dcfg, hsotg->regs + DCFG); - dwc2_writel(dr->dctl, hsotg->regs + DCTL); + if (!remote_wakeup) + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK); dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK); dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Restore IN EPs */ - dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); - + dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + /** WA for enabled EPx's IN in DDMA mode. On entering to + * hibernation wrong value read and saved from DIEPDMAx, + * as result BNA interrupt asserted on hibernation exit + * by restoring from saved area. + */ + if (hsotg->params.g_dma_desc && + (dr->diepctl[i] & DXEPCTL_EPENA)) + dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma; + dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); /* Restore OUT EPs */ - dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + /* WA for enabled EPx's OUT in DDMA mode. On entering to + * hibernation wrong value read and saved from DOEPDMAx, + * as result BNA interrupt asserted on hibernation exit + * by restoring from saved area. + */ + if (hsotg->params.g_dma_desc && + (dr->doepctl[i] & DXEPCTL_EPENA)) + dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma; dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); } - /* Set the Power-On Programming done bit */ - dctl = dwc2_readl(hsotg->regs + DCTL); - dctl |= DCTL_PWRONPRGDONE; - dwc2_writel(dctl, hsotg->regs + DCTL); - return 0; } + +/** + * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode + * + * @hsotg: Programming view of DWC_otg controller + * + */ +void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) +{ + u32 val; + + if (!hsotg->params.lpm) + return; + + val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES; + val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0; + val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; + val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; + val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + dwc2_writel(val, hsotg->regs + GLPMCFG); + dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs + + GLPMCFG)); +} + +/** + * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return non-zero if failed to enter to hibernation. + */ +int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 gpwrdn; + int ret = 0; + + /* Change to L2(suspend) state */ + hsotg->lx_state = DWC2_L2; + dev_dbg(hsotg->dev, "Start of hibernation completed\n"); + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + + gpwrdn = GPWRDN_PWRDNRSTN; + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Set flag to indicate that we are in hibernation */ + hsotg->hibernated = 1; + + /* Enable interrupts from wake up logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Unmask device mode interrupts in GPWRDN */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_RST_DET_MSK; + gpwrdn |= GPWRDN_LNSTSCHG_MSK; + gpwrdn |= GPWRDN_STS_CHGINT_MSK; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Switch off VDD */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Save gpwrdn register for further usage if stschng interrupt */ + hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + dev_dbg(hsotg->dev, "Hibernation completed\n"); + + return ret; +} + +/** + * dwc2_gadget_exit_hibernation() + * This function is for exiting from Device mode hibernation by host initiated + * resume/reset and device initiated remote-wakeup. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Device or Host. + * @param reset: indicates whether resume is initiated by Reset. + * + * Return non-zero if failed to exit from hibernation. + */ +int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) +{ + u32 pcgcctl; + u32 gpwrdn; + u32 dctl; + int ret = 0; + struct dwc2_gregs_backup *gr; + struct dwc2_dregs_backup *dr; + + gr = &hsotg->gr_backup; + dr = &hsotg->dr_backup; + + if (!hsotg->hibernated) { + dev_dbg(hsotg->dev, "Already exited from Hibernation\n"); + return 1; + } + dev_dbg(hsotg->dev, + "%s: called with rem_wakeup = %d reset = %d\n", + __func__, rem_wakeup, reset); + + dwc2_hib_restore_common(hsotg, rem_wakeup, 0); + + if (!reset) { + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + } + + /* De-assert Restore */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + if (!rem_wakeup) { + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + } + + /* Restore GUSBCFG, DCFG and DCTL */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + + /* De-assert Wakeup Logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + + if (rem_wakeup) { + udelay(10); + /* Start Remote Wakeup Signaling */ + dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL); + } else { + udelay(50); + /* Set Device programming done bit */ + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + /* Wait for interrupts which must be cleared */ + mdelay(2); + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Restore global registers */ + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + /* Restore device registers */ + ret = dwc2_restore_device_registers(hsotg, rem_wakeup); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + + if (rem_wakeup) { + mdelay(10); + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + + hsotg->hibernated = 0; + hsotg->lx_state = DWC2_L0; + dev_dbg(hsotg->dev, "Hibernation recovery completes here\n"); + + return ret; +} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index a5d72fcd1603..190f95964000 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -91,6 +91,9 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; + if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm) + intmsk |= GINTSTS_LPMTRANRCVD; + dwc2_writel(intmsk, hsotg->regs + GINTMSK); } @@ -138,7 +141,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after a PHY select */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, @@ -236,7 +239,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after setting the PHY parameters */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, "%s: Reset failed, aborting", __func__); @@ -308,22 +311,10 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) break; } - dev_dbg(hsotg->dev, "host_dma:%d dma_desc_enable:%d\n", - hsotg->params.host_dma, - hsotg->params.dma_desc_enable); - - if (hsotg->params.host_dma) { - if (hsotg->params.dma_desc_enable) - dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n"); - else - dev_dbg(hsotg->dev, "Using Buffer DMA mode\n"); - } else { - dev_dbg(hsotg->dev, "Using Slave mode\n"); - hsotg->params.dma_desc_enable = false; - } - if (hsotg->params.host_dma) ahbcfg |= GAHBCFG_DMA_EN; + else + hsotg->params.dma_desc_enable = false; dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); @@ -365,6 +356,23 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); } +static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg) +{ + hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); + if (IS_ERR(hsotg->vbus_supply)) + return 0; + + return regulator_enable(hsotg->vbus_supply); +} + +static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg) +{ + if (hsotg->vbus_supply) + return regulator_disable(hsotg->vbus_supply); + + return 0; +} + /** * dwc2_enable_host_interrupts() - Enables the Host mode interrupts * @@ -989,6 +997,24 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* + * In buffer DMA or external DMA mode channel can't be halted + * for non-split periodic channels. At the end of the next + * uframe/frame (in the worst case), the core generates a channel + * halted and disables the channel automatically. + */ + if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) || + hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) { + if (!chan->do_split && + (chan->ep_type == USB_ENDPOINT_XFER_ISOC || + chan->ep_type == USB_ENDPOINT_XFER_INT)) { + dev_err(hsotg->dev, "%s() Channel can't be halted\n", + __func__); + return; + } + } + if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS) dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status); @@ -2232,7 +2258,7 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg, * @hsotg: Programming view of the DWC_otg controller * @initial_setup: If true then this is the first init for this instance. */ -static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) +int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { u32 usbcfg, otgctl; int retval; @@ -2261,7 +2287,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) * needed to in order to properly detect various parameters). */ if (!initial_setup) { - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, "%s(): Reset failed, aborting\n", __func__); @@ -2322,10 +2348,22 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) */ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) { - u32 hcfg, hfir, otgctl; + u32 hcfg, hfir, otgctl, usbcfg; dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + /* Restart the Phy Clock */ dwc2_writel(0, hsotg->regs + PCGCTL); @@ -2403,27 +2441,24 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* Halt all channels to put them into a known state */ for (i = 0; i < num_channels; i++) { - int count = 0; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); dev_dbg(hsotg->dev, "%s: Halt channel %d\n", __func__, i); - do { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - if (++count > 1000) { - dev_err(hsotg->dev, - "Unable to clear enable on channel %d\n", - i); - break; - } - udelay(1); - } while (hcchar & HCCHAR_CHENA); + + if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i), + HCCHAR_CHENA, 1000)) { + dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n", + i); + } } } + /* Enable ACG feature in host mode, if supported */ + dwc2_enable_acg(hsotg); + /* Turn on the vbus power */ dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state); if (hsotg->op_state == OTG_STATE_A_HOST) { @@ -3257,6 +3292,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) /* B-Device connector (Device Mode) */ if (gotgctl & GOTGCTL_CONID_B) { + dwc2_vbus_supply_exit(hsotg); /* Wait for switch to device mode */ dev_dbg(hsotg->dev, "connId B\n"); if (hsotg->bus_suspended) { @@ -3290,6 +3326,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work) spin_lock_irqsave(&hsotg->lock, flags); dwc2_hsotg_core_init_disconnected(hsotg, false); spin_unlock_irqrestore(&hsotg->lock, flags); + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); } else { host: @@ -3377,10 +3415,10 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) hsotg->bus_suspended = true; /* - * If hibernation is supported, Phy clock will be suspended + * If power_down is supported, Phy clock will be suspended * after registers are backuped. */ - if (!hsotg->params.hibernation) { + if (!hsotg->params.power_down) { /* Suspend the Phy Clock */ pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl |= PCGCTL_STOPPCLK; @@ -3412,10 +3450,10 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); /* - * If hibernation is supported, Phy clock is already resumed + * If power_down is supported, Phy clock is already resumed * after registers restore. */ - if (!hsotg->params.hibernation) { + if (!hsotg->params.power_down) { pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; dwc2_writel(pcgctl, hsotg->regs + PCGCTL); @@ -3486,8 +3524,12 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - if (hsotg->bus_suspended) - dwc2_port_resume(hsotg); + if (hsotg->bus_suspended) { + if (hsotg->hibernated) + dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + dwc2_port_resume(hsotg); + } break; case USB_PORT_FEAT_POWER: @@ -3695,7 +3737,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (windex != hsotg->otg_port) goto error; - dwc2_port_suspend(hsotg, windex); + if (hsotg->params.power_down == 2) + dwc2_enter_hibernation(hsotg, 1); + else + dwc2_port_suspend(hsotg, windex); break; case USB_PORT_FEAT_POWER: @@ -3707,6 +3752,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; case USB_PORT_FEAT_RESET: + if (hsotg->params.power_down == 2 && + hsotg->hibernated) + dwc2_exit_hibernation(hsotg, 0, 1, 1); hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); @@ -4002,7 +4050,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", (p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); - dwc2_hcd_dump_frrem(hsotg); dwc2_dump_global_registers(hsotg); dwc2_dump_host_registers(hsotg); dev_dbg(hsotg->dev, @@ -4011,75 +4058,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) #endif } -/* - * NOTE: This function will be removed once the peripheral controller code - * is integrated and the driver is stable - */ -void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg) -{ -#ifdef DWC2_DUMP_FRREM - dev_dbg(hsotg->dev, "Frame remaining at SOF:\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->frrem_samples, hsotg->frrem_accum, - hsotg->frrem_samples > 0 ? - hsotg->frrem_accum / hsotg->frrem_samples : 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples, - hsotg->hfnum_7_frrem_accum, - hsotg->hfnum_7_samples > 0 ? - hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples, - hsotg->hfnum_0_frrem_accum, - hsotg->hfnum_0_samples > 0 ? - hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples, - hsotg->hfnum_other_frrem_accum, - hsotg->hfnum_other_samples > 0 ? - hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples : - 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a, - hsotg->hfnum_7_samples_a > 0 ? - hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a, - hsotg->hfnum_0_samples_a > 0 ? - hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a, - hsotg->hfnum_other_samples_a > 0 ? - hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a - : 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b, - hsotg->hfnum_7_samples_b > 0 ? - hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b, - (hsotg->hfnum_0_samples_b > 0) ? - hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b, - (hsotg->hfnum_other_samples_b > 0) ? - hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b - : 0); -#endif -} - struct wrapper_priv_data { struct dwc2_hsotg *hsotg; }; @@ -4363,6 +4341,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) } spin_unlock_irqrestore(&hsotg->lock, flags); + + dwc2_vbus_supply_init(hsotg); + return 0; } @@ -4390,6 +4371,8 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_exit(hsotg); + usleep_range(1000, 3000); } @@ -4414,7 +4397,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (!hsotg->params.hibernation) + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) goto skip_power_saving; /* @@ -4426,14 +4409,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) hprt0 |= HPRT0_SUSP; hprt0 &= ~HPRT0_PWR; dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_vbus_supply_exit(hsotg); } - /* Enter hibernation */ - ret = dwc2_enter_hibernation(hsotg); + /* Enter partial_power_down */ + ret = dwc2_enter_partial_power_down(hsotg); if (ret) { if (ret != -ENOTSUPP) dev_err(hsotg->dev, - "enter hibernation failed\n"); + "enter partial_power_down failed\n"); goto skip_power_saving; } @@ -4444,7 +4428,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* After entering hibernation, hardware is no more accessible */ + /* After entering partial_power_down, hardware is no more accessible */ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); skip_power_saving: @@ -4469,7 +4453,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (!hsotg->params.hibernation) { + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) { hsotg->lx_state = DWC2_L0; goto unlock; } @@ -4491,10 +4475,10 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* Exit hibernation */ - ret = dwc2_exit_hibernation(hsotg, true); + /* Exit partial_power_down */ + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit hibernation failed\n"); + dev_err(hsotg->dev, "exit partial_power_down failed\n"); hsotg->lx_state = DWC2_L0; @@ -4506,6 +4490,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_port_resume(hsotg); } else { + dwc2_vbus_supply_init(hsotg); + /* Wait for controller to correctly update D+/D- level */ usleep_range(3000, 5000); @@ -5368,6 +5354,7 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) hr->hprt0 = dwc2_read_hprt0(hsotg); hr->hfir = dwc2_readl(hsotg->regs + HFIR); + hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); hr->valid = true; return 0; @@ -5404,7 +5391,231 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); dwc2_writel(hr->hfir, hsotg->regs + HFIR); + dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ); hsotg->frame_number = 0; return 0; } + +/** + * dwc2_host_enter_hibernation() - Put controller in Hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + int ret = 0; + u32 hprt0; + u32 pcgcctl; + u32 gusbcfg; + u32 gpwrdn; + + dev_dbg(hsotg->dev, "Preparing host for hibernation\n"); + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + + /* Enter USB Suspend Mode */ + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 |= HPRT0_SUSP; + hprt0 &= ~HPRT0_ENA; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for the HPRT0.PrtSusp register field to be set */ + if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300)) + dev_warn(hsotg->dev, "Suspend wasn't generated\n"); + + /* + * We need to disable interrupts to prevent servicing of any IRQ + * during going to hibernation + */ + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->lx_state = DWC2_L2; + + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { + /* ULPI interface */ + /* Suspend the Phy Clock */ + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + } else { + /* UTMI+ Interface */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + } + + /* Enable interrupts from wake up logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Unmask host mode interrupts in GPWRDN */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_DISCONN_DET_MSK; + gpwrdn |= GPWRDN_LNSTSCHG_MSK; + gpwrdn |= GPWRDN_STS_CHGINT_MSK; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Switch off VDD */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + + hsotg->hibernated = 1; + hsotg->bus_suspended = 1; + dev_dbg(hsotg->dev, "Host hibernation completed\n"); + spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; +} + +/* + * dwc2_host_exit_hibernation() + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Device or Host. + * @param reset: indicates whether resume is initiated by Reset. + * + * Return: non-zero if failed to enter to hibernation. + * + * This function is for exiting from Host mode hibernation by + * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup. + */ +int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, + int reset) +{ + u32 gpwrdn; + u32 hprt0; + int ret = 0; + struct dwc2_gregs_backup *gr; + struct dwc2_hregs_backup *hr; + + gr = &hsotg->gr_backup; + hr = &hsotg->hr_backup; + + dev_dbg(hsotg->dev, + "%s: called with rem_wakeup = %d reset = %d\n", + __func__, rem_wakeup, reset); + + dwc2_hib_restore_common(hsotg, rem_wakeup, 1); + hsotg->hibernated = 0; + + /* + * This step is not described in functional spec but if not wait for + * this delay, mismatch interrupts occurred because just after restore + * core is in Device mode(gintsts.curmode == 0) + */ + mdelay(100); + + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* De-assert Restore */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Restore GUSBCFG, HCFG */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + + /* De-assert Wakeup Logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + hprt0 = hr->hprt0; + hprt0 |= HPRT0_PWR; + hprt0 &= ~HPRT0_ENA; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + hprt0 = hr->hprt0; + hprt0 |= HPRT0_PWR; + hprt0 &= ~HPRT0_ENA; + hprt0 &= ~HPRT0_SUSP; + + if (reset) { + hprt0 |= HPRT0_RST; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for Resume time and then program HPRT again */ + mdelay(60); + hprt0 &= ~HPRT0_RST; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } else { + hprt0 |= HPRT0_RES; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for Resume time and then program HPRT again */ + mdelay(100); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + /* Clear all interrupt status */ + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 |= HPRT0_CONNDET; + hprt0 |= HPRT0_ENACHG; + hprt0 &= ~HPRT0_ENA; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Restore global registers */ + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + /* Restore host registers */ + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + + hsotg->hibernated = 0; + hsotg->bus_suspended = 0; + hsotg->lx_state = DWC2_L0; + dev_dbg(hsotg->dev, "Host hibernation restore complete\n"); + return ret; +} diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index ad60e46e66e1..96a9da5fb202 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -783,19 +783,6 @@ int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg); */ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg); -/** - * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF - * - * @hsotg: The DWC2 HCD - * - * This can be used to determine average interrupt latency. Frame remaining is - * also shown for start transfer and two additional sample points. - * - * NOTE: This function will be removed once the peripheral controller code - * is integrated and the driver is stable - */ -void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg); - /* URB interface */ /* Transfer flags */ @@ -813,47 +800,4 @@ int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context); void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, int status); -#ifdef DEBUG -/* - * Macro to sample the remaining PHY clocks left in the current frame. This - * may be used during debugging to determine the average time it takes to - * execute sections of code. There are two possible sample points, "a" and - * "b", so the _letter_ argument must be one of these values. - * - * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For - * example, "cat /sys/devices/lm0/hcd_frrem". - */ -#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \ -do { \ - struct hfnum_data _hfnum_; \ - struct dwc2_qtd *_qtd_; \ - \ - _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \ - qtd_list_entry); \ - if (usb_pipeint(_qtd_->urb->pipe) && \ - (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \ - _hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \ - switch (_hfnum_.b.frnum & 0x7) { \ - case 7: \ - (_hcd_)->hfnum_7_samples_##_letter_++; \ - (_hcd_)->hfnum_7_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - case 0: \ - (_hcd_)->hfnum_0_samples_##_letter_++; \ - (_hcd_)->hfnum_0_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - default: \ - (_hcd_)->hfnum_other_samples_##_letter_++; \ - (_hcd_)->hfnum_other_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - } \ - } \ -} while (0) -#else -#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0) -#endif - #endif /* __DWC2_HCD_H__ */ diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 2c906d8ee465..38391e48351f 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -231,6 +231,7 @@ #define GUID HSOTG_REG(0x003c) #define GSNPSID HSOTG_REG(0x0040) #define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) #define GHWCFG2 HSOTG_REG(0x0048) #define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) @@ -309,6 +310,7 @@ #define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_ACG_SUPPORTED BIT(12) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 @@ -320,28 +322,30 @@ #define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 #define GLPMCFG HSOTG_REG(0x0054) -#define GLPMCFG_INV_SEL_HSIC BIT(31) -#define GLPMCFG_HSIC_CONNECT BIT(30) -#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25) -#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25 -#define GLPMCFG_SEND_LPM BIT(24) -#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21) -#define GLPMCFG_RETRY_COUNT_SHIFT 21 -#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17) -#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17 -#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16) -#define GLPMCFG_PRT_SLEEP_STS BIT(15) -#define GLPMCFG_LPM_RESP_MASK (0x3 << 13) -#define GLPMCFG_LPM_RESP_SHIFT 13 +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 #define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) #define GLPMCFG_HIRD_THRES_SHIFT 8 -#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) -#define GLPMCFG_EN_UTMI_SLEEP BIT(7) -#define GLPMCFG_REM_WKUP_EN BIT(6) +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) #define GLPMCFG_HIRD_MASK (0xf << 2) #define GLPMCFG_HIRD_SHIFT 2 -#define GLPMCFG_APPL_RESP BIT(1) -#define GLPMCFG_LPM_CAP_EN BIT(0) +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) #define GPWRDN HSOTG_REG(0x0058) #define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) @@ -644,6 +648,10 @@ #define PCGCTL_GATEHCLK BIT(1) #define PCGCTL_STOPPCLK BIT(0) +#define PCGCCTL1 HSOTG_REG(0xe04) +#define PCGCCTL1_TIMER (0x3 << 1) +#define PCGCCTL1_GATEEN BIT(0) + #define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) /* Host Mode Registers */ diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index c4a47496d2fb..f03e41879224 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -252,6 +252,20 @@ static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) p->g_tx_fifo_size[i] = depth_average; } +static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg) +{ + int val; + + if (hsotg->hw_params.hibernation) + val = 2; + else if (hsotg->hw_params.power_optimized) + val = 1; + else + val = 0; + + hsotg->params.power_down = val; +} + /** * dwc2_set_default_params() - Set all core parameters to their * auto-detected default values. @@ -266,21 +280,27 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) dwc2_set_param_phy_type(hsotg); dwc2_set_param_speed(hsotg); dwc2_set_param_phy_utmi_width(hsotg); + dwc2_set_param_power_down(hsotg); p->phy_ulpi_ddr = false; p->phy_ulpi_ext_vbus = false; p->enable_dynamic_fifo = hw->enable_dynamic_fifo; p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; p->i2c_enable = hw->i2c_enable; + p->acg_enable = hw->acg_enable; p->ulpi_fs_ls = false; p->ts_dline = false; p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); p->uframe_sched = true; p->external_id_pin_ctl = false; - p->hibernation = false; + p->lpm = true; + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; p->max_packet_count = hw->max_packet_count; p->max_transfer_size = hw->max_transfer_size; - p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; if ((hsotg->dr_mode == USB_DR_MODE_HOST) || (hsotg->dr_mode == USB_DR_MODE_OTG)) { @@ -463,6 +483,38 @@ static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg) dwc2_set_param_phy_utmi_width(hsotg); } +static void dwc2_check_param_power_down(struct dwc2_hsotg *hsotg) +{ + int param = hsotg->params.power_down; + + switch (param) { + case DWC2_POWER_DOWN_PARAM_NONE: + break; + case DWC2_POWER_DOWN_PARAM_PARTIAL: + if (hsotg->hw_params.power_optimized) + break; + dev_dbg(hsotg->dev, + "Partial power down isn't supported by HW\n"); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + if (hsotg->hw_params.hibernation) + break; + dev_dbg(hsotg->dev, + "Hibernation isn't supported by HW\n"); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + default: + dev_err(hsotg->dev, + "%s: Invalid parameter power_down=%d\n", + __func__, param); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + } + + hsotg->params.power_down = param; +} + static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) { int fifo_count; @@ -523,10 +575,19 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) dwc2_check_param_phy_type(hsotg); dwc2_check_param_speed(hsotg); dwc2_check_param_phy_utmi_width(hsotg); + dwc2_check_param_power_down(hsotg); CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo); CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo); CHECK_BOOL(i2c_enable, hw->i2c_enable); + CHECK_BOOL(acg_enable, hw->acg_enable); CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a)); + CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a)); + CHECK_BOOL(lpm, hw->lpm_mode); + CHECK_BOOL(lpm_clock_gating, hsotg->params.lpm); + CHECK_BOOL(besl, hsotg->params.lpm); + CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)); + CHECK_BOOL(hird_threshold_en, hsotg->params.lpm); + CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0); CHECK_RANGE(max_packet_count, 15, hw->max_packet_count, hw->max_packet_count); @@ -579,19 +640,15 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg) struct dwc2_hw_params *hw = &hsotg->hw_params; u32 gnptxfsiz; u32 hptxfsiz; - bool forced; if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) return; - forced = dwc2_force_mode_if_needed(hsotg, true); + dwc2_force_mode(hsotg, true); gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); - if (forced) - dwc2_clear_force_mode(hsotg); - hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >> @@ -606,14 +663,13 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg) static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) { struct dwc2_hw_params *hw = &hsotg->hw_params; - bool forced; u32 gnptxfsiz; int fifo, fifo_count; if (hsotg->dr_mode == USB_DR_MODE_HOST) return; - forced = dwc2_force_mode_if_needed(hsotg, false); + dwc2_force_mode(hsotg, false); gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); @@ -625,9 +681,6 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; } - if (forced) - dwc2_clear_force_mode(hsotg); - hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; } @@ -646,14 +699,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) /* * Attempt to ensure this device is really a DWC_otg Controller. * Read and verify the GSNPSID register contents. The value should be - * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3", - * as in "OTG version 2.xx" or "OTG version 3.xx". + * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx */ + hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID); - if ((hw->snpsid & 0xfffff000) != 0x4f542000 && - (hw->snpsid & 0xfffff000) != 0x4f543000 && - (hw->snpsid & 0xffff0000) != 0x55310000 && - (hw->snpsid & 0xffff0000) != 0x55320000) { + if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n", hw->snpsid); return -ENODEV; @@ -706,6 +758,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> GHWCFG3_DFIFO_DEPTH_SHIFT; + hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN); /* hwcfg4 */ hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); @@ -715,8 +768,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) GHWCFG4_NUM_IN_EPS_SHIFT; hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER); hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); /* fifo sizes */ hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index 3ecc951a1aea..7f21747007f1 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -83,7 +83,6 @@ static void dwc2_pci_remove(struct pci_dev *pci) platform_device_unregister(glue->dwc2); usb_phy_generic_unregister(glue->phy); - kfree(glue); pci_set_drvdata(pci, NULL); } @@ -105,10 +104,17 @@ static int dwc2_pci_probe(struct pci_dev *pci, pci_set_master(pci); + phy = usb_phy_generic_register(); + if (IS_ERR(phy)) { + dev_err(dev, "error registering generic PHY (%ld)\n", + PTR_ERR(phy)); + return PTR_ERR(phy); + } + dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO); if (!dwc2) { dev_err(dev, "couldn't allocate dwc2 device\n"); - return -ENOMEM; + goto err; } memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); @@ -125,32 +131,25 @@ static int dwc2_pci_probe(struct pci_dev *pci, ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc2 device\n"); - return ret; + goto err; } dwc2->dev.parent = dev; - phy = usb_phy_generic_register(); - if (IS_ERR(phy)) { - dev_err(dev, "error registering generic PHY (%ld)\n", - PTR_ERR(phy)); - return PTR_ERR(phy); - } - ret = dwc2_pci_quirks(pci, dwc2); if (ret) goto err; + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + goto err; + ret = platform_device_add(dwc2); if (ret) { dev_err(dev, "failed to register dwc2 device\n"); goto err; } - glue = kzalloc(sizeof(*glue), GFP_KERNEL); - if (!glue) - return -ENOMEM; - glue->phy = phy; glue->dwc2 = dwc2; pci_set_drvdata(pci, glue); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 4703478f702f..4c0819554bcd 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -382,8 +382,10 @@ static int dwc2_driver_probe(struct platform_device *dev) if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32)); - if (retval) + if (retval) { + dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval); return retval; + } res = platform_get_resource(dev, IORESOURCE_MEM, 0); hsotg->regs = devm_ioremap_resource(&dev->dev, res); @@ -425,13 +427,20 @@ static int dwc2_driver_probe(struct platform_device *dev) * Reset before dwc2_get_hwparams() then it could get power-on real * reset value form registers. */ - dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); + if (retval) + goto error; /* Detect config values from hardware */ retval = dwc2_get_hwparams(hsotg); if (retval) goto error; + /* + * For OTG cores, set the force mode bits to reflect the value + * of dr_mode. Force mode bits should not be touched at any + * other time after this. + */ dwc2_force_dr_mode(hsotg); retval = dwc2_init_params(hsotg); @@ -439,7 +448,7 @@ static int dwc2_driver_probe(struct platform_device *dev) goto error; if (hsotg->dr_mode != USB_DR_MODE_HOST) { - retval = dwc2_gadget_init(hsotg, hsotg->irq); + retval = dwc2_gadget_init(hsotg); if (retval) goto error; hsotg->gadget_enabled = 1; @@ -456,6 +465,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } platform_set_drvdata(dev, hsotg); + hsotg->hibernated = 0; dwc2_debugfs_init(hsotg); diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 7ac725038f8d..025bc68094fc 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o -ifneq ($(CONFIG_FTRACE),) +ifneq ($(CONFIG_TRACING),) dwc3-y += trace.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e94bf91cc58a..a15648d25e30 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) return 0; } -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc); -static int dwc3_event_buffers_setup(struct dwc3 *dwc); - -static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -110,13 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work) unsigned long flags; int ret; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) + dwc3_otg_update(dwc, 0); + if (!dwc->desired_dr_role) return; if (dwc->desired_dr_role == dwc->current_dr_role) return; - if (dwc->dr_mode != USB_DR_MODE_OTG) + if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev) return; switch (dwc->current_dr_role) { @@ -127,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work) dwc3_gadget_exit(dwc); dwc3_event_buffers_cleanup(dwc); break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE; + spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_otg_update(dwc, 1); + break; default: break; } @@ -162,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work) if (ret) dev_err(dwc->dev, "failed to initialize peripheral\n"); break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_init(dwc); + dwc3_otg_update(dwc, 0); + break; default: break; } + } void dwc3_set_mode(struct dwc3 *dwc, u32 mode) @@ -229,7 +244,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) do { reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) - return 0; + goto done; udelay(1); } while (--retries); @@ -238,6 +253,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) phy_exit(dwc->usb2_generic_phy); return -ETIMEDOUT; + +done: + /* + * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, + * we must wait at least 50ms before accessing the PHY domain + * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + */ + if (dwc3_is_usb31(dwc)) + msleep(50); + + return 0; } /* @@ -348,7 +374,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -365,7 +391,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) return 0; } -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +void dwc3_event_buffers_cleanup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -846,6 +872,43 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } + /* + * Must config both number of packets and max burst settings to enable + * RX and/or TX threshold. + */ + if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; + u8 rx_maxburst = dwc->rx_max_burst_prd; + u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; + u8 tx_maxburst = dwc->tx_max_burst_prd; + + if (rx_thr_num && rx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg |= DWC31_RXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); + reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num); + + reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + } + + if (tx_thr_num && tx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg |= DWC31_TXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); + reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num); + + reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + } + } + return 0; err4: @@ -1016,6 +1079,10 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -1050,6 +1117,14 @@ static void dwc3_get_properties(struct dwc3 *dwc) &hird_threshold); dwc->usb3_lpm_capable = device_property_read_bool(dev, "snps,usb3_lpm_capable"); + device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd", + &rx_thr_num_pkt_prd); + device_property_read_u8(dev, "snps,rx-max-burst-prd", + &rx_max_burst_prd); + device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd", + &tx_thr_num_pkt_prd); + device_property_read_u8(dev, "snps,tx-max-burst-prd", + &tx_max_burst_prd); dwc->disable_scramble_quirk = device_property_read_bool(dev, "snps,disable_scramble_quirk"); @@ -1100,6 +1175,12 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd; + dwc->rx_max_burst_prd = rx_max_burst_prd; + + dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; + dwc->tx_max_burst_prd = tx_max_burst_prd; + dwc->imod_interval = 0; } @@ -1326,6 +1407,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) if (!PMSG_IS_AUTO(msg)) dwc3_core_exit(dwc); break; + case DWC3_GCTL_PRTCAP_OTG: + /* do nothing during runtime_suspend */ + if (PMSG_IS_AUTO(msg)) + break; + + if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_suspend(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + } + + dwc3_otg_exit(dwc); + dwc3_core_exit(dwc); + break; default: /* do nothing */ break; @@ -1345,6 +1440,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1355,7 +1451,29 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) ret = dwc3_core_init(dwc); if (ret) return ret; + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + } + break; + case DWC3_GCTL_PRTCAP_OTG: + /* nothing to do on runtime_resume */ + if (PMSG_IS_AUTO(msg)) + break; + + ret = dwc3_core_init(dwc); + if (ret) + return ret; + + dwc3_set_prtcap(dwc, dwc->current_dr_role); + + dwc3_otg_init(dwc); + if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) { + dwc3_otg_host_init(dwc); + } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_resume(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); } + break; default: /* do nothing */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 860d2bc184d1..4f3b43809917 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -58,6 +58,11 @@ #define DWC3_DEVICE_EVENT_CMD_CMPL 10 #define DWC3_DEVICE_EVENT_OVERFLOW 11 +/* Controller's role while using the OTG block */ +#define DWC3_OTG_ROLE_IDLE 0 +#define DWC3_OTG_ROLE_HOST 1 +#define DWC3_OTG_ROLE_DEVICE 2 + #define DWC3_GEVNTCOUNT_MASK 0xfffc #define DWC3_GEVNTCOUNT_EHB BIT(31) #define DWC3_GSNPSID_MASK 0xffff0000 @@ -100,6 +105,11 @@ #define DWC3_GHWPARAMS7 0xc15c #define DWC3_GDBGFIFOSPACE 0xc160 #define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GDBGBMU 0xc16c +#define DWC3_GDBGLSPMUX 0xc170 +#define DWC3_GDBGLSP 0xc174 +#define DWC3_GDBGEPINFO0 0xc178 +#define DWC3_GDBGEPINFO1 0xc17c #define DWC3_GPRTBIMAP_HS0 0xc180 #define DWC3_GPRTBIMAP_HS1 0xc184 #define DWC3_GPRTBIMAP_FS0 0xc188 @@ -173,6 +183,26 @@ #define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24) #define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29) +/* Global RX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global TX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f) + /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN BIT(16) @@ -201,6 +231,15 @@ #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +/* Global Status Register */ +#define DWC3_GSTS_OTG_IP BIT(10) +#define DWC3_GSTS_BC_IP BIT(9) +#define DWC3_GSTS_ADP_IP BIT(8) +#define DWC3_GSTS_HOST_IP BIT(7) +#define DWC3_GSTS_DEVICE_IP BIT(6) +#define DWC3_GSTS_CSR_TIMEOUT BIT(5) +#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) + /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) #define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30) @@ -241,6 +280,8 @@ #define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1) /* Global TX Fifo Size Register */ +#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ +#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) @@ -286,6 +327,11 @@ #define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Global HWPARAMS6 Register */ +#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) +#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13) +#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12) +#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11) +#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) #define DWC3_GHWPARAMS6_EN_FPGA BIT(7) /* Global HWPARAMS7 Register */ @@ -467,6 +513,74 @@ #define DWC3_DEV_IMOD_INTERVAL_SHIFT 0 #define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0) +/* OTG Configuration Register */ +#define DWC3_OCFG_DISPWRCUTTOFF BIT(5) +#define DWC3_OCFG_HIBDISMASK BIT(4) +#define DWC3_OCFG_SFTRSTMASK BIT(3) +#define DWC3_OCFG_OTGVERSION BIT(2) +#define DWC3_OCFG_HNPCAP BIT(1) +#define DWC3_OCFG_SRPCAP BIT(0) + +/* OTG CTL Register */ +#define DWC3_OCTL_OTG3GOERR BIT(7) +#define DWC3_OCTL_PERIMODE BIT(6) +#define DWC3_OCTL_PRTPWRCTL BIT(5) +#define DWC3_OCTL_HNPREQ BIT(4) +#define DWC3_OCTL_SESREQ BIT(3) +#define DWC3_OCTL_TERMSELIDPULSE BIT(2) +#define DWC3_OCTL_DEVSETHNPEN BIT(1) +#define DWC3_OCTL_HSTSETHNPEN BIT(0) + +/* OTG Event Register */ +#define DWC3_OEVT_DEVICEMODE BIT(31) +#define DWC3_OEVT_XHCIRUNSTPSET BIT(27) +#define DWC3_OEVT_DEVRUNSTPSET BIT(26) +#define DWC3_OEVT_HIBENTRY BIT(25) +#define DWC3_OEVT_CONIDSTSCHNG BIT(24) +#define DWC3_OEVT_HRRCONFNOTIF BIT(23) +#define DWC3_OEVT_HRRINITNOTIF BIT(22) +#define DWC3_OEVT_ADEVIDLE BIT(21) +#define DWC3_OEVT_ADEVBHOSTEND BIT(20) +#define DWC3_OEVT_ADEVHOST BIT(19) +#define DWC3_OEVT_ADEVHNPCHNG BIT(18) +#define DWC3_OEVT_ADEVSRPDET BIT(17) +#define DWC3_OEVT_ADEVSESSENDDET BIT(16) +#define DWC3_OEVT_BDEVBHOSTEND BIT(11) +#define DWC3_OEVT_BDEVHNPCHNG BIT(10) +#define DWC3_OEVT_BDEVSESSVLDDET BIT(9) +#define DWC3_OEVT_BDEVVBUSCHNG BIT(8) +#define DWC3_OEVT_BSESSVLD BIT(3) +#define DWC3_OEVT_HSTNEGSTS BIT(2) +#define DWC3_OEVT_SESREQSTS BIT(1) +#define DWC3_OEVT_ERROR BIT(0) + +/* OTG Event Enable Register */ +#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27) +#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26) +#define DWC3_OEVTEN_HIBENTRYEN BIT(25) +#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24) +#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23) +#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22) +#define DWC3_OEVTEN_ADEVIDLEEN BIT(21) +#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20) +#define DWC3_OEVTEN_ADEVHOSTEN BIT(19) +#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18) +#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17) +#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16) +#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11) +#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10) +#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9) +#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8) + +/* OTG Status Register */ +#define DWC3_OSTS_DEVRUNSTP BIT(13) +#define DWC3_OSTS_XHCIRUNSTP BIT(12) +#define DWC3_OSTS_PERIPHERALSTATE BIT(4) +#define DWC3_OSTS_XHCIPRTPOWER BIT(3) +#define DWC3_OSTS_BSESVLD BIT(2) +#define DWC3_OSTS_VBUSVLD BIT(1) +#define DWC3_OSTS_CONIDSTS BIT(0) + /* Structures */ struct dwc3_trb; @@ -781,6 +895,10 @@ struct dwc3_scratchpad_array { * @regs_size: address space size * @fladj: frame length adjustment * @irq_gadget: peripheral controller's IRQ number + * @otg_irq: IRQ number for OTG IRQs + * @current_otg_role: current role of operation while using the OTG block + * @desired_otg_role: desired role of operation while using the OTG block + * @otg_restart_host: flag that OTG controller needs to restart host * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -816,6 +934,10 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @rx_thr_num_pkt_prd: periodic ESS receive packet count + * @rx_max_burst_prd: max periodic ESS receive burst size + * @tx_thr_num_pkt_prd: periodic ESS transmit packet count + * @tx_max_burst_prd: max periodic ESS transmit burst size * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @delayed_status: true when gadget driver asks for delayed status @@ -914,6 +1036,10 @@ struct dwc3 { u32 fladj; u32 irq_gadget; + u32 otg_irq; + u32 current_otg_role; + u32 desired_otg_role; + bool otg_restart_host; u32 nr_scratch; u32 u1u2; u32 maximum_speed; @@ -979,6 +1105,10 @@ struct dwc3 { u8 test_mode_nr; u8 lpm_nyet_threshold; u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; const char *hsphy_interface; @@ -1175,6 +1305,7 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); @@ -1192,6 +1323,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc) bool dwc3_has_imod(struct dwc3 *dwc); +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); @@ -1235,11 +1369,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_drd_init(struct dwc3 *dwc); void dwc3_drd_exit(struct dwc3 *dwc); +void dwc3_otg_init(struct dwc3 *dwc); +void dwc3_otg_exit(struct dwc3 *dwc); +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus); +void dwc3_otg_host_init(struct dwc3 *dwc); #else static inline int dwc3_drd_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_drd_exit(struct dwc3 *dwc) { } +static inline void dwc3_otg_init(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ } +static inline void dwc3_otg_host_init(struct dwc3 *dwc) +{ } #endif /* power management interface */ diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 00e65530c81e..2f07be1e1f31 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -81,6 +81,11 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GHWPARAMS7), dump_register(GDBGFIFOSPACE), dump_register(GDBGLTSSM), + dump_register(GDBGBMU), + dump_register(GDBGLSPMUX), + dump_register(GDBGLSP), + dump_register(GDBGEPINFO0), + dump_register(GDBGEPINFO1), dump_register(GPRTBIMAP_HS0), dump_register(GPRTBIMAP_HS1), dump_register(GPRTBIMAP_FS0), @@ -487,8 +492,8 @@ static const struct file_operations dwc3_link_state_fops = { }; struct dwc3_ep_file_map { - char name[25]; - int (*show)(struct seq_file *s, void *unused); + const char name[25]; + const struct file_operations *const fops; }; static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) @@ -596,7 +601,7 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused) return 0; } -static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused) +static int dwc3_transfer_type_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -632,7 +637,7 @@ out: return 0; } -static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused) +static int dwc3_trb_ring_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -670,58 +675,39 @@ out: return 0; } -static struct dwc3_ep_file_map map[] = { - { "tx_fifo_queue", dwc3_tx_fifo_queue_show, }, - { "rx_fifo_queue", dwc3_rx_fifo_queue_show, }, - { "tx_request_queue", dwc3_tx_request_queue_show, }, - { "rx_request_queue", dwc3_rx_request_queue_show, }, - { "rx_info_queue", dwc3_rx_info_queue_show, }, - { "descriptor_fetch_queue", dwc3_descriptor_fetch_queue_show, }, - { "event_queue", dwc3_event_queue_show, }, - { "transfer_type", dwc3_ep_transfer_type_show, }, - { "trb_ring", dwc3_ep_trb_ring_show, }, +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); +DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); + +static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { + { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, }, + { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, }, + { "tx_request_queue", &dwc3_tx_request_queue_fops, }, + { "rx_request_queue", &dwc3_rx_request_queue_fops, }, + { "rx_info_queue", &dwc3_rx_info_queue_fops, }, + { "descriptor_fetch_queue", &dwc3_descriptor_fetch_queue_fops, }, + { "event_queue", &dwc3_event_queue_fops, }, + { "transfer_type", &dwc3_transfer_type_fops, }, + { "trb_ring", &dwc3_trb_ring_fops, }, }; -static int dwc3_endpoint_open(struct inode *inode, struct file *file) -{ - const char *file_name = file_dentry(file)->d_iname; - struct dwc3_ep_file_map *f_map; - int i; - - for (i = 0; i < ARRAY_SIZE(map); i++) { - f_map = &map[i]; - - if (strcmp(f_map->name, file_name) == 0) - break; - } - - return single_open(file, f_map->show, inode->i_private); -} - -static const struct file_operations dwc3_endpoint_fops = { - .open = dwc3_endpoint_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void dwc3_debugfs_create_endpoint_file(struct dwc3_ep *dep, - struct dentry *parent, int type) -{ - struct dentry *file; - struct dwc3_ep_file_map *ep_file = &map[type]; - - file = debugfs_create_file(ep_file->name, S_IRUGO, parent, dep, - &dwc3_endpoint_fops); -} - static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, struct dentry *parent) { int i; - for (i = 0; i < ARRAY_SIZE(map); i++) - dwc3_debugfs_create_endpoint_file(dep, parent, i); + for (i = 0; i < ARRAY_SIZE(dwc3_ep_file_map); i++) { + const struct file_operations *fops = dwc3_ep_file_map[i].fops; + const char *name = dwc3_ep_file_map[i].name; + + debugfs_create_file(name, S_IRUGO, parent, dep, fops); + } } static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep, diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index cc8ab9a8e9d2..1d8c557e97e0 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -8,22 +8,423 @@ */ #include <linux/extcon.h> +#include <linux/platform_device.h> #include "debug.h" #include "core.h" #include "gadget.h" -static void dwc3_drd_update(struct dwc3 *dwc) +static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + + reg &= ~(disable_mask); + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + + reg |= (enable_mask); + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +static void dwc3_otg_clear_events(struct dwc3 *dwc) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT); + + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \ + DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \ + DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \ + DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \ + DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \ + DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \ + DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVBHOSTENDEN | \ + DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \ + DWC3_OEVTEN_BDEVVBUSCHNGEN) + +static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc) { + struct dwc3 *dwc = _dwc; + + spin_lock(&dwc->lock); + if (dwc->otg_restart_host) { + dwc3_otg_host_init(dwc); + dwc->otg_restart_host = 0; + } + + spin_unlock(&dwc->lock); + + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + + return IRQ_HANDLED; +} + +static irqreturn_t dwc3_otg_irq(int irq, void *_dwc) +{ + u32 reg; + struct dwc3 *dwc = _dwc; + irqreturn_t ret = IRQ_NONE; + + reg = dwc3_readl(dwc->regs, DWC3_OEVT); + if (reg) { + /* ignore non OTG events, we can't disable them in OEVTEN */ + if (!(reg & DWC3_OTG_ALL_EVENTS)) { + dwc3_writel(dwc->regs, DWC3_OEVT, reg); + return IRQ_NONE; + } + + if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && + !(reg & DWC3_OEVT_DEVICEMODE)) + dwc->otg_restart_host = 1; + dwc3_writel(dwc->regs, DWC3_OEVT, reg); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static void dwc3_otgregs_init(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Prevent host/device reset from resetting OTG core. + * If we don't do this then xhci_reset (USBCMD.HCRST) will reset + * the signal outputs sent to the PHY, the OTG FSM logic of the + * core and also the resets to the VBUS filters inside the core. + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg |= DWC3_OCFG_SFTRSTMASK; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + + /* Disable hibernation for simplicity */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_GBLHIBERNATIONEN; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* + * Initialize OTG registers as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP); + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + /* OEVT = FFFF */ + dwc3_otg_clear_events(dwc); + /* OEVTEN = 0 */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */ + dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* + * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0, + * OCTL.HNPReq = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PERIMODE; + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN | + DWC3_OCTL_HNPREQ); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +static int dwc3_otg_get_irq(struct dwc3 *dwc) +{ + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + int irq; + + irq = platform_get_irq_byname(dwc3_pdev, "otg"); + if (irq > 0) + goto out; + + if (irq == -EPROBE_DEFER) + goto out; + + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq > 0) + goto out; + + if (irq == -EPROBE_DEFER) + goto out; + + irq = platform_get_irq(dwc3_pdev, 0); + if (irq > 0) + goto out; + + if (irq != -EPROBE_DEFER) + dev_err(dwc->dev, "missing OTG IRQ\n"); + + if (!irq) + irq = -EINVAL; + +out: + return irq; +} + +void dwc3_otg_init(struct dwc3 *dwc) +{ + u32 reg; + + /* + * As per Figure 11-4 OTG Driver Overall Programming Flow, + * block "Initialize GCTL for OTG operation". + */ + /* GCTL.PrtCapDir=2'b11 */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + /* GUSB2PHYCFG0.SusPHY=0 */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + /* Initialize OTG registers */ + dwc3_otgregs_init(dwc); +} + +void dwc3_otg_exit(struct dwc3 *dwc) +{ + /* disable all OTG IRQs */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* clear all events */ + dwc3_otg_clear_events(dwc); +} + +/* should be called before Host controller driver is started */ +void dwc3_otg_host_init(struct dwc3 *dwc) +{ + u32 reg; + + /* As per Figure 11-10 A-Device Flow Diagram */ + /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */ + + /* + * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0, + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE | + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); + + /* + * OCFG.DisPrtPwrCutoff = 0/1 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg &= ~DWC3_OCFG_DISPWRCUTTOFF; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + + /* + * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP + * We don't want SRP/HNP for simple dual-role so leave + * these disabled. + */ + + /* + * OEVTEN.OTGADevHostEvntEn = 1 + * OEVTEN.OTGADevSessEndDetEvntEn = 1 + * We don't want HNP/role-swap so leave these disabled. + */ + + /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */ + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + + /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PRTPWRCTL; + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +/* should be called after Host controller driver is stopped */ +static void dwc3_otg_host_exit(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Exit from A-device flow as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + + /* + * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0 + * OEVTEN.OTGADevSessEndDetEvntEn=0, + * OEVTEN.OTGADevHostEvntEn = 0 + * But we don't disable any OTG events + */ + + /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +/* should be called before the gadget controller driver is started */ +static void dwc3_otg_device_init(struct dwc3 *dwc) +{ + u32 reg; + + /* As per Figure 11-20 B-Device Flow Diagram */ + + /* + * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1 + * but we keep them 0 for simple dual-role operation. + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + /* OCFG.OTGSftRstMsk = 0/1 */ + reg |= DWC3_OCFG_SFTRSTMASK; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + /* + * OCTL.PeriMode = 1 + * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0 + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PERIMODE; + reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ | + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); + /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */ + dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN); + /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */ + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + /* GCTL.GblHibernationEn = 0. Already 0. */ +} + +/* should be called after the gadget controller driver is stopped */ +static void dwc3_otg_device_exit(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Exit from B-device flow as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + + /* + * OEVTEN.OTGBDevHNPChngEvntEn = 0 + * OEVTEN.OTGBDevVBusChngEvntEn = 0 + * OEVTEN.OTGBDevBHostEndEvntEn = 0 + */ + dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN | + DWC3_OEVTEN_BDEVVBUSCHNGEN | + DWC3_OEVTEN_BDEVBHOSTENDEN); + + /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ); + reg |= DWC3_OCTL_PERIMODE; + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ + int ret; + u32 reg; int id; + unsigned long flags; - id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); - if (id < 0) - id = 0; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return; - dwc3_set_mode(dwc, id ? - DWC3_GCTL_PRTCAP_HOST : - DWC3_GCTL_PRTCAP_DEVICE); + /* don't do anything if debug user changed role to not OTG */ + if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG) + return; + + if (!ignore_idstatus) { + reg = dwc3_readl(dwc->regs, DWC3_OSTS); + id = !!(reg & DWC3_OSTS_CONIDSTS); + + dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE : + DWC3_OTG_ROLE_HOST; + } + + if (dwc->desired_otg_role == dwc->current_otg_role) + return; + + switch (dwc->current_otg_role) { + case DWC3_OTG_ROLE_HOST: + dwc3_host_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otg_host_exit(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + break; + case DWC3_OTG_ROLE_DEVICE: + dwc3_gadget_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc3_event_buffers_cleanup(dwc); + dwc3_otg_device_exit(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + break; + default: + break; + } + + spin_lock_irqsave(&dwc->lock, flags); + + dwc->current_otg_role = dwc->desired_otg_role; + + spin_unlock_irqrestore(&dwc->lock, flags); + + switch (dwc->desired_otg_role) { + case DWC3_OTG_ROLE_HOST: + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otgregs_init(dwc); + dwc3_otg_host_init(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dwc->dev, "failed to initialize host\n"); + } else { + if (dwc->usb2_phy) + otg_set_vbus(dwc->usb2_phy->otg, true); + if (dwc->usb2_generic_phy) + phy_set_mode(dwc->usb2_generic_phy, + PHY_MODE_USB_HOST); + } + break; + case DWC3_OTG_ROLE_DEVICE: + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otgregs_init(dwc); + dwc3_otg_device_init(dwc); + dwc3_event_buffers_setup(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + if (dwc->usb2_phy) + otg_set_vbus(dwc->usb2_phy->otg, false); + if (dwc->usb2_generic_phy) + phy_set_mode(dwc->usb2_generic_phy, + PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(dwc); + if (ret) + dev_err(dwc->dev, "failed to initialize peripheral\n"); + break; + default: + break; + } +} + +static void dwc3_drd_update(struct dwc3 *dwc) +{ + int id; + + if (dwc->edev) { + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + if (id < 0) + id = 0; + dwc3_set_mode(dwc, id ? + DWC3_GCTL_PRTCAP_HOST : + DWC3_GCTL_PRTCAP_DEVICE); + } } static int dwc3_drd_notifier(struct notifier_block *nb, @@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb, int dwc3_drd_init(struct dwc3 *dwc) { - int ret; + int ret, irq; - if (dwc->dev->of_node) { - if (of_property_read_bool(dwc->dev->of_node, "extcon")) - dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0); + if (dwc->dev->of_node && + of_property_read_bool(dwc->dev->of_node, "extcon")) { + dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0); if (IS_ERR(dwc->edev)) return PTR_ERR(dwc->edev); @@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc) dev_err(dwc->dev, "couldn't register cable notifier\n"); return ret; } - } - dwc3_drd_update(dwc); + dwc3_drd_update(dwc); + } else { + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG; + + /* use OTG block to get ID event */ + irq = dwc3_otg_get_irq(dwc); + if (irq < 0) + return irq; + + dwc->otg_irq = irq; + + /* disable all OTG IRQs */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* clear all events */ + dwc3_otg_clear_events(dwc); + + ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq, + dwc3_otg_thread_irq, + IRQF_SHARED, "dwc3-otg", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + dwc->otg_irq, ret); + ret = -ENODEV; + return ret; + } + + dwc3_otg_init(dwc); + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + } return 0; } void dwc3_drd_exit(struct dwc3 *dwc) { - extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, - &dwc->edev_nb); + unsigned long flags; + + if (dwc->edev) + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + + cancel_work_sync(&dwc->drd_work); + + /* debug user might have changed role, clean based on current role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_gadget_exit(dwc); + dwc3_event_buffers_cleanup(dwc); + break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE; + spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_otg_update(dwc, 1); + break; + default: + break; + } - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); - flush_work(&dwc->drd_work); - dwc3_gadget_exit(dwc); + if (!dwc->edev) + free_irq(dwc->otg_irq, dwc); } diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e54c3622eb28..cb2ee96fd3e8 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -27,6 +27,7 @@ struct dwc3_of_simple { struct clk **clks; int num_clocks; struct reset_control *resets; + bool pulse_resets; }; static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) @@ -83,6 +84,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) int ret; int i; + bool shared_resets = false; simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); if (!simple) @@ -91,16 +93,28 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, simple); simple->dev = dev; - simple->resets = of_reset_control_array_get_optional_exclusive(np); + if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") || + of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) { + shared_resets = true; + simple->pulse_resets = true; + } + + simple->resets = of_reset_control_array_get(np, shared_resets, true); if (IS_ERR(simple->resets)) { ret = PTR_ERR(simple->resets); dev_err(dev, "failed to get device resets, err=%d\n", ret); return ret; } - ret = reset_control_deassert(simple->resets); - if (ret) - goto err_resetc_put; + if (simple->pulse_resets) { + ret = reset_control_reset(simple->resets); + if (ret) + goto err_resetc_put; + } else { + ret = reset_control_deassert(simple->resets); + if (ret) + goto err_resetc_put; + } ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, "clocks", "#clock-cells")); @@ -124,7 +138,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) return 0; err_resetc_assert: - reset_control_assert(simple->resets); + if (!simple->pulse_resets) + reset_control_assert(simple->resets); err_resetc_put: reset_control_put(simple->resets); @@ -145,7 +160,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) } simple->num_clocks = 0; - reset_control_assert(simple->resets); + if (!simple->pulse_resets) + reset_control_assert(simple->resets); + reset_control_put(simple->resets); pm_runtime_put_sync(dev); @@ -196,6 +213,8 @@ static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "xlnx,zynqmp-dwc3" }, { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, + { .compatible = "amlogic,meson-axg-dwc3" }, + { .compatible = "amlogic,meson-gxl-dwc3" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 3ba11136ebf0..c961a94d136b 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -222,7 +222,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc3 device\n"); - return ret; + goto err; } dwc->pci = pci; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 18be31d5743a..5a991bca8ed7 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -814,7 +814,7 @@ out: static void dwc3_ep0_complete_data(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { - struct dwc3_request *r = NULL; + struct dwc3_request *r; struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2bda4eb1e9ac..550ee952c0d1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1858,7 +1858,11 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * bursts of data without going through any sort of endpoint throttling. */ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); - reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + if (dwc3_is_usb31(dwc)) + reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; + else + reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); dwc3_gadget_setup_nump(dwc); @@ -1950,6 +1954,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; int epnum; + u32 tmo_eps = 0; spin_lock_irqsave(&dwc->lock, flags); @@ -1960,6 +1965,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { struct dwc3_ep *dep = dwc->eps[epnum]; + int ret; if (!dep) continue; @@ -1967,9 +1973,24 @@ static int dwc3_gadget_stop(struct usb_gadget *g) if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) continue; - wait_event_lock_irq(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock); + ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer, + !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), + dwc->lock, msecs_to_jiffies(5)); + + if (ret <= 0) { + /* Timed out or interrupted! There's nothing much + * we can do so we just log here and print which + * endpoints timed out at the end. + */ + tmo_eps |= 1 << epnum; + dep->flags &= DWC3_EP_END_TRANSFER_PENDING; + } + } + + if (tmo_eps) { + dev_err(dwc->dev, + "end transfer timed out on endpoints 0x%x [bitmap]\n", + tmo_eps); } out: @@ -2023,7 +2044,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g, reg |= DWC3_DCFG_SUPERSPEED; break; case USB_SPEED_SUPER_PLUS: - reg |= DWC3_DCFG_SUPERSPEED_PLUS; + if (dwc3_is_usb31(dwc)) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + else + reg |= DWC3_DCFG_SUPERSPEED; break; default: dev_err(dwc->dev, "invalid speed (%d)\n", speed); @@ -2101,7 +2125,10 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) mdwidth /= 8; size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num)); - size = DWC3_GTXFIFOSIZ_TXFDEF(size); + if (dwc3_is_usb31(dwc)) + size = DWC31_GTXFIFOSIZ_TXFDEF(size); + else + size = DWC3_GTXFIFOSIZ_TXFDEF(size); /* FIFO Depth is in MDWDITH bytes. Multiply */ size *= mdwidth; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 77c7ecca816a..63a7cb87514a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1422,11 +1422,12 @@ static int count_ext_compat(struct usb_configuration *c) return res; } -static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +static int fill_ext_compat(struct usb_configuration *c, u8 *buf) { int i, count; count = 16; + buf += 16; for (i = 0; i < c->next_interface_id; ++i) { struct usb_function *f; int j; @@ -1449,10 +1450,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf) buf += 23; } count += 24; - if (count >= 4096) - return; + if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; } } + + return count; } static int count_ext_prop(struct usb_configuration *c, int interface) @@ -1497,25 +1500,21 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) struct usb_os_desc *d; struct usb_os_desc_ext_prop *ext_prop; int j, count, n, ret; - u8 *start = buf; f = c->interface[interface]; + count = 10; /* header length */ + buf += 10; for (j = 0; j < f->os_desc_n; ++j) { if (interface != f->os_desc_table[j].if_id) continue; d = f->os_desc_table[j].os_desc; if (d) list_for_each_entry(ext_prop, &d->ext_prop, entry) { - /* 4kB minus header length */ - n = buf - start; - if (n >= 4086) - return 0; - - count = ext_prop->data_len + + n = ext_prop->data_len + ext_prop->name_len + 14; - if (count > 4086 - n) - return -EINVAL; - usb_ext_prop_put_size(buf, count); + if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + usb_ext_prop_put_size(buf, n); usb_ext_prop_put_type(buf, ext_prop->type); ret = usb_ext_prop_put_name(buf, ext_prop->name, ext_prop->name_len); @@ -1541,11 +1540,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) default: return -EINVAL; } - buf += count; + buf += n; + count += n; } } - return 0; + return count; } /* @@ -1827,6 +1827,7 @@ unknown: req->complete = composite_setup_complete; buf = req->buf; os_desc_cfg = cdev->os_desc_config; + w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ); memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { @@ -1834,24 +1835,16 @@ unknown: if (w_index != 0x4 || (w_value >> 8)) break; buf[6] = w_index; - if (w_length == 0x10) { - /* Number of ext compat interfaces */ - count = count_ext_compat(os_desc_cfg); - buf[8] = count; - count *= 24; /* 24 B/ext compat desc */ - count += 16; /* header */ - put_unaligned_le32(count, buf); - value = w_length; - } else { - /* "extended compatibility ID"s */ - count = count_ext_compat(os_desc_cfg); - buf[8] = count; - count *= 24; /* 24 B/ext compat desc */ - count += 16; /* header */ - put_unaligned_le32(count, buf); - buf += 16; - fill_ext_compat(os_desc_cfg, buf); - value = w_length; + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x10) { + value = fill_ext_compat(os_desc_cfg, buf); + value = min_t(u16, w_length, value); } break; case USB_RECIP_INTERFACE: @@ -1859,47 +1852,23 @@ unknown: break; interface = w_value & 0xFF; buf[6] = w_index; - if (w_length == 0x0A) { - count = count_ext_prop(os_desc_cfg, - interface); - put_unaligned_le16(count, buf + 8); - count = len_ext_prop(os_desc_cfg, - interface); - put_unaligned_le32(count, buf); - - value = w_length; - } else { - count = count_ext_prop(os_desc_cfg, - interface); - put_unaligned_le16(count, buf + 8); - count = len_ext_prop(os_desc_cfg, - interface); - put_unaligned_le32(count, buf); - buf += 10; + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x0A) { value = fill_ext_prop(os_desc_cfg, interface, buf); - if (value < 0) - return value; - - value = w_length; + if (value >= 0) + value = min_t(u16, w_length, value); } break; } - if (value >= 0) { - req->length = value; - req->context = cdev; - req->zero = value < w_length; - value = composite_ep0_queue(cdev, req, - GFP_ATOMIC); - if (value < 0) { - DBG(cdev, "ep_queue --> %d\n", value); - req->status = 0; - composite_setup_complete(gadget->ep0, - req); - } - } - return value; + goto check_value; } VDBG(cdev, @@ -1973,6 +1942,7 @@ try_fun_setup: goto done; } +check_value: /* respond with data transfer before status phase? */ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; @@ -2156,8 +2126,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, goto end; } - /* OS feature descriptor length <= 4kB */ - cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL); + cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ, + GFP_KERNEL); if (!cdev->os_desc_req->buf) { ret = -ENOMEM; usb_ep_free_request(ep0, cdev->os_desc_req); @@ -2172,6 +2142,7 @@ end: void composite_dev_cleanup(struct usb_composite_dev *cdev) { struct usb_gadget_string_container *uc, *tmp; + struct usb_ep *ep, *tmp_ep; list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) { list_del(&uc->list); @@ -2193,6 +2164,21 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) } cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); + + /* + * Some UDC backends have a dynamic EP allocation scheme. + * + * In that case, the dispose() callback is used to notify the + * backend that the EPs are no longer in use. + * + * Note: The UDC backend can remove the EP from the ep_list as + * a result, so we need to use the _safe list iterator. + */ + list_for_each_entry_safe(ep, tmp_ep, + &cdev->gadget->ep_list, ep_list) { + if (ep->ops->dispose) + ep->ops->dispose(ep); + } } static int composite_bind(struct usb_gadget *gadget, diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d2428a9e8900..0294e4f18873 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -758,9 +758,13 @@ static void ffs_user_copy_worker(struct work_struct *work) bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; if (io_data->read && ret > 0) { + mm_segment_t oldfs = get_fs(); + + set_fs(USER_DS); use_mm(io_data->mm); ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); unuse_mm(io_data->mm); + set_fs(oldfs); } io_data->kiocb->ki_complete(io_data->kiocb, ret, ret); @@ -3238,7 +3242,7 @@ static int ffs_func_setup(struct usb_function *f, __ffs_event_add(ffs, FUNCTIONFS_SETUP); spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); - return 0; + return USB_GADGET_DELAYED_STATUS; } static bool ffs_func_req_match(struct usb_function *f, diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index da81cf16b850..d78dbb73bde8 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1094,7 +1094,7 @@ static int usbg_submit_command(struct f_uas *fu, struct command_iu *cmd_iu = cmdbuf; struct usbg_cmd *cmd; struct usbg_tpg *tpg = fu->tpg; - struct tcm_usbg_nexus *tv_nexus = tpg->tpg_nexus; + struct tcm_usbg_nexus *tv_nexus; u32 cmd_len; u16 scsi_tag; diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index ef3d25259b0e..fd5595ac5bf7 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -225,7 +225,7 @@ static int msg_unbind(struct usb_composite_dev *cdev) static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, - .max_speed = USB_SPEED_SUPER, + .max_speed = USB_SPEED_SUPER_PLUS, .needs_serial = 1, .strings = dev_strings, .bind = msg_bind, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 075eaaa8a408..27c16399c7e8 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -23,7 +23,8 @@ #include <linux/usb/atmel_usba_udc.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/irq.h> +#include <linux/gpio/consumer.h> #include "atmel_usba_udc.h" #define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \ @@ -415,8 +416,8 @@ static inline void usba_int_enb_set(struct usba_udc *udc, u32 val) static int vbus_is_present(struct usba_udc *udc) { - if (gpio_is_valid(udc->vbus_pin)) - return gpio_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted; + if (udc->vbus_pin) + return gpiod_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted; /* No Vbus detection: Assume always present */ return 1; @@ -1975,8 +1976,8 @@ static int atmel_usba_start(struct usb_gadget *gadget, mutex_lock(&udc->vbus_mutex); - if (gpio_is_valid(udc->vbus_pin)) - enable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + enable_irq(gpiod_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ udc->vbus_prev = vbus_is_present(udc); @@ -1990,8 +1991,8 @@ static int atmel_usba_start(struct usb_gadget *gadget, return 0; err: - if (gpio_is_valid(udc->vbus_pin)) - disable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + disable_irq(gpiod_to_irq(udc->vbus_pin)); mutex_unlock(&udc->vbus_mutex); @@ -2006,8 +2007,8 @@ static int atmel_usba_stop(struct usb_gadget *gadget) { struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); - if (gpio_is_valid(udc->vbus_pin)) - disable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + disable_irq(gpiod_to_irq(udc->vbus_pin)); if (fifo_mode == 0) udc->configured_ep = 1; @@ -2019,7 +2020,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget) return 0; } -#ifdef CONFIG_OF static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) { regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN, @@ -2055,7 +2055,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, { u32 val; const char *name; - enum of_gpio_flags flags; struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct device_node *pp; @@ -2075,9 +2074,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc->num_ep = 0; - udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, - &flags); - udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus", + GPIOD_IN); + udc->vbus_pin_inverted = gpiod_is_active_low(udc->vbus_pin); if (fifo_mode == 0) { pp = NULL; @@ -2204,75 +2203,10 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, err: return ERR_PTR(ret); } -#else -static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, - struct usba_udc *udc) -{ - return ERR_PTR(-ENOSYS); -} -#endif - -static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, - struct usba_udc *udc) -{ - struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct usba_ep *eps; - int i; - - if (!pdata) - return ERR_PTR(-ENXIO); - - eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * pdata->num_ep, - GFP_KERNEL); - if (!eps) - return ERR_PTR(-ENOMEM); - - udc->gadget.ep0 = &eps[0].ep; - - udc->vbus_pin = pdata->vbus_pin; - udc->vbus_pin_inverted = pdata->vbus_pin_inverted; - udc->num_ep = pdata->num_ep; - - INIT_LIST_HEAD(&eps[0].ep.ep_list); - - for (i = 0; i < pdata->num_ep; i++) { - struct usba_ep *ep = &eps[i]; - - ep->ep_regs = udc->regs + USBA_EPT_BASE(i); - ep->dma_regs = udc->regs + USBA_DMA_BASE(i); - ep->fifo = udc->fifo + USBA_FIFO_BASE(i); - ep->ep.ops = &usba_ep_ops; - ep->ep.name = pdata->ep[i].name; - ep->fifo_size = pdata->ep[i].fifo_size; - usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); - ep->udc = udc; - INIT_LIST_HEAD(&ep->queue); - ep->nr_banks = pdata->ep[i].nr_banks; - ep->index = pdata->ep[i].index; - ep->can_dma = pdata->ep[i].can_dma; - ep->can_isoc = pdata->ep[i].can_isoc; - - if (i == 0) { - ep->ep.caps.type_control = true; - } else { - ep->ep.caps.type_iso = ep->can_isoc; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - } - - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - - if (i) - list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - } - - return eps; -} static int usba_udc_probe(struct platform_device *pdev) { - struct resource *regs, *fifo; + struct resource *res; struct clk *pclk, *hclk; struct usba_udc *udc; int irq, ret, i; @@ -2284,10 +2218,18 @@ static int usba_udc_probe(struct platform_device *pdev) udc->gadget = usba_gadget_template; INIT_LIST_HEAD(&udc->gadget.ep_list); - regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); - if (!regs || !fifo) - return -ENXIO; + res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); + udc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(udc->regs)) + return PTR_ERR(udc->regs); + dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n", + res, udc->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); + udc->fifo = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(udc->fifo)) + return PTR_ERR(udc->fifo); + dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -2305,23 +2247,6 @@ static int usba_udc_probe(struct platform_device *pdev) udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; - udc->vbus_pin = -ENODEV; - - ret = -ENOMEM; - udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (!udc->regs) { - dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); - return ret; - } - dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", - (unsigned long)regs->start, udc->regs); - udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo)); - if (!udc->fifo) { - dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); - return ret; - } - dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", - (unsigned long)fifo->start, udc->fifo); platform_set_drvdata(pdev, udc); @@ -2335,10 +2260,7 @@ static int usba_udc_probe(struct platform_device *pdev) usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(pclk); - if (pdev->dev.of_node) - udc->usba_ep = atmel_udc_of_init(pdev, udc); - else - udc->usba_ep = usba_udc_pdata(pdev, udc); + udc->usba_ep = atmel_udc_of_init(pdev, udc); toggle_bias(udc, 0); @@ -2354,24 +2276,18 @@ static int usba_udc_probe(struct platform_device *pdev) } udc->irq = irq; - if (gpio_is_valid(udc->vbus_pin)) { - if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { - irq_set_status_flags(gpio_to_irq(udc->vbus_pin), - IRQ_NOAUTOEN); - ret = devm_request_threaded_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), NULL, + if (udc->vbus_pin) { + irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(&pdev->dev, + gpiod_to_irq(udc->vbus_pin), NULL, usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS, "atmel_usba_udc", udc); if (ret) { - udc->vbus_pin = -ENODEV; + udc->vbus_pin = NULL; dev_warn(&udc->pdev->dev, "failed to request vbus irq; " "assuming always on\n"); } - } else { - /* gpio_request fail so use -EINVAL for gpio_is_valid */ - udc->vbus_pin = -EINVAL; - } } ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); @@ -2423,9 +2339,9 @@ static int usba_udc_suspend(struct device *dev) * Device may wake up. We stay clocked if we failed * to request vbus irq, assuming always on. */ - if (gpio_is_valid(udc->vbus_pin)) { + if (udc->vbus_pin) { usba_stop(udc); - enable_irq_wake(gpio_to_irq(udc->vbus_pin)); + enable_irq_wake(gpiod_to_irq(udc->vbus_pin)); } out: @@ -2441,8 +2357,8 @@ static int usba_udc_resume(struct device *dev) if (!udc->driver) return 0; - if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin)) - disable_irq_wake(gpio_to_irq(udc->vbus_pin)); + if (device_may_wakeup(dev) && udc->vbus_pin) + disable_irq_wake(gpiod_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ mutex_lock(&udc->vbus_mutex); @@ -2462,7 +2378,7 @@ static struct platform_driver udc_driver = { .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, - .of_match_table = of_match_ptr(atmel_udc_dt_ids), + .of_match_table = atmel_udc_dt_ids, }, }; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 860a00a6fdd0..969ce8f3c3e2 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -7,6 +7,8 @@ #ifndef __LINUX_USB_GADGET_USBA_UDC_H__ #define __LINUX_USB_GADGET_USBA_UDC_H__ +#include <linux/gpio/consumer.h> + /* USB register offsets */ #define USBA_CTRL 0x0000 #define USBA_FNUM 0x0004 @@ -323,7 +325,7 @@ struct usba_udc { struct platform_device *pdev; const struct usba_udc_errata *errata; int irq; - int vbus_pin; + struct gpio_desc *vbus_pin; int vbus_pin_inverted; int num_ep; int configured_ep; diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index f40d4c13cfa4..03149b9d7ea7 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -151,7 +151,7 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) if (!bd_table) goto fail; - bd_table->start_bd = dma_pool_alloc(bdc->bd_table_pool, + bd_table->start_bd = dma_pool_zalloc(bdc->bd_table_pool, GFP_ATOMIC, &dma); if (!bd_table->start_bd) { @@ -167,7 +167,6 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) (unsigned long long)bd_table->dma, prev_table); ep->bd_list.bd_table_array[index] = bd_table; - memset(bd_table->start_bd, 0, bd_p_tab * sizeof(struct bdc_bd)); if (prev_table) chain_table(prev_table, bd_table, bd_p_tab); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 1f8b19d9cf97..50988b21a21b 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1482,7 +1482,7 @@ ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - return snprintf(buf, PAGE_SIZE, "%s\n", \ + return scnprintf(buf, PAGE_SIZE, "%s\n", \ usb_speed_string(udc->gadget->param)); \ } \ static DEVICE_ATTR_RO(name) @@ -1497,7 +1497,7 @@ ssize_t name##_show(struct device *dev, \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ struct usb_gadget *gadget = udc->gadget; \ \ - return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ } \ static DEVICE_ATTR_RO(name) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index e744d4b7bfed..baf72f95f0f1 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2366,7 +2366,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) { int ep = usb_pipeendpoint(urb->pipe); - return snprintf(buf, size, + return scnprintf(buf, size, "urb/%p %s ep%d%s%s len %d/%d\n", urb, ({ char *s; diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 26601bf4e7a9..70023d401079 100644 --- a/drivers/usb/gadget/udc/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h @@ -25,7 +25,7 @@ struct goku_udc_regs { # define INT_EP1DATASET 0x00040 # define INT_EP2DATASET 0x00080 # define INT_EP3DATASET 0x00100 -#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */ +#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */ # define INT_EP1NAK 0x00200 # define INT_EP2NAK 0x00400 # define INT_EP3NAK 0x00800 diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 7eb2b2b911e5..66143ab8c043 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -533,7 +533,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) { struct ab8500_usb *ab = (struct ab8500_usb *) data; - enum usb_phy_events event = UX500_MUSB_NONE; + enum usb_phy_events event = USB_EVENT_NONE; /* Link status will not be updated till phy is disabled. */ if (ab->mode == USB_HOST) { diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 74ba88297991..a53b89be5324 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -63,9 +63,9 @@ static void nop_reset(struct usb_phy_generic *nop) if (!nop->gpiod_reset) return; - gpiod_set_value(nop->gpiod_reset, 1); + gpiod_set_value_cansleep(nop->gpiod_reset, 1); usleep_range(10000, 20000); - gpiod_set_value(nop->gpiod_reset, 0); + gpiod_set_value_cansleep(nop->gpiod_reset, 0); } /* interface to regulator framework */ @@ -159,7 +159,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); - gpiod_set_value(nop->gpiod_reset, 1); + gpiod_set_value_cansleep(nop->gpiod_reset, 1); if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index fbec863350f6..e5aa24c1e4fd 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -578,7 +578,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x) * It must be called after DP is pulled up, which is used to * differentiate DCP and CDP. */ -enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) +static enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) { struct regmap *regmap = x->regmap_anatop; int val; diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index f668bfb708d3..0e8d23e51732 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -16,7 +16,7 @@ #include <linux/export.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_device.h> @@ -305,14 +305,10 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) { - unsigned long timeout = 2000; - do { - if ((readl(reg) & mask) == result) - return 0; - udelay(1); - timeout--; - } while (timeout); - return -1; + u32 tmp; + + return readl_poll_timeout(reg, tmp, (tmp & mask) == result, + 2000, 6000); } static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) |