summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2/hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2/hcd.c')
-rw-r--r--drivers/usb/dwc2/hcd.c1972
1 files changed, 1289 insertions, 683 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 740c7e86d31b..30eb8506617c 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1,37 +1,8 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* hcd.c - DesignWare HS OTG Controller host-mode routines
*
* Copyright (C) 2004-2013 Synopsys, Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option) any
- * later version.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
@@ -51,12 +22,11 @@
#include <linux/usb/hcd.h>
#include <linux/usb/ch11.h>
+#include <linux/usb/of.h>
#include "core.h"
#include "hcd.h"
-static void dwc2_port_resume(struct dwc2_hsotg *hsotg);
-
/*
* =========================================================================
* Host Core Layer Functions
@@ -74,10 +44,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
u32 intmsk;
/* Clear any pending OTG Interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GOTGINT);
+ dwc2_writel(hsotg, 0xffffffff, GOTGINT);
/* Clear any pending interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Enable the interrupts in the GINTMSK */
intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT;
@@ -90,197 +60,15 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
GINTSTS_SESSREQINT;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
-}
-
-/*
- * Initializes the FSLSPClkSel field of the HCFG register depending on the
- * PHY type
- */
-static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
-{
- u32 hcfg, val;
-
- if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
- hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
- hsotg->params.ulpi_fs_ls) ||
- hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
- /* Full speed PHY */
- val = HCFG_FSLSPCLKSEL_48_MHZ;
- } else {
- /* High speed PHY running at full speed or high speed */
- val = HCFG_FSLSPCLKSEL_30_60_MHZ;
- }
-
- dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
- hcfg = dwc2_readl(hsotg->regs + HCFG);
- hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
- hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
-}
-
-static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
- u32 usbcfg, ggpio, i2cctl;
- int retval = 0;
-
- /*
- * core_init() is now called on every switch so only call the
- * following for the first time through
- */
- if (select_phy) {
- dev_dbg(hsotg->dev, "FS PHY selected\n");
-
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- if (!(usbcfg & GUSBCFG_PHYSEL)) {
- usbcfg |= GUSBCFG_PHYSEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
-
- /* Reset after a PHY select */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
-
- if (retval) {
- dev_err(hsotg->dev,
- "%s: Reset failed, aborting", __func__);
- return retval;
- }
- }
-
- if (hsotg->params.activate_stm_fs_transceiver) {
- ggpio = dwc2_readl(hsotg->regs + GGPIO);
- if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
- dev_dbg(hsotg->dev, "Activating transceiver\n");
- /*
- * STM32F4x9 uses the GGPIO register as general
- * core configuration register.
- */
- ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
- dwc2_writel(ggpio, hsotg->regs + GGPIO);
- }
- }
- }
-
- /*
- * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
- * do this on HNP Dev/Host mode switches (done in dev_init and
- * host_init).
- */
- if (dwc2_is_host_mode(hsotg))
- dwc2_init_fs_ls_pclk_sel(hsotg);
-
- if (hsotg->params.i2c_enable) {
- dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
-
- /* Program GUSBCFG.OtgUtmiFsSel to I2C */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
-
- /* Program GI2CCTL.I2CEn */
- i2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
- i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
- i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
- i2cctl &= ~GI2CCTL_I2CEN;
- dwc2_writel(i2cctl, hsotg->regs + GI2CCTL);
- i2cctl |= GI2CCTL_I2CEN;
- dwc2_writel(i2cctl, hsotg->regs + GI2CCTL);
- }
-
- return retval;
-}
-
-static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
- u32 usbcfg, usbcfg_old;
- int retval = 0;
-
- if (!select_phy)
- return 0;
-
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- usbcfg_old = usbcfg;
-
- /*
- * HS PHY parameters. These parameters are preserved during soft reset
- * so only program the first time. Do a soft reset immediately after
- * setting phyif.
- */
- switch (hsotg->params.phy_type) {
- case DWC2_PHY_TYPE_PARAM_ULPI:
- /* ULPI interface */
- dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
- usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
- usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
- if (hsotg->params.phy_ulpi_ddr)
- usbcfg |= GUSBCFG_DDRSEL;
- break;
- case DWC2_PHY_TYPE_PARAM_UTMI:
- /* UTMI+ interface */
- dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
- usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
- if (hsotg->params.phy_utmi_width == 16)
- usbcfg |= GUSBCFG_PHYIF16;
- break;
- default:
- dev_err(hsotg->dev, "FS PHY selected at HS!\n");
- break;
- }
-
- if (usbcfg != usbcfg_old) {
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
-
- /* Reset after setting the PHY parameters */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
- if (retval) {
- dev_err(hsotg->dev,
- "%s: Reset failed, aborting", __func__);
- return retval;
- }
- }
+ if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm)
+ intmsk |= GINTSTS_LPMTRANRCVD;
- return retval;
-}
-
-static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
-{
- u32 usbcfg;
- int retval = 0;
-
- if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
- hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
- hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
- /* If FS/LS mode with FS/LS PHY */
- retval = dwc2_fs_phy_init(hsotg, select_phy);
- if (retval)
- return retval;
- } else {
- /* High speed PHY */
- retval = dwc2_hs_phy_init(hsotg, select_phy);
- if (retval)
- return retval;
- }
-
- if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
- hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
- hsotg->params.ulpi_fs_ls) {
- dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- usbcfg |= GUSBCFG_ULPI_FS_LS;
- usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
- } else {
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- usbcfg &= ~GUSBCFG_ULPI_FS_LS;
- usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
- }
-
- return retval;
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
{
- u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
switch (hsotg->hw_params.arch) {
case GHWCFG2_EXT_DMA_ARCH:
@@ -302,24 +90,12 @@ 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);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
return 0;
}
@@ -328,24 +104,20 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
{
u32 usbcfg;
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP);
switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
- if (hsotg->params.otg_cap ==
- DWC2_CAP_PARAM_HNP_SRP_CAPABLE)
+ if (hsotg->params.otg_caps.hnp_support &&
+ hsotg->params.otg_caps.srp_support)
usbcfg |= GUSBCFG_HNPCAP;
- if (hsotg->params.otg_cap !=
- DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
- usbcfg |= GUSBCFG_SRPCAP;
- break;
+ fallthrough;
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
- if (hsotg->params.otg_cap !=
- DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+ if (hsotg->params.otg_caps.srp_support)
usbcfg |= GUSBCFG_SRPCAP;
break;
@@ -356,7 +128,23 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
break;
}
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
+}
+
+static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->vbus_supply)
+ return regulator_enable(hsotg->vbus_supply);
+
+ return 0;
+}
+
+static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->vbus_supply)
+ return regulator_disable(hsotg->vbus_supply);
+
+ return 0;
}
/**
@@ -371,16 +159,16 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "%s()\n", __func__);
/* Disable all interrupts */
- dwc2_writel(0, hsotg->regs + GINTMSK);
- dwc2_writel(0, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, 0, GINTMSK);
+ dwc2_writel(hsotg, 0, HAINTMSK);
/* Enable the common interrupts */
dwc2_enable_common_interrupts(hsotg);
/* Enable host mode interrupts without disturbing common interrupts */
- intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ intmsk = dwc2_readl(hsotg, GINTMSK);
intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
/**
@@ -390,12 +178,12 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
*/
static void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
{
- u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 intmsk = dwc2_readl(hsotg, GINTMSK);
/* Disable host mode interrupts without disturbing common interrupts */
intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT);
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
/*
@@ -475,37 +263,37 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
dwc2_calculate_dynamic_fifo(hsotg);
/* Rx FIFO */
- grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
+ grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
grxfsiz &= ~GRXFSIZ_DEPTH_MASK;
grxfsiz |= params->host_rx_fifo_size <<
GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK;
- dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ);
+ dwc2_writel(hsotg, grxfsiz, GRXFSIZ);
dev_dbg(hsotg->dev, "new grxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GRXFSIZ));
+ dwc2_readl(hsotg, GRXFSIZ));
/* Non-periodic Tx FIFO */
dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GNPTXFSIZ));
nptxfsiz = params->host_nperio_tx_fifo_size <<
FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
nptxfsiz |= params->host_rx_fifo_size <<
FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
- dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ);
+ dwc2_writel(hsotg, nptxfsiz, GNPTXFSIZ);
dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GNPTXFSIZ));
/* Periodic Tx FIFO */
dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + HPTXFSIZ));
+ dwc2_readl(hsotg, HPTXFSIZ));
hptxfsiz = params->host_perio_tx_fifo_size <<
FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
hptxfsiz |= (params->host_rx_fifo_size +
params->host_nperio_tx_fifo_size) <<
FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
- dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ);
+ dwc2_writel(hsotg, hptxfsiz, HPTXFSIZ);
dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + HPTXFSIZ));
+ dwc2_readl(hsotg, HPTXFSIZ));
if (hsotg->params.en_multiple_tx_fifo &&
hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_91a) {
@@ -514,14 +302,14 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
* Global DFIFOCFG calculation for Host mode -
* include RxFIFO, NPTXFIFO and HPTXFIFO
*/
- dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
+ dfifocfg = dwc2_readl(hsotg, GDFIFOCFG);
dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK;
dfifocfg |= (params->host_rx_fifo_size +
params->host_nperio_tx_fifo_size +
params->host_perio_tx_fifo_size) <<
GDFIFOCFG_EPINFOBASE_SHIFT &
GDFIFOCFG_EPINFOBASE_MASK;
- dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG);
+ dwc2_writel(hsotg, dfifocfg, GDFIFOCFG);
}
}
@@ -541,8 +329,8 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
u32 hprt0;
int clock = 60; /* default value */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) &&
!(usbcfg & GUSBCFG_PHYIF16))
@@ -578,13 +366,12 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
* dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination
* buffer
*
- * @core_if: Programming view of DWC_otg controller
+ * @hsotg: Programming view of DWC_otg controller
* @dest: Destination buffer for the packet
* @bytes: Number of bytes to copy to the destination
*/
void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
{
- u32 __iomem *fifo = hsotg->regs + HCFIFO(0);
u32 *data_buf = (u32 *)dest;
int word_count = (bytes + 3) / 4;
int i;
@@ -598,7 +385,7 @@ void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes);
for (i = 0; i < word_count; i++, data_buf++)
- *data_buf = dwc2_readl(fifo);
+ *data_buf = dwc2_readl(hsotg, HCFIFO(0));
}
/**
@@ -627,10 +414,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
if (!chan)
return;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
- hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chan->hc_num));
- hc_dma = dwc2_readl(hsotg->regs + HCDMA(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+ hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chan->hc_num));
+ hc_dma = dwc2_readl(hsotg, HCDMA(chan->hc_num));
dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan);
dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n",
@@ -653,6 +440,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive,
qh_list_entry)
dev_dbg(hsotg->dev, " %p\n", qh);
+ dev_dbg(hsotg->dev, " NP waiting sched:\n");
+ list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting,
+ qh_list_entry)
+ dev_dbg(hsotg->dev, " %p\n", qh);
dev_dbg(hsotg->dev, " NP active sched:\n");
list_for_each_entry(qh, &hsotg->non_periodic_sched_active,
qh_list_entry)
@@ -774,7 +565,7 @@ static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg,
break;
}
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
}
@@ -811,7 +602,7 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
}
}
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
}
@@ -832,16 +623,16 @@ static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
}
/* Enable the top level host channel interrupt */
- intmsk = dwc2_readl(hsotg->regs + HAINTMSK);
+ intmsk = dwc2_readl(hsotg, HAINTMSK);
intmsk |= 1 << chan->hc_num;
- dwc2_writel(intmsk, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, intmsk, HAINTMSK);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk);
/* Make sure host channel interrupts are enabled */
- intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ intmsk = dwc2_readl(hsotg, GINTMSK);
intmsk |= GINTSTS_HCHINT;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk);
}
@@ -870,7 +661,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
/* Clear old interrupt conditions for this host channel */
hcintmsk = 0xffffffff;
hcintmsk &= ~HCINTMSK_RESERVED14_31;
- dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINT(hc_num));
/* Enable channel interrupts required for this transfer */
dwc2_hc_enable_ints(hsotg, chan);
@@ -887,7 +678,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
hcchar |= HCCHAR_LSPDDEV;
hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK;
hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(hc_num));
if (dbg_hc(chan)) {
dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n",
hc_num, hcchar);
@@ -941,7 +732,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
}
}
- dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num));
+ dwc2_writel(hsotg, hcsplt, HCSPLT(hc_num));
}
/**
@@ -979,6 +770,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);
@@ -993,14 +802,14 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
u32 hcintmsk = HCINTMSK_CHHLTD;
dev_vdbg(hsotg->dev, "dequeue/error\n");
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
/*
* Make sure no other interrupts besides halt are currently
* pending. Handling another interrupt could cause a crash due
* to the QTD and QH state.
*/
- dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num));
+ dwc2_writel(hsotg, ~hcintmsk, HCINT(chan->hc_num));
/*
* Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
@@ -1009,7 +818,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
*/
chan->halt_status = halt_status;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
if (!(hcchar & HCCHAR_CHENA)) {
/*
* The channel is either already halted or it hasn't
@@ -1037,7 +846,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
return;
}
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
/* No need to set the bit in DDMA for disabling the channel */
/* TODO check it everywhere channel is disabled */
@@ -1060,7 +869,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
chan->ep_type == USB_ENDPOINT_XFER_BULK) {
dev_vdbg(hsotg->dev, "control/bulk\n");
- nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS);
+ nptxsts = dwc2_readl(hsotg, GNPTXSTS);
if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) {
dev_vdbg(hsotg->dev, "Disabling channel\n");
hcchar &= ~HCCHAR_CHENA;
@@ -1068,7 +877,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
} else {
if (dbg_perio())
dev_vdbg(hsotg->dev, "isoc/intr\n");
- hptxsts = dwc2_readl(hsotg->regs + HPTXSTS);
+ hptxsts = dwc2_readl(hsotg, HPTXSTS);
if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 ||
hsotg->queuing_high_bandwidth) {
if (dbg_perio())
@@ -1081,7 +890,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
dev_vdbg(hsotg->dev, "DMA enabled\n");
}
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
chan->halt_status = halt_status;
if (hcchar & HCCHAR_CHENA) {
@@ -1130,10 +939,10 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
* Clear channel interrupt enables and any unhandled channel interrupt
* conditions
*/
- dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, 0, HCINTMSK(chan->hc_num));
hcintmsk = 0xffffffff;
hcintmsk &= ~HCINTMSK_RESERVED14_31;
- dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINT(chan->hc_num));
}
/**
@@ -1161,7 +970,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
/*
* Try to figure out if we're an even or odd frame. If we set
- * even and the current frame number is even the the transfer
+ * even and the current frame number is even the transfer
* will happen immediately. Similar if both are odd. If one is
* even and the other is odd then the transfer will happen when
* the frame number ticks.
@@ -1187,7 +996,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
!chan->do_split) ? chan->speed : USB_SPEED_HIGH;
/* See how many bytes are in the periodic FIFO right now */
- fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
+ fifo_space = (dwc2_readl(hsotg, HPTXSTS) &
TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
bytes_in_fifo = sizeof(u32) *
(hsotg->params.host_perio_tx_fifo_size -
@@ -1288,14 +1097,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
u32 remaining_count;
u32 byte_count;
u32 dword_count;
- u32 __iomem *data_fifo;
u32 *data_buf = (u32 *)chan->xfer_buf;
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
- data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num));
-
remaining_count = chan->xfer_len - chan->xfer_count;
if (remaining_count > chan->max_packet)
byte_count = chan->max_packet;
@@ -1307,13 +1113,13 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
if (((unsigned long)data_buf & 0x3) == 0) {
/* xfer_buf is DWORD aligned */
for (i = 0; i < dword_count; i++, data_buf++)
- dwc2_writel(*data_buf, data_fifo);
+ dwc2_writel(hsotg, *data_buf, HCFIFO(chan->hc_num));
} else {
/* xfer_buf is not DWORD aligned */
for (i = 0; i < dword_count; i++, data_buf++) {
u32 data = data_buf[0] | data_buf[1] << 8 |
data_buf[2] << 16 | data_buf[3] << 24;
- dwc2_writel(data, data_fifo);
+ dwc2_writel(hsotg, data, HCFIFO(chan->hc_num));
}
}
@@ -1342,12 +1148,12 @@ static void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
hctsiz = TSIZ_DOPNG;
hctsiz |= 1 << TSIZ_PKTCNT_SHIFT;
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar |= HCCHAR_CHENA;
hcchar &= ~HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
}
/**
@@ -1472,19 +1278,20 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
if (num_packets > max_hc_pkt_count) {
num_packets = max_hc_pkt_count;
chan->xfer_len = num_packets * chan->max_packet;
+ } else if (chan->ep_is_in) {
+ /*
+ * Always program an integral # of max packets
+ * for IN transfers.
+ * Note: This assumes that the input buffer is
+ * aligned and sized accordingly.
+ */
+ chan->xfer_len = num_packets * chan->max_packet;
}
} else {
/* Need 1 packet for transfer length of 0 */
num_packets = 1;
}
- if (chan->ep_is_in)
- /*
- * Always program an integral # of max packets for IN
- * transfers
- */
- chan->xfer_len = num_packets * chan->max_packet;
-
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC)
/*
@@ -1507,7 +1314,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK;
hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
TSIZ_SC_MC_PID_MASK;
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
if (dbg_hc(chan)) {
dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n",
hctsiz, chan->hc_num);
@@ -1526,22 +1333,31 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
}
if (hsotg->params.host_dma) {
- dwc2_writel((u32)chan->xfer_dma,
- hsotg->regs + HCDMA(chan->hc_num));
+ dma_addr_t dma_addr;
+
+ if (chan->align_buf) {
+ if (dbg_hc(chan))
+ dev_vdbg(hsotg->dev, "align_buf\n");
+ dma_addr = chan->align_buf;
+ } else {
+ dma_addr = chan->xfer_dma;
+ }
+ dwc2_writel(hsotg, (u32)dma_addr, HCDMA(chan->hc_num));
+
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
- (unsigned long)chan->xfer_dma, chan->hc_num);
+ (unsigned long)dma_addr, chan->hc_num);
}
/* Start the split */
if (chan->do_split) {
- u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num));
+ u32 hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
hcsplt |= HCSPLT_SPLTENA;
- dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num));
+ dwc2_writel(hsotg, hcsplt, HCSPLT(chan->hc_num));
}
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
@@ -1560,7 +1376,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
(hcchar & HCCHAR_MULTICNT_MASK) >>
HCCHAR_MULTICNT_SHIFT);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
chan->hc_num);
@@ -1618,18 +1434,18 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1);
}
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
chan->desc_list_sz, DMA_TO_DEVICE);
- dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
+ dwc2_writel(hsotg, chan->desc_list_addr, HCDMA(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
&chan->desc_list_addr, chan->hc_num);
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
HCCHAR_MULTICNT_MASK;
@@ -1648,7 +1464,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
(hcchar & HCCHAR_MULTICNT_MASK) >>
HCCHAR_MULTICNT_SHIFT);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
chan->hc_num);
@@ -1705,7 +1521,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
* transfer completes, the extra requests for the channel will
* be flushed.
*/
- u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ u32 hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
hcchar |= HCCHAR_CHENA;
@@ -1713,7 +1529,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n",
hcchar);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
chan->requests++;
return 1;
}
@@ -1723,7 +1539,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
if (chan->xfer_count < chan->xfer_len) {
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
- u32 hcchar = dwc2_readl(hsotg->regs +
+ u32 hcchar = dwc2_readl(hsotg,
HCCHAR(chan->hc_num));
dwc2_hc_set_even_odd_frame(hsotg, chan,
@@ -1812,6 +1628,7 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
{
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
+ dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
@@ -1836,7 +1653,7 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg)
*/
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
queue_delayed_work(hsotg->wq_otg, &hsotg->start_work,
@@ -1857,11 +1674,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
channel = hsotg->hc_ptr_array[i];
if (!list_empty(&channel->hc_list_entry))
continue;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
if (hcchar & HCCHAR_CHENA) {
hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR);
hcchar |= HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
}
}
}
@@ -1870,11 +1687,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
channel = hsotg->hc_ptr_array[i];
if (!list_empty(&channel->hc_list_entry))
continue;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
if (hcchar & HCCHAR_CHENA) {
/* Halt the channel */
hcchar |= HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
}
dwc2_hc_cleanup(hsotg, channel);
@@ -1934,11 +1751,11 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
* interrupt mask and status bits and disabling subsequent host
* channel interrupts.
*/
- intr = dwc2_readl(hsotg->regs + GINTMSK);
+ intr = dwc2_readl(hsotg, GINTMSK);
intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT);
- dwc2_writel(intr, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intr, GINTMSK);
intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT;
- dwc2_writel(intr, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, intr, GINTSTS);
/*
* Turn off the vbus power only if the core has transitioned to device
@@ -1948,7 +1765,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->op_state != OTG_STATE_A_SUSPEND) {
dev_dbg(hsotg->dev, "Disconnect: PortPower off\n");
- dwc2_writel(0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, 0, HPRT0);
}
dwc2_disable_host_interrupts(hsotg);
@@ -1976,7 +1793,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
* and won't get any future interrupts to handle the connect.
*/
if (!force) {
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
dwc2_hcd_connect(hsotg);
}
@@ -2020,7 +1837,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
/* Turn off the vbus power */
dev_dbg(hsotg->dev, "PortPower off\n");
- dwc2_writel(0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, 0, HPRT0);
}
/* Caller must hold driver lock */
@@ -2044,7 +1861,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
if ((dev_speed == USB_SPEED_LOW) &&
(hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) &&
(hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) {
- u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ u32 hprt0 = dwc2_readl(hsotg, HPRT0);
u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
if (prtspd == HPRT0_SPD_FULL_SPEED)
@@ -2063,7 +1880,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
return retval;
}
- intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg, GINTMSK);
if (!(intr_mask & GINTSTS_SOF)) {
enum dwc2_transaction_type tr_type;
@@ -2221,14 +2038,14 @@ 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;
dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
/* Set ULPI External VBUS bit if needed */
usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV;
@@ -2240,7 +2057,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
if (hsotg->params.ts_dline)
usbcfg |= GUSBCFG_TERMSELDLPULSE;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/*
* Reset the Controller
@@ -2250,7 +2067,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__);
@@ -2274,9 +2091,9 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
dwc2_gusbcfg_init(hsotg);
/* Program the GOTGCTL register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_OTGVER;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
/* Clear the SRP success bit for FS-I2c */
hsotg->srp_success = 0;
@@ -2311,20 +2128,32 @@ 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, GUSBCFG);
+ usbcfg |= GUSBCFG_TOUTCAL(7);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
/* Restart the Phy Clock */
- dwc2_writel(0, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, 0, PCGCTL);
/* Initialize Host Configuration Register */
dwc2_init_fs_ls_pclk_sel(hsotg);
if (hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
hsotg->params.speed == DWC2_SPEED_PARAM_LOW) {
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_FSLSSUPP;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
/*
@@ -2333,9 +2162,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
* and its value must not be changed during runtime.
*/
if (hsotg->params.reload_ctl) {
- hfir = dwc2_readl(hsotg->regs + HFIR);
+ hfir = dwc2_readl(hsotg, HFIR);
hfir |= HFIR_RLDCTRL;
- dwc2_writel(hfir, hsotg->regs + HFIR);
+ dwc2_writel(hsotg, hfir, HFIR);
}
if (hsotg->params.dma_desc_enable) {
@@ -2352,9 +2181,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
"falling back to buffer DMA mode.\n");
hsotg->params.dma_desc_enable = false;
} else {
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_DESCDMA;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
}
@@ -2363,18 +2192,18 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* TODO - check this */
/* Clear Host Set HNP Enable in the OTG Control Register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_HSTSETHNPEN;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
/* Make sure the FIFOs are flushed */
dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */);
dwc2_flush_rx_fifo(hsotg);
/* Clear Host Set HNP Enable in the OTG Control Register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_HSTSETHNPEN;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
if (!hsotg->params.dma_desc_enable) {
int num_channels, i;
@@ -2383,36 +2212,39 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* Flush out any leftover queued requests */
num_channels = hsotg->params.host_channels;
for (i = 0; i < num_channels; i++) {
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
- hcchar &= ~HCCHAR_CHENA;
- hcchar |= HCCHAR_CHDIS;
- hcchar &= ~HCCHAR_EPDIR;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
+ if (hcchar & HCCHAR_CHENA) {
+ hcchar &= ~HCCHAR_CHENA;
+ hcchar |= HCCHAR_CHDIS;
+ hcchar &= ~HCCHAR_EPDIR;
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
+ }
}
/* 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;
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
+ if (hcchar & HCCHAR_CHENA) {
+ hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
+ hcchar &= ~HCCHAR_EPDIR;
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
+ dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
+ __func__, i);
+
+ 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);
}
- udelay(1);
- } while (hcchar & HCCHAR_CHENA);
+ }
}
}
+ /* 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) {
@@ -2422,7 +2254,7 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
!!(hprt0 & HPRT0_PWR));
if (!(hprt0 & HPRT0_PWR)) {
hprt0 |= HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
}
@@ -2574,36 +2406,68 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
}
}
-#define DWC2_USB_DMA_ALIGN 4
+static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
+ struct dwc2_qh *qh,
+ struct dwc2_host_chan *chan)
+{
+ if (!hsotg->unaligned_cache ||
+ chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
+ return -ENOMEM;
-struct dma_aligned_buffer {
- void *kmalloc_ptr;
- void *old_xfer_buffer;
- u8 data[0];
-};
+ if (!qh->dw_align_buf) {
+ qh->dw_align_buf = kmem_cache_alloc(hsotg->unaligned_cache,
+ GFP_ATOMIC | GFP_DMA);
+ if (!qh->dw_align_buf)
+ return -ENOMEM;
+ }
+
+ qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf,
+ DWC2_KMEM_UNALIGNED_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
+ dev_err(hsotg->dev, "can't map align_buf\n");
+ chan->align_buf = 0;
+ return -EINVAL;
+ }
+
+ chan->align_buf = qh->dw_align_buf_dma;
+ return 0;
+}
+
+#define DWC2_USB_DMA_ALIGN 4
static void dwc2_free_dma_aligned_buffer(struct urb *urb)
{
- struct dma_aligned_buffer *temp;
+ void *stored_xfer_buffer;
+ size_t length;
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
return;
- temp = container_of(urb->transfer_buffer,
- struct dma_aligned_buffer, data);
+ /* Restore urb->transfer_buffer from the end of the allocated area */
+ memcpy(&stored_xfer_buffer,
+ PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
+ dma_get_cache_alignment()),
+ sizeof(urb->transfer_buffer));
- if (usb_urb_dir_in(urb))
- memcpy(temp->old_xfer_buffer, temp->data,
- urb->transfer_buffer_length);
- urb->transfer_buffer = temp->old_xfer_buffer;
- kfree(temp->kmalloc_ptr);
+ if (usb_urb_dir_in(urb)) {
+ if (usb_pipeisoc(urb->pipe))
+ length = urb->transfer_buffer_length;
+ else
+ length = urb->actual_length;
+
+ memcpy(stored_xfer_buffer, urb->transfer_buffer, length);
+ }
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = stored_xfer_buffer;
urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
}
static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
- struct dma_aligned_buffer *temp, *kmalloc_ptr;
+ void *kmalloc_ptr;
size_t kmalloc_size;
if (urb->num_sgs || urb->sg ||
@@ -2611,22 +2475,31 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
return 0;
- /* Allocate a buffer with enough padding for alignment */
+ /*
+ * Allocate a buffer with enough padding for original transfer_buffer
+ * pointer. This allocation is guaranteed to be aligned properly for
+ * DMA
+ */
kmalloc_size = urb->transfer_buffer_length +
- sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
+ (dma_get_cache_alignment() - 1) +
+ sizeof(urb->transfer_buffer);
kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
if (!kmalloc_ptr)
return -ENOMEM;
- /* Position our struct dma_aligned_buffer such that data is aligned */
- temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
- temp->kmalloc_ptr = kmalloc_ptr;
- temp->old_xfer_buffer = urb->transfer_buffer;
+ /*
+ * Position value of original urb->transfer_buffer pointer to the end
+ * of allocation for later referencing
+ */
+ memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length,
+ dma_get_cache_alignment()),
+ &urb->transfer_buffer, sizeof(urb->transfer_buffer));
+
if (usb_urb_dir_out(urb))
- memcpy(temp->data, urb->transfer_buffer,
+ memcpy(kmalloc_ptr, urb->transfer_buffer,
urb->transfer_buffer_length);
- urb->transfer_buffer = temp->data;
+ urb->transfer_buffer = kmalloc_ptr;
urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
@@ -2705,7 +2578,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
chan->dev_addr = dwc2_hcd_get_dev_addr(&urb->pipe_info);
chan->ep_num = dwc2_hcd_get_ep_num(&urb->pipe_info);
chan->speed = qh->dev_speed;
- chan->max_packet = dwc2_max_packet(qh->maxp);
+ chan->max_packet = qh->maxp;
chan->xfer_started = 0;
chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS;
@@ -2751,13 +2624,39 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
/* Set the transfer attributes */
dwc2_hc_init_xfer(hsotg, chan, qtd);
+ /* For non-dword aligned buffers */
+ if (hsotg->params.host_dma && qh->do_split &&
+ chan->ep_is_in && (chan->xfer_dma & 0x3)) {
+ dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
+ if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
+ dev_err(hsotg->dev,
+ "Failed to allocate memory to handle non-aligned buffer\n");
+ /* Add channel back to free list */
+ chan->align_buf = 0;
+ chan->multi_count = 0;
+ list_add_tail(&chan->hc_list_entry,
+ &hsotg->free_hc_list);
+ qtd->in_process = 0;
+ qh->channel = NULL;
+ return -ENOMEM;
+ }
+ } else {
+ /*
+ * We assume that DMA is always aligned in non-split
+ * case or split out case. Warn if not.
+ */
+ WARN_ON_ONCE(hsotg->params.host_dma &&
+ (chan->xfer_dma & 0x3));
+ chan->align_buf = 0;
+ }
+
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC)
/*
* This value may be modified when the transfer is started
* to reflect the actual transfer length
*/
- chan->multi_count = dwc2_hb_mult(qh->maxp);
+ chan->multi_count = qh->maxp_mult;
if (hsotg->params.dma_desc_enable) {
chan->desc_list_addr = qh->desc_list_dma;
@@ -2802,8 +2701,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
hsotg->available_host_channels--;
}
qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
- if (dwc2_assign_and_init_hc(hsotg, qh))
+ if (dwc2_assign_and_init_hc(hsotg, qh)) {
+ if (hsotg->params.uframe_sched)
+ hsotg->available_host_channels++;
break;
+ }
/*
* Move the QH from the periodic ready schedule to the
@@ -2836,8 +2738,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
hsotg->available_host_channels--;
}
- if (dwc2_assign_and_init_hc(hsotg, qh))
+ if (dwc2_assign_and_init_hc(hsotg, qh)) {
+ if (hsotg->params.uframe_sched)
+ hsotg->available_host_channels++;
break;
+ }
/*
* Move the QH from the non-periodic inactive schedule to the
@@ -2891,7 +2796,7 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
list_move_tail(&chan->split_order_list_entry,
&hsotg->split_order);
- if (hsotg->params.host_dma) {
+ if (hsotg->params.host_dma && chan->qh) {
if (hsotg->params.dma_desc_enable) {
if (!chan->xfer_started ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
@@ -2961,7 +2866,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
if (dbg_perio())
dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
- tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ tx_status = dwc2_readl(hsotg, HPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -2976,7 +2881,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
qh_ptr = hsotg->periodic_sched_assigned.next;
while (qh_ptr != &hsotg->periodic_sched_assigned) {
- tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ tx_status = dwc2_readl(hsotg, HPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
if (qspcavail == 0) {
@@ -3046,10 +2951,10 @@ exit:
* level to ensure that new requests are loaded as
* soon as possible.)
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
if (!(gintmsk & GINTSTS_PTXFEMP)) {
gintmsk |= GINTSTS_PTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
} else {
/*
@@ -3059,10 +2964,10 @@ exit:
* handlers to queue more transactions as transfer
* states change.
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
if (gintmsk & GINTSTS_PTXFEMP) {
gintmsk &= ~GINTSTS_PTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3091,7 +2996,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n");
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -3114,7 +3019,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
* available in the request queue or the Tx FIFO
*/
do {
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
if (!hsotg->params.host_dma && qspcavail == 0) {
@@ -3151,7 +3056,7 @@ next:
} while (hsotg->non_periodic_qh_ptr != orig_qh_ptr);
if (!hsotg->params.host_dma) {
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -3171,9 +3076,9 @@ next:
* level to ensure that new requests are loaded as
* soon as possible.)
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
} else {
/*
* Disable the Tx FIFO empty interrupt since there are
@@ -3182,9 +3087,9 @@ next:
* handlers to queue more transactions as transfer
* states change.
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3221,10 +3126,10 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
* Ensure NP Tx FIFO empty interrupt is disabled when
* there are no non-periodic transfers to process
*/
- u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3239,13 +3144,14 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dev_dbg(hsotg->dev, "%s()\n", __func__);
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",
!!(gotgctl & GOTGCTL_CONID_B));
/* 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) {
@@ -3264,7 +3170,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
* check it again and jump to host mode if that was
* the case.
*/
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (!(gotgctl & GOTGCTL_CONID_B))
goto host;
if (++count > 250)
@@ -3273,13 +3179,23 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
if (count > 250)
dev_err(hsotg->dev,
"Connection id status change timed out\n");
+
+ /*
+ * Exit Partial Power Down without restoring registers.
+ * No need to check the return value as registers
+ * are not being restored.
+ */
+ if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
+ dwc2_exit_partial_power_down(hsotg, 0, false);
+
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
- dwc2_hsotg_disconnect(hsotg);
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:
@@ -3296,8 +3212,12 @@ host:
if (count > 250)
dev_err(hsotg->dev,
"Connection id status change timed out\n");
- hsotg->op_state = OTG_STATE_A_HOST;
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dwc2_hsotg_disconnect(hsotg);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ hsotg->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
@@ -3305,9 +3225,9 @@ host:
}
}
-static void dwc2_wakeup_detected(unsigned long data)
+static void dwc2_wakeup_detected(struct timer_list *t)
{
- struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data;
+ struct dwc2_hsotg *hsotg = timer_container_of(hsotg, t, wkp_timer);
u32 hprt0;
dev_dbg(hsotg->dev, "%s()\n", __func__);
@@ -3319,9 +3239,9 @@ static void dwc2_wakeup_detected(unsigned long data)
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0);
hprt0 &= ~HPRT0_RES;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
- dwc2_readl(hsotg->regs + HPRT0));
+ dwc2_readl(hsotg, HPRT0));
dwc2_hcd_rem_wakeup(hsotg);
hsotg->bus_suspended = false;
@@ -3337,48 +3257,70 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg)
return hcd->self.b_hnp_enable;
}
-/* Must NOT be called with interrupt disabled or spinlock held */
-static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
+/**
+ * dwc2_port_suspend() - Put controller in suspend mode for host.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @windex: The control request wIndex field
+ *
+ * Return: non-zero if failed to enter suspend mode for host.
+ *
+ * This function is for entering Host mode suspend.
+ * Must NOT be called with interrupt disabled or spinlock held.
+ */
+int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
{
unsigned long flags;
- u32 hprt0;
u32 pcgctl;
u32 gotgctl;
+ int ret = 0;
dev_dbg(hsotg->dev, "%s()\n", __func__);
spin_lock_irqsave(&hsotg->lock, flags);
if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) {
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl |= GOTGCTL_HSTSETHNPEN;
- dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
hsotg->op_state = OTG_STATE_A_SUSPEND;
}
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_SUSP;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
-
- hsotg->bus_suspended = true;
-
- /*
- * If hibernation is supported, Phy clock will be suspended
- * after registers are backuped.
- */
- if (!hsotg->params.hibernation) {
- /* Suspend the Phy Clock */
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
- pcgctl |= PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
- udelay(10);
+ switch (hsotg->params.power_down) {
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ ret = dwc2_enter_partial_power_down(hsotg);
+ if (ret)
+ dev_err(hsotg->dev,
+ "enter partial_power_down failed.\n");
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ /*
+ * Perform spin unlock and lock because in
+ * "dwc2_host_enter_hibernation()" function there is a spinlock
+ * logic which prevents servicing of any IRQ during entering
+ * hibernation.
+ */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ ret = dwc2_enter_hibernation(hsotg, 1);
+ if (ret)
+ dev_err(hsotg->dev, "enter hibernation failed.\n");
+ spin_lock_irqsave(&hsotg->lock, flags);
+ break;
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ /*
+ * If not hibernation nor partial power down are supported,
+ * clock gating is used to save power.
+ */
+ if (!hsotg->params.no_clock_gating)
+ dwc2_host_enter_clock_gating(hsotg);
+ break;
}
/* For HNP the bus must be suspended for at least 200ms */
if (dwc2_host_is_b_hnp_enabled(hsotg)) {
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -3386,44 +3328,54 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
} else {
spin_unlock_irqrestore(&hsotg->lock, flags);
}
+
+ return ret;
}
-/* Must NOT be called with interrupt disabled or spinlock held */
-static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
+/**
+ * dwc2_port_resume() - Exit controller from suspend mode for host.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return: non-zero if failed to exit suspend mode for host.
+ *
+ * This function is for exiting Host mode suspend.
+ * Must NOT be called with interrupt disabled or spinlock held.
+ */
+int dwc2_port_resume(struct dwc2_hsotg *hsotg)
{
unsigned long flags;
- u32 hprt0;
- u32 pcgctl;
+ int ret = 0;
spin_lock_irqsave(&hsotg->lock, flags);
- /*
- * If hibernation is supported, Phy clock is already resumed
- * after registers restore.
- */
- if (!hsotg->params.hibernation) {
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
- pcgctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ switch (hsotg->params.power_down) {
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ ret = dwc2_exit_partial_power_down(hsotg, 0, true);
+ if (ret)
+ dev_err(hsotg->dev,
+ "exit partial_power_down failed.\n");
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ /* Exit host hibernation. */
+ ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+ if (ret)
+ dev_err(hsotg->dev, "exit hibernation failed.\n");
+ break;
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ /*
+ * If not hibernation nor partial power down are supported,
+ * port resume is done using the clock gating programming flow.
+ */
spin_unlock_irqrestore(&hsotg->lock, flags);
- msleep(20);
+ dwc2_host_exit_clock_gating(hsotg, 0);
spin_lock_irqsave(&hsotg->lock, flags);
+ break;
}
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_RES;
- hprt0 &= ~HPRT0_SUSP;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
spin_unlock_irqrestore(&hsotg->lock, flags);
- msleep(USB_RESUME_TIMEOUT);
-
- spin_lock_irqsave(&hsotg->lock, flags);
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 &= ~(HPRT0_RES | HPRT0_SUSP);
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
- hsotg->bus_suspended = false;
- spin_unlock_irqrestore(&hsotg->lock, flags);
+ return ret;
}
/* Handles hub class-specific requests */
@@ -3436,6 +3388,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 port_status;
u32 speed;
u32 pcgctl;
+ u32 pwr;
switch (typereq) {
case ClearHubFeature:
@@ -3465,7 +3418,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"ClearPortFeature USB_PORT_FEAT_ENABLE\n");
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_ENA;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
break;
case USB_PORT_FEAT_SUSPEND:
@@ -3473,15 +3426,18 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
if (hsotg->bus_suspended)
- dwc2_port_resume(hsotg);
+ retval = dwc2_port_resume(hsotg);
break;
case USB_PORT_FEAT_POWER:
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 &= ~HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (pwr)
+ dwc2_vbus_supply_exit(hsotg);
break;
case USB_PORT_FEAT_INDICATOR:
@@ -3590,11 +3546,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
port_status |= USB_PORT_STAT_C_OVERCURRENT << 16;
}
- if (!hsotg->flags.b.port_connect_status) {
+ if (dwc2_is_device_mode(hsotg)) {
/*
- * The port is disconnected, which means the core is
- * either in device mode or it soon will be. Just
- * return 0's for the remainder of the port status
+ * Just return 0's for the remainder of the port status
* since the port register can't be read if the core
* is in device mode.
*/
@@ -3602,7 +3556,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
break;
}
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0);
if (hprt0 & HPRT0_CONNSTS)
@@ -3643,9 +3597,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
hsotg->params.dma_desc_enable = true;
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_DESCDMA;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
hsotg->new_connection = false;
}
}
@@ -3664,13 +3618,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1))
goto error;
- if (!hsotg->flags.b.port_connect_status) {
+ if (dwc2_is_device_mode(hsotg)) {
/*
- * The port is disconnected, which means the core is
- * either in device mode or it soon will be. Just
- * return without doing anything since the port
- * register can't be written if the core is in device
- * mode.
+ * Just return 0's for the remainder of the port status
+ * since the port register can't be read if the core
+ * is in device mode.
*/
break;
}
@@ -3681,28 +3633,54 @@ 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->bus_suspended)
+ retval = dwc2_port_suspend(hsotg, windex);
break;
case USB_PORT_FEAT_POWER:
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 |= HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
break;
case USB_PORT_FEAT_RESET:
- hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_RESET\n");
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+
+ if (hsotg->hibernated) {
+ retval = dwc2_exit_hibernation(hsotg, 0, 1, 1);
+ if (retval)
+ dev_err(hsotg->dev,
+ "exit hibernation failed\n");
+ }
+
+ if (hsotg->in_ppd) {
+ retval = dwc2_exit_partial_power_down(hsotg, 1,
+ true);
+ if (retval)
+ dev_err(hsotg->dev,
+ "exit partial_power_down failed\n");
+ }
+
+ if (hsotg->params.power_down ==
+ DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)
+ dwc2_host_exit_clock_gating(hsotg, 0);
+
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK);
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
/* ??? Original driver does this */
- dwc2_writel(0, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, 0, PCGCTL);
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
/* Clear suspend bit if resetting from suspend state */
hprt0 &= ~HPRT0_SUSP;
@@ -3715,13 +3693,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hprt0 |= HPRT0_PWR | HPRT0_RST;
dev_dbg(hsotg->dev,
"In host mode, hprt0=%08x\n", hprt0);
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
}
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
msleep(50);
hprt0 &= ~HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->lx_state = DWC2_L0; /* Now back to On state */
break;
@@ -3737,7 +3717,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"SetPortFeature - USB_PORT_FEAT_TEST\n");
hprt0 &= ~HPRT0_TSTCTL_MASK;
hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
break;
default:
@@ -3794,7 +3774,7 @@ static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port)
int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{
- u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+ u32 hfnum = dwc2_readl(hsotg, HFNUM);
#ifdef DWC2_DEBUG_SOF
dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n",
@@ -3805,9 +3785,9 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
{
- u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
- u32 hfir = dwc2_readl(hsotg->regs + HFIR);
- u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+ u32 hprt = dwc2_readl(hsotg, HPRT0);
+ u32 hfir = dwc2_readl(hsotg, HFIR);
+ u32 hfnum = dwc2_readl(hsotg, HFNUM);
unsigned int us_per_frame;
unsigned int frame_number;
unsigned int remaining;
@@ -3842,10 +3822,8 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg,
gfp_t mem_flags)
{
struct dwc2_hcd_urb *urb;
- u32 size = sizeof(*urb) + iso_desc_count *
- sizeof(struct dwc2_hcd_iso_packet_desc);
- urb = kzalloc(size, mem_flags);
+ urb = kzalloc(struct_size(urb, iso_descs, iso_desc_count), mem_flags);
if (urb)
urb->packet_count = iso_desc_count;
return urb;
@@ -3853,19 +3831,21 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg,
static void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, u8 dev_addr,
- u8 ep_num, u8 ep_type, u8 ep_dir, u16 mps)
+ u8 ep_num, u8 ep_type, u8 ep_dir,
+ u16 maxp, u16 maxp_mult)
{
if (dbg_perio() ||
ep_type == USB_ENDPOINT_XFER_BULK ||
ep_type == USB_ENDPOINT_XFER_CONTROL)
dev_vdbg(hsotg->dev,
- "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, mps=%d\n",
- dev_addr, ep_num, ep_dir, ep_type, mps);
+ "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, maxp=%d (%d mult)\n",
+ dev_addr, ep_num, ep_dir, ep_type, maxp, maxp_mult);
urb->pipe_info.dev_addr = dev_addr;
urb->pipe_info.ep_num = ep_num;
urb->pipe_info.pipe_type = ep_type;
urb->pipe_info.pipe_dir = ep_dir;
- urb->pipe_info.mps = mps;
+ urb->pipe_info.maxp = maxp;
+ urb->pipe_info.maxp_mult = maxp_mult;
}
/*
@@ -3926,11 +3906,11 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
if (chan->xfer_started) {
u32 hfnum, hcchar, hctsiz, hcint, hcintmsk;
- hfnum = dwc2_readl(hsotg->regs + HFNUM);
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(i));
- hcint = dwc2_readl(hsotg->regs + HCINT(i));
- hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(i));
+ hfnum = dwc2_readl(hsotg, HFNUM);
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(i));
+ hcint = dwc2_readl(hsotg, HCINT(i));
+ hcintmsk = dwc2_readl(hsotg, HCINTMSK(i));
dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum);
dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar);
dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz);
@@ -3956,8 +3936,9 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
dwc2_hcd_is_pipe_in(&urb->pipe_info) ?
"IN" : "OUT");
dev_dbg(hsotg->dev,
- " Max packet size: %d\n",
- dwc2_hcd_get_mps(&urb->pipe_info));
+ " Max packet size: %d (%d mult)\n",
+ dwc2_hcd_get_maxp(&urb->pipe_info),
+ dwc2_hcd_get_maxp_mult(&urb->pipe_info));
dev_dbg(hsotg->dev,
" transfer_buffer: %p\n",
urb->buf);
@@ -3978,17 +3959,16 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, " periodic_channels: %d\n",
hsotg->periodic_channels);
dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs);
- np_tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ np_tx_status = dwc2_readl(hsotg, GNPTXSTS);
dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n",
(np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n",
(np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
- p_tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ p_tx_status = dwc2_readl(hsotg, HPTXSTS);
dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n",
(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,
@@ -3997,75 +3977,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;
};
@@ -4090,7 +4001,6 @@ static struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *hcd)
* then the refcount for the structure will go to 0 and we'll free it.
*
* @hsotg: The HCD state structure for the DWC OTG controller.
- * @qh: The QH structure.
* @context: The priv pointer from a struct dwc2_hcd_urb.
* @mem_flags: Flags for allocating memory.
* @ttport: We'll return this device's port number here. That's used to
@@ -4235,6 +4145,8 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
urb->actual_length);
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ if (!hsotg->params.dma_desc_enable)
+ urb->start_frame = qtd->qh->start_active_frame;
urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb);
for (i = 0; i < urb->number_of_packets; ++i) {
urb->iso_frame_desc[i].actual_length =
@@ -4304,12 +4216,23 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->flags.b.port_reset_change = 1;
spin_unlock_irqrestore(&hsotg->lock, flags);
}
+static void dwc2_hcd_phy_reset_func(struct work_struct *work)
+{
+ struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
+ phy_reset_work);
+ int ret;
+
+ ret = phy_reset(hsotg->phy);
+ if (ret)
+ dev_warn(hsotg->dev, "PHY reset failed\n");
+}
+
/*
* =========================================================================
* Linux HC Driver Functions
@@ -4326,6 +4249,8 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
struct usb_bus *bus = hcd_to_bus(hcd);
unsigned long flags;
+ u32 hprt0;
+ int ret;
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
@@ -4341,6 +4266,17 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
dwc2_hcd_reinit(hsotg);
+ hprt0 = dwc2_read_hprt0(hsotg);
+ /* Has vbus power been turned on in dwc2_core_host_init ? */
+ if (hprt0 & HPRT0_PWR) {
+ /* Enable external vbus supply before resuming root hub */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ ret = dwc2_vbus_supply_init(hsotg);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&hsotg->lock, flags);
+ }
+
/* Initialize and connect root hub if one is not already attached */
if (bus->root_hub) {
dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n");
@@ -4349,6 +4285,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
}
spin_unlock_irqrestore(&hsotg->lock, flags);
+
return 0;
}
@@ -4360,6 +4297,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
+ u32 hprt0;
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
@@ -4368,6 +4306,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
synchronize_irq(hcd->irq);
spin_lock_irqsave(&hsotg->lock, flags);
+ hprt0 = dwc2_read_hprt0(hsotg);
/* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
@@ -4376,6 +4315,10 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
+ /* keep balanced supply init/exit by checking HPRT0_PWR */
+ if (hprt0 & HPRT0_PWR)
+ dwc2_vbus_supply_exit(hsotg);
+
usleep_range(1000, 3000);
}
@@ -4384,10 +4327,12 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
int ret = 0;
- u32 hprt0;
spin_lock_irqsave(&hsotg->lock, flags);
+ if (dwc2_is_device_mode(hsotg))
+ goto unlock;
+
if (hsotg->lx_state != DWC2_L0)
goto unlock;
@@ -4397,29 +4342,53 @@ 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->bus_suspended)
goto skip_power_saving;
- /*
- * Drive USB suspend and disable port Power
- * if usb bus is not suspended.
- */
- if (!hsotg->bus_suspended) {
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_SUSP;
- hprt0 &= ~HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
- }
+ if (!(dwc2_read_hprt0(hsotg) & HPRT0_CONNSTS))
+ goto skip_power_saving;
- /* Enter hibernation */
- ret = dwc2_enter_hibernation(hsotg);
- if (ret) {
- if (ret != -ENOTSUPP)
+ switch (hsotg->params.power_down) {
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ /* Enter partial_power_down */
+ ret = dwc2_enter_partial_power_down(hsotg);
+ if (ret)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
+ "enter partial_power_down failed\n");
+ /* After entering suspend, hardware is not accessible */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ /* Enter hibernation */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ ret = dwc2_enter_hibernation(hsotg, 1);
+ if (ret)
+ dev_err(hsotg->dev, "enter hibernation failed\n");
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ /* After entering suspend, hardware is not accessible */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ break;
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ /*
+ * If not hibernation nor partial power down are supported,
+ * clock gating is used to save power.
+ */
+ if (!hsotg->params.no_clock_gating) {
+ dwc2_host_enter_clock_gating(hsotg);
+
+ /* After entering suspend, hardware is not accessible */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ }
+ break;
+ default:
goto skip_power_saving;
}
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_vbus_supply_exit(hsotg);
+ spin_lock_irqsave(&hsotg->lock, flags);
+
/* Ask phy to be suspended */
if (!IS_ERR_OR_NULL(hsotg->uphy)) {
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -4427,9 +4396,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* After entering hibernation, hardware is no more accessible */
- clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
skip_power_saving:
hsotg->lx_state = DWC2_L2;
unlock:
@@ -4442,23 +4408,83 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
+ u32 hprt0;
int ret = 0;
spin_lock_irqsave(&hsotg->lock, flags);
+ if (dwc2_is_device_mode(hsotg))
+ goto unlock;
+
if (hsotg->lx_state != DWC2_L2)
goto unlock;
- if (!hsotg->params.hibernation) {
+ hprt0 = dwc2_read_hprt0(hsotg);
+
+ /*
+ * Added port connection status checking which prevents exiting from
+ * Partial Power Down mode from _dwc2_hcd_resume() if not in Partial
+ * Power Down mode.
+ */
+ if (hprt0 & HPRT0_CONNSTS) {
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
hsotg->lx_state = DWC2_L0;
goto unlock;
}
- /*
- * Set HW accessible bit before powering on the controller
- * since an interrupt may rise.
- */
- set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ switch (hsotg->params.power_down) {
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ ret = dwc2_exit_partial_power_down(hsotg, 0, true);
+ if (ret)
+ dev_err(hsotg->dev,
+ "exit partial_power_down failed\n");
+ /*
+ * Set HW accessible bit before powering on the controller
+ * since an interrupt may rise.
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+ if (ret)
+ dev_err(hsotg->dev, "exit hibernation failed.\n");
+
+ /*
+ * Set HW accessible bit before powering on the controller
+ * since an interrupt may rise.
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ break;
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ /*
+ * If not hibernation nor partial power down are supported,
+ * port resume is done using the clock gating programming flow.
+ */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_host_exit_clock_gating(hsotg, 0);
+
+ /*
+ * Initialize the Core for Host mode, as after system resume
+ * the global interrupts are disabled.
+ */
+ dwc2_core_init(hsotg, false);
+ dwc2_enable_global_interrupts(hsotg);
+ dwc2_hcd_reinit(hsotg);
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ /*
+ * Set HW accessible bit before powering on the controller
+ * since an interrupt may rise.
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ break;
+ default:
+ hsotg->lx_state = DWC2_L0;
+ goto unlock;
+ }
+
+ /* Change Root port status, as port status change occurred after resume.*/
+ hsotg->flags.b.port_suspend_change = 1;
/*
* Enable power if not already done.
@@ -4471,35 +4497,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* Exit hibernation */
- ret = dwc2_exit_hibernation(hsotg, true);
- if (ret && (ret != -ENOTSUPP))
- dev_err(hsotg->dev, "exit hibernation failed\n");
-
- hsotg->lx_state = DWC2_L0;
-
+ /* Enable external vbus supply after resuming the port. */
spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_vbus_supply_init(hsotg);
- if (hsotg->bus_suspended) {
- spin_lock_irqsave(&hsotg->lock, flags);
- hsotg->flags.b.port_suspend_change = 1;
- spin_unlock_irqrestore(&hsotg->lock, flags);
- dwc2_port_resume(hsotg);
- } else {
- /* Wait for controller to correctly update D+/D- level */
- usleep_range(3000, 5000);
+ /* Wait for controller to correctly update D+/D- level */
+ usleep_range(3000, 5000);
+ spin_lock_irqsave(&hsotg->lock, flags);
- /*
- * Clear Port Enable and Port Status changes.
- * Enable Port Power.
- */
- dwc2_writel(HPRT0_PWR | HPRT0_CONNDET |
- HPRT0_ENACHG, hsotg->regs + HPRT0);
- /* Wait for controller to detect Port Connect */
- usleep_range(5000, 7000);
- }
+ /*
+ * Clear Port Enable and Port Status changes.
+ * Enable Port Power.
+ */
+ dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
+ HPRT0_ENACHG, HPRT0);
- return ret;
+ /* Wait for controller to detect Port Connect */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ usleep_range(5000, 7000);
+ spin_lock_irqsave(&hsotg->lock, flags);
unlock:
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -4564,8 +4580,10 @@ static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb,
}
dev_vdbg(hsotg->dev, " Speed: %s\n", speed);
- dev_vdbg(hsotg->dev, " Max packet size: %d\n",
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+ dev_vdbg(hsotg->dev, " Max packet size: %d (%d mult)\n",
+ usb_endpoint_maxp(&urb->ep->desc),
+ usb_endpoint_maxp_mult(&urb->ep->desc));
+
dev_vdbg(hsotg->dev, " Data buffer length: %d\n",
urb->transfer_buffer_length);
dev_vdbg(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n",
@@ -4608,12 +4626,41 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
struct dwc2_qh *qh;
bool qh_allocated = false;
struct dwc2_qtd *qtd;
+ struct dwc2_gregs_backup *gr;
+
+ gr = &hsotg->gr_backup;
if (dbg_urb(urb)) {
dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n");
dwc2_dump_urb_info(hcd, urb, "urb_enqueue");
}
+ if (hsotg->hibernated) {
+ if (gr->gotgctl & GOTGCTL_CURMODE_HOST)
+ retval = dwc2_exit_hibernation(hsotg, 0, 0, 1);
+ else
+ retval = dwc2_exit_hibernation(hsotg, 0, 0, 0);
+
+ if (retval)
+ dev_err(hsotg->dev,
+ "exit hibernation failed.\n");
+ }
+
+ if (hsotg->in_ppd) {
+ retval = dwc2_exit_partial_power_down(hsotg, 0, true);
+ if (retval)
+ dev_err(hsotg->dev,
+ "exit partial_power_down failed\n");
+ }
+
+ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
+ hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
+ if (dwc2_is_device_mode(hsotg))
+ dwc2_gadget_exit_clock_gating(hsotg, 0);
+ else
+ dwc2_host_exit_clock_gating(hsotg, 0);
+ }
+
if (!ep)
return -EINVAL;
@@ -4648,12 +4695,12 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, usb_pipedevice(urb->pipe),
usb_pipeendpoint(urb->pipe), ep_type,
usb_pipein(urb->pipe),
- usb_maxpacket(urb->dev, urb->pipe,
- !(usb_pipein(urb->pipe))));
+ usb_endpoint_maxp(&ep->desc),
+ usb_endpoint_maxp_mult(&ep->desc));
buf = urb->transfer_buffer;
- if (hcd->self.uses_dma) {
+ if (hcd_uses_dma(hcd)) {
if (!buf && (urb->transfer_dma & 3)) {
dev_err(hsotg->dev,
"%s: unaligned transfer with no transfer_buffer",
@@ -4727,10 +4774,9 @@ fail3:
if (qh_allocated && qh->channel && qh->channel->qh == qh)
qh->channel->qh = NULL;
fail2:
- spin_unlock_irqrestore(&hsotg->lock, flags);
urb->hcpriv = NULL;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
kfree(qtd);
- qtd = NULL;
fail1:
if (qh_allocated) {
struct dwc2_qtd *qtd2, *qtd2_tmp;
@@ -4983,6 +5029,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
/* Free memory for QH/QTD lists */
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive);
+ dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting);
dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active);
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive);
dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready);
@@ -5013,17 +5060,17 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
hsotg->status_buf = NULL;
}
- ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ ahbcfg = dwc2_readl(hsotg, GAHBCFG);
/* Disable all interrupts */
ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
- dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
- dwc2_writel(0, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
+ dwc2_writel(hsotg, 0, GINTMSK);
if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) {
- dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_SFTDISCON;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
if (hsotg->wq_otg) {
@@ -5032,7 +5079,9 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
destroy_workqueue(hsotg->wq_otg);
}
- del_timer(&hsotg->wkp_timer);
+ cancel_work_sync(&hsotg->phy_reset_work);
+
+ timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
@@ -5066,17 +5115,18 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
retval = -ENOMEM;
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
- hsotg->frame_num_array = kzalloc(sizeof(*hsotg->frame_num_array) *
- FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
+ hsotg->frame_num_array = kcalloc(FRAME_NUM_ARRAY_SIZE,
+ sizeof(*hsotg->frame_num_array),
+ GFP_KERNEL);
if (!hsotg->frame_num_array)
goto error1;
- hsotg->last_frame_num_array = kzalloc(
- sizeof(*hsotg->last_frame_num_array) *
- FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
+ hsotg->last_frame_num_array =
+ kcalloc(FRAME_NUM_ARRAY_SIZE,
+ sizeof(*hsotg->last_frame_num_array), GFP_KERNEL);
if (!hsotg->last_frame_num_array)
goto error1;
#endif
@@ -5104,16 +5154,20 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
dwc2_hc_driver.reset_device = dwc2_reset_device;
}
+ if (hsotg->params.host_dma)
+ dwc2_hc_driver.flags |= HCD_DMA;
+
hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev));
if (!hcd)
goto error1;
- if (!hsotg->params.host_dma)
- hcd->self.uses_dma = 0;
-
hcd->has_tt = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ retval = -EINVAL;
+ goto error2;
+ }
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
@@ -5140,11 +5194,11 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
}
INIT_WORK(&hsotg->wf_otg, dwc2_conn_id_status_change);
- setup_timer(&hsotg->wkp_timer, dwc2_wakeup_detected,
- (unsigned long)hsotg);
+ timer_setup(&hsotg->wkp_timer, dwc2_wakeup_detected, 0);
/* Initialize the non-periodic schedule */
INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive);
+ INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting);
INIT_LIST_HEAD(&hsotg->non_periodic_sched_active);
/* Initialize the periodic schedule */
@@ -5172,11 +5226,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
hsotg->hc_ptr_array[i] = channel;
}
- /* Initialize hsotg start work */
+ /* Initialize work */
INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
-
- /* Initialize port reset work */
INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func);
+ INIT_WORK(&hsotg->phy_reset_work, dwc2_hcd_phy_reset_func);
/*
* Allocate space for storing data on status transactions. Normally no
@@ -5236,6 +5289,19 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
}
}
+ if (hsotg->params.host_dma) {
+ /*
+ * Create kmem caches to handle non-aligned buffer
+ * in Buffer DMA mode.
+ */
+ hsotg->unaligned_cache = kmem_cache_create("dwc2-unaligned-dma",
+ DWC2_KMEM_UNALIGNED_BUF_SIZE, 4,
+ SLAB_CACHE_DMA, NULL);
+ if (!hsotg->unaligned_cache)
+ dev_err(hsotg->dev,
+ "unable to create dwc2 unaligned cache\n");
+ }
+
hsotg->otg_port = 1;
hsotg->frame_list = NULL;
hsotg->frame_list_dma = 0;
@@ -5249,6 +5315,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
/* Don't support SG list at this point */
hcd->self.sg_tablesize = 0;
+ hcd->tpl_support = of_usb_host_tpl_support(hsotg->dev->of_node);
+
if (!IS_ERR_OR_NULL(hsotg->uphy))
otg_set_host(hsotg->uphy->otg, &hcd->self);
@@ -5270,8 +5338,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
return 0;
error4:
- kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->unaligned_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
+ kmem_cache_destroy(hsotg->desc_gen_cache);
error3:
dwc2_hcd_release(hsotg);
error2:
@@ -5312,8 +5381,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
usb_remove_hcd(hcd);
hsotg->priv = NULL;
- kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->unaligned_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
+ kmem_cache_destroy(hsotg->desc_gen_cache);
dwc2_hcd_release(hsotg);
usb_put_hcd(hcd);
@@ -5340,13 +5410,21 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
/* Backup Host regs */
hr = &hsotg->hr_backup;
- hr->hcfg = dwc2_readl(hsotg->regs + HCFG);
- hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK);
- for (i = 0; i < hsotg->params.host_channels; ++i)
- hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i));
+ hr->hcfg = dwc2_readl(hsotg, HCFG);
+ hr->hflbaddr = dwc2_readl(hsotg, HFLBADDR);
+ hr->haintmsk = dwc2_readl(hsotg, HAINTMSK);
+ for (i = 0; i < hsotg->params.host_channels; ++i) {
+ hr->hcchar[i] = dwc2_readl(hsotg, HCCHAR(i));
+ hr->hcsplt[i] = dwc2_readl(hsotg, HCSPLT(i));
+ hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i));
+ hr->hctsiz[i] = dwc2_readl(hsotg, HCTSIZ(i));
+ hr->hcidma[i] = dwc2_readl(hsotg, HCDMA(i));
+ hr->hcidmab[i] = dwc2_readl(hsotg, HCDMAB(i));
+ }
hr->hprt0 = dwc2_read_hprt0(hsotg);
- hr->hfir = dwc2_readl(hsotg->regs + HFIR);
+ hr->hfir = dwc2_readl(hsotg, HFIR);
+ hr->hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ);
hr->valid = true;
return 0;
@@ -5375,15 +5453,543 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
}
hr->valid = false;
- dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
- dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK);
-
- for (i = 0; i < hsotg->params.host_channels; ++i)
- dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
+ dwc2_writel(hsotg, hr->hcfg, HCFG);
+ dwc2_writel(hsotg, hr->hflbaddr, HFLBADDR);
+ dwc2_writel(hsotg, hr->haintmsk, HAINTMSK);
+
+ for (i = 0; i < hsotg->params.host_channels; ++i) {
+ dwc2_writel(hsotg, hr->hcchar[i], HCCHAR(i));
+ dwc2_writel(hsotg, hr->hcsplt[i], HCSPLT(i));
+ dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i));
+ dwc2_writel(hsotg, hr->hctsiz[i], HCTSIZ(i));
+ dwc2_writel(hsotg, hr->hcidma[i], HCDMA(i));
+ dwc2_writel(hsotg, hr->hcidmab[i], HCDMAB(i));
+ }
- dwc2_writel(hr->hprt0, hsotg->regs + HPRT0);
- dwc2_writel(hr->hfir, hsotg->regs + HFIR);
+ dwc2_writel(hsotg, hr->hprt0, HPRT0);
+ dwc2_writel(hsotg, hr->hfir, HFIR);
+ dwc2_writel(hsotg, hr->hptxfsiz, HPTXFSIZ);
hsotg->frame_number = 0;
return 0;
}
+
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* Backup all registers */
+ 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;
+ }
+
+ return 0;
+}
+
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ 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_host_backup_critical_registers(hsotg);
+ if (ret)
+ return ret;
+
+ /* Enter USB Suspend Mode */
+ hprt0 = dwc2_readl(hsotg, HPRT0);
+ hprt0 |= HPRT0_SUSP;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ /* Wait for the HPRT0.PrtSusp register field to be set */
+ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 5000))
+ 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, GUSBCFG);
+ if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
+ /* ULPI interface */
+ udelay(10);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+ /* Suspend the Phy Clock */
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(10);
+
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+ } else {
+ /* UTMI+ Interface */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(10);
+ }
+
+ /* Enable interrupts from wake up logic */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PMUINTSEL;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ /* Unmask host mode interrupts in GPWRDN */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_DISCONN_DET_MSK;
+ gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+ gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNCLMP;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNSWTCH;
+ dwc2_writel(hsotg, gpwrdn, 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(hsotg, 0xffffffff, GINTSTS);
+
+ /* De-assert Restore */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_RESTORE;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ /* Restore GUSBCFG, HCFG */
+ dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+ dwc2_writel(hsotg, hr->hcfg, HCFG);
+
+ /* Reset ULPI latch */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
+ /* De-assert Wakeup Logic */
+ if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+ }
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+
+ if (reset) {
+ hprt0 |= HPRT0_RST;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(60);
+ hprt0 &= ~HPRT0_RST;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ } else {
+ hprt0 |= HPRT0_RES;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ /* De-assert Wakeup Logic */
+ if ((rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+ }
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(100);
+ hprt0 &= ~HPRT0_RES;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ }
+ /* Clear all interrupt status */
+ hprt0 = dwc2_readl(hsotg, HPRT0);
+ hprt0 |= HPRT0_CONNDET;
+ hprt0 |= HPRT0_ENACHG;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ hprt0 = dwc2_readl(hsotg, HPRT0);
+
+ /* Clear all pending interupts */
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+ /* Restore global registers */
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
+ return ret;
+
+ if (rem_wakeup) {
+ dwc2_hcd_rem_wakeup(hsotg);
+ /*
+ * Change "port_connect_status_change" flag to re-enumerate,
+ * because after exit from hibernation port connection status
+ * is not detected.
+ */
+ hsotg->flags.b.port_connect_status_change = 1;
+ }
+
+ hsotg->hibernated = 0;
+ hsotg->bus_suspended = 0;
+ hsotg->lx_state = DWC2_L0;
+ dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
+ return ret;
+}
+
+bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
+{
+ struct usb_device *root_hub = dwc2_hsotg_to_hcd(dwc2)->self.root_hub;
+
+ /* If the controller isn't allowed to wakeup then we can power off. */
+ if (!device_may_wakeup(dwc2->dev))
+ return true;
+
+ /*
+ * We don't want to power off the PHY if something under the
+ * root hub has wakeup enabled.
+ */
+ if (usb_wakeup_enabled_descendants(root_hub))
+ return false;
+
+ /* No reason to keep the PHY powered, so allow poweroff */
+ return true;
+}
+
+/**
+ * dwc2_host_enter_partial_power_down() - Put controller in partial
+ * power down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return: non-zero if failed to enter host partial power down.
+ *
+ * This function is for entering Host mode partial power down.
+ */
+int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
+{
+ u32 pcgcctl;
+ u32 hprt0;
+ int ret = 0;
+
+ dev_dbg(hsotg->dev, "Entering host partial power down started.\n");
+
+ /* Put this port in suspend mode. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_SUSP;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ udelay(5);
+
+ /* Wait for the HPRT0.PrtSusp register field to be set */
+ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000))
+ dev_warn(hsotg->dev, "Suspend wasn't generated\n");
+
+ /* Backup all registers */
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
+ return ret;
+
+ /*
+ * Clear any pending interrupts since dwc2 will not be able to
+ * clear them after entering partial_power_down.
+ */
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+
+ /* Put the controller in low power state */
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+
+ pcgcctl |= PCGCTL_PWRCLMP;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(5);
+
+ pcgcctl |= PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(5);
+
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+
+ /* Set in_ppd flag to 1 as here core enters suspend. */
+ hsotg->in_ppd = 1;
+ hsotg->lx_state = DWC2_L2;
+ hsotg->bus_suspended = true;
+
+ dev_dbg(hsotg->dev, "Entering host partial power down completed.\n");
+
+ return ret;
+}
+
+/*
+ * dwc2_host_exit_partial_power_down() - Exit controller from host partial
+ * power down.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Reset.
+ * @restore: indicates whether need to restore the registers or not.
+ *
+ * Return: non-zero if failed to exit host partial power down.
+ *
+ * This function is for exiting from Host mode partial power down.
+ */
+int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, bool restore)
+{
+ u32 pcgcctl;
+ int ret = 0;
+ u32 hprt0;
+
+ dev_dbg(hsotg->dev, "Exiting host partial power down started.\n");
+
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl &= ~PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(5);
+
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl &= ~PCGCTL_PWRCLMP;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(5);
+
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+
+ udelay(100);
+ if (restore) {
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
+ return ret;
+ }
+
+ /* Drive resume signaling and exit suspend mode on the port. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_RES;
+ hprt0 &= ~HPRT0_SUSP;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ udelay(5);
+
+ if (!rem_wakeup) {
+ /* Stop driveing resume signaling on the port. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 &= ~HPRT0_RES;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ hsotg->bus_suspended = false;
+ } else {
+ /* Turn on the port power bit. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_PWR;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ /* Connect hcd. */
+ dwc2_hcd_connect(hsotg);
+
+ mod_timer(&hsotg->wkp_timer,
+ jiffies + msecs_to_jiffies(71));
+ }
+
+ /* Set lx_state to and in_ppd to 0 as here core exits from suspend. */
+ hsotg->in_ppd = 0;
+ hsotg->lx_state = DWC2_L0;
+
+ dev_dbg(hsotg->dev, "Exiting host partial power down completed.\n");
+ return ret;
+}
+
+/**
+ * dwc2_host_enter_clock_gating() - Put controller in clock gating.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * This function is for entering Host mode clock gating.
+ */
+void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg)
+{
+ u32 hprt0;
+ u32 pcgctl;
+
+ dev_dbg(hsotg->dev, "Entering host clock gating.\n");
+
+ /* Put this port in suspend mode. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_SUSP;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ /* Set the Phy Clock bit as suspend is received. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+
+ /* Set the Gate hclk as suspend is received. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl |= PCGCTL_GATEHCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+
+ hsotg->bus_suspended = true;
+ hsotg->lx_state = DWC2_L2;
+}
+
+/**
+ * dwc2_host_exit_clock_gating() - Exit controller from clock gating.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by remote wakeup
+ *
+ * This function is for exiting Host mode clock gating.
+ */
+void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup)
+{
+ u32 hprt0;
+ u32 pcgctl;
+
+ dev_dbg(hsotg->dev, "Exiting host clock gating.\n");
+
+ /* Clear the Gate hclk. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl &= ~PCGCTL_GATEHCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+
+ /* Phy Clock bit. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl &= ~PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+
+ /* Drive resume signaling and exit suspend mode on the port. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_RES;
+ hprt0 &= ~HPRT0_SUSP;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ udelay(5);
+
+ if (!rem_wakeup) {
+ /* In case of port resume need to wait for 40 ms */
+ msleep(USB_RESUME_TIMEOUT);
+
+ /* Stop driveing resume signaling on the port. */
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 &= ~HPRT0_RES;
+ dwc2_writel(hsotg, hprt0, HPRT0);
+
+ hsotg->bus_suspended = false;
+ hsotg->lx_state = DWC2_L0;
+ } else {
+ mod_timer(&hsotg->wkp_timer,
+ jiffies + msecs_to_jiffies(71));
+ }
+}